Avoid : Cannot create graphics instruction outside the main Kivy thread - python

I'm trying to make a chat application based on client-server. When the client receive a message I want to display it on my kivy screen. Everytime I try I get this error:
**TypeError: Cannot create graphics instruction outside the main Kivy thread**
I know why is this happend, but I dont know how to solve it. Please help me with it!!
Client Class:
class Client(object):
def __init__(self) -> None:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.connect((socket.gethostname(), 7777))
self.message, self.username = None, None
def recv(self) -> None:
data = self.server.recv(1024)
return self.parse(pickle.loads(data))
def parse(self, data: dict) -> None:
if (action := data.get("action")):
print(data)
if action == "update":
clients = pickle.loads(data.get("active-users"))
#update la lista cu useri conectati
elif action == "message":
self.message, self.username = data.get("data"), data.get("username")
return "message_done"
MainScreen Class:
class MainScreen(Screen):
container = ObjectProperty(None)
messages = ObjectProperty(None)
text = ObjectProperty(None)
def __init__(self, **kw):
super(MainScreen,self).__init__(**kw)
def add_message(self, username: str, message: str):
self.messages.add_widget(CustomLabel(text="[size=17][i][b]" + username + ":[/b][/i][/size] " + message))
def on_pre_enter(self, *args):
threading.Thread(target = self.receive_messages).start()
def receive_messages(self):
while True:
if client.recv() == "message_done":
self.add_message(client.username,client.message)
def remove_specific_widget(self, username: str):
for user in self.container.children:
if user.text == username:
return self.container.remove_widget(user)
def send(self, text: str) -> None:
client.send(self.text.text)

You can't modify the GUI outside of the main thread. You are trying to do that in your add_message() method, since it is called by code running in your thread that is started in the on_pre_enter() method. An easy fix is to just add the mainthread decorator to the add_message() method:
#mainthread
def add_message(self, username: str, message: str):
self.messages.add_widget(CustomLabel(text="[size=17][i][b]" + username + ":[/b][/i][/size] " + message))
This forces the add_message() to be run on the main thread, similar to what Clock.schedule_once() would do.

Related

Can't Pass Textual Inputs to Wesocket Client and store response

I am trying to send and receive web socket data using my Client.py file. I am also trying to load the prompts ("Username?") into the Feeler console and have input from InConsole stored in the username variable in the Client.py file. So far I have been unable to make this setup work.
What is the most efficient way for me to accomplish this task?
I have the following code in Feeler.py
...
def Message(output):
message = Client.main(output)
return message
class OutConsole(ScrollView):
prev = Text("")
async def eval(self, text_input):
pre_y = self.y
with console.capture() as capture:
try:
console.print(f"{text_input}")
except Exception:
console.print_exception(show_locals=True)
self.prev.append(Text.from_ansi(capture.get() + "\n"))
await self.update(self.prev)
self.y = pre_y
self.animate("y", self.window.virtual_size.height, duration=1, easing="linear")
class InConsole(TextInput):
def __init__(self, out):
super(InConsole, self).__init__()
self.out = out
async def on_key(self, event: events.Key) -> None:
if event.key == "enter":
await self.out.eval(self.value)
self.value = ""
class GridTest(App):
async def on_mount(self) -> None:
"""Make a simple grid arrangement."""
# Git code
output = OutConsole()
in_put = InConsole(out=output)
message = Message(output)
await output.eval(message)
...
I also Have this in Client.py
def main():
# Define the host IP and port for the server
HOST = socket.gethostname()
PORT = 5050
# Ask For initial username and password
return ("Username?")
username = input(">")
return ("Password?")
password = getpass.getpass(f"{username}> ")
The rest of my code can be found at https://github.com/DanielATucker/Brain/tree/Client for reference.
Thanks!

Tornado client with stdin

I am trying to build a multiplayer game that uses Python. I am using Tornado to build the client and server. Ideally, what I would like to happen are as follows:
(a) For the client to wait for user input from the command line
(b) When the client gets user input, to send the user input to the server
(c) for the server to simulate some processing(which will be the game engine) on it and send the response back to a client.
The server
"""
Server module for game server
"""
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.websocket
import uuid
import json
class Controller(object):
def __init__(self):
self.players = ()
self.connections = ()
def check(self):
return "Hi"
def run_somthing(self, text):
new_text = "Server: " + text
return new_text
class InitMessageHandler(tornado.web.RequestHandler):
def get(self):
user_data = {}
user_data['user_id'] = str(uuid.uuid4())
self.write(json.dumps(user_data))
class GameHandler(tornado.websocket.WebSocketHandler):
def open(self):
# called anytime a new connection with this server is opened
print("Client connected")
print("Client sent: ", self)
if seif not in self.application.controller.connections:
self.application.controller.connections.add(self)
def on_message(self):
# called anytime a new message is received
pass
def check_origin(self, origin):
return True
def on_close(self):
# called a websocket connection is closed
if self in self.application.controller.connections:
self.application.controller.connections.remove(self)
class Server(tornado.web.Application):
def __init__(self):
self.controller = Controller()
handlers = [
(r"/join", InitMessageHandler),
(r"/game", GameHandler)
]
tornado.web.Application.__init__(self, handlers)
if __name__ == "__main__":
tornado.options.parse_command_line()
try:
application = Server()
server = tornado.httpserver.HTTPServer(application)
server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
except KeyboardInterrupt:
tornado.ioloop.IOLoop.instance().stop()
print("closed")
The client
"""
Client module for the game clients(Players)
"""
import tornado.ioloop
import tornado.websocket
import requests
import json
import sys
import tornado.gen
class Client(object):
def __init__(self, join_url, play_url):
self.wsconn = None
self.join_url = join_url
self.play_url = play_url
#self.io_loop = tornado.ioloop.IOLoop.instance()
#self.io_loop.add_handler(sys.stdin, self.handle_user_input, tornado.ioloop.IOLoop.READ)
self.user_details = {}
self.has_initialised = False
#self.io_loop.start()
self.main()
def get_user_input(self, question=None):
str_choice = input(question)
while any((str_choice is None, not str_choice.strip())):
print("You entered an empty answer")
str_choice = input(question)
return str_choice
def _merge_dicts(*dict_args):
"""
Given any number of dicts, shallow copy and merge into a new dict,
precedence goes to key value pairs in latter dicts.
"""
result = {}
for dictionary in dict_args:
result.update(dictionary)
return result
def generate_wsmessage(self):
msg_line = input("Enter message to send to server")
while any((msg_line is None, not msg_line.strip())):
print("You entered an empty answer")
msg_line = input("Enter message to send to server")
msg = {}
msg['message'] = msg_line
msg_to_send = self._merge_dicts(self.user_details, msg)
return json.dumps(msg_to-send)
def init(self):
print("Heh")
username = self.get_user_input("What is your username? ")
print("Getting initial user details")
req = requests.get(self.join_url)
response = json.loads(req.text)
print(response)
self.user_details['name'] = username
self.user_details['user_id'] = response['user_id']
self.has_initialised = True
def server_recv(self, msg):
print("Server has connected on websocket socket with msg=", msg)
#tornado.gen.coroutine
def connect_on_websocket(self):
try:
self.wsconn = yield tornado.websocket.websocket_connect(self.play_url, on_message_callback=self.server_recv)
except Exception as e:
print("Connection error: {}".format(e))
else:
print("Connected")
#tornado.gen.coroutine
def send_wsmessage(self):
msg = self.generate_wsmessage()
yield self.wsconn.write_message(msg)
#tornado.gen.coroutine
def communicate_with_websocket(self):
self.send_wsmessage()
while True:
recv_msg = yield self.wsconn.read_message()
if recv_msg is None:
self.wsconn.close()
break
yield tornado.gen.sleep(0.1)
self.send_wsmessage()
print("IoLoop terminate")
def main(self):
choice = input("Do you want to continue(y/n)? ")
if choice == "y" and self.has_initialised == False:
print("Yup")
self.init()
if self.has_initialised == True:
self.connect_on_websocket()
self.communicate_with_websocket()
if __name__ == "__main__":
try:
client = Client("http://localhost:8888/join", "ws://localhost:8888/game")
tornado.ioloop.IOLoop.instance().start()
except (SystemExit, KeyboardInterrupt):
print("Client closed")
From reading the some examples online, I came up the code above, but it is not working. So my main question is
how to make Tornado coroutines work with stdin(command line input)
My other questions are:
(a) Is the code I have written the right way to work with Tornado coroutines or not?
(b) If it is not, could you ELI5? Also I would appreciate code examples that really use Tornado in interesting ways(at any intermediate level) so that I can learn from them.
(c) Is there a more intuitive way to do what I want to do,in Python? Like a Flask+Gevents or Twisted version or just pure sockets version that might be easier to work with?
Thanks for your help.
EDIT: Flan pointed out some errors for me and I fixed it and it works now.
As I can see at the moment, problem is not in stdin interaction, but your wrong way of using coroutines. Your connect_on_websocket and communicate_with_websocket functions are coroutines but you are using them as a plain functions and it won't work. I propose these changes.
Make main() coroutine (add decorator), don't call it, remove from the Client.__init__().
In name=main block schedule client.main() invocation with tornado.ioloop.IOLoop.instance().add_callback(client.main).
In main and all your code change coroutine-functions' (with #tornado.gen.coroutine) calls to yield, for example yield self.connect_on_websocket() instead of just self.connect_on_websocket()
That should be sufficient so you can proceed your development further.
The edited code is
The server:
"""
Server module for game server
"""
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.websocket
import uuid
import json
class Controller(object):
def __init__(self):
self.players = set()
self.connections = set()
def check(self):
return "Hi"
def run_somthing(self, text):
new_text = "Server: " + text
return new_text
class InitMessageHandler(tornado.web.RequestHandler):
def get(self):
print("Client has asked for initial details")
user_data = {}
user_data['user_id'] = str(uuid.uuid4())
self.write(json.dumps(user_data))
class GameHandler(tornado.websocket.WebSocketHandler):
def open(self):
# called anytime a new connection with this server is opened
print("Client connected")
print("Client sent: ", self)
if self not in self.application.controller.connections:
self.application.controller.connections.add(self)
def on_message(self, message):
# called anytime a new message is received
print("Received from client ,msg=", message)
msg = "Server: " + message
self.write_message(json.dumps(msg))
def check_origin(self, origin):
return True
def on_close(self):
# called a websocket connection is closed
if self in self.application.controller.connections:
self.application.controller.connections.remove(self)
class Server(tornado.web.Application):
def __init__(self):
self.controller = Controller()
handlers = [
(r"/join", InitMessageHandler),
(r"/game", GameHandler)
]
tornado.web.Application.__init__(self, handlers)
if __name__ == "__main__":
tornado.options.parse_command_line()
try:
application = Server()
server = tornado.httpserver.HTTPServer(application)
server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
except KeyboardInterrupt:
tornado.ioloop.IOLoop.instance().stop()
print("Server closed")
The client
import tornado.ioloop
import tornado.websocket
import requests
import json
import sys
import tornado.gen
class Client(object):
def __init__(self, join_url, play_url):
self.wsconn = None
self.join_url = join_url
self.play_url = play_url
self.user_details = {}
def get_user_input(self, question=None):
str_choice = input(question)
while any((str_choice is None, not str_choice.strip())):
print("You entered an empty answer")
str_choice = input(question)
return str_choice
def _merge_dicts(*dict_args):
"""
Given any number of dicts, shallow copy and merge into a new dict,
precedence goes to key value pairs in latter dicts.
"""
result = {}
for dictionary in dict_args:
result.update(dictionary)
return result
def generate_wsmessage(self):
msg_line = self.get_user_input("Enter message to send to server: ")
msg = {}
msg['message'] = msg_line
msg['user_id'] = self.user_details['user_id']
msg['user_name'] = self.user_details['user_name']
print("Message to send: ", msg)
return json.dumps(msg)
def init(self):
print("Heh")
username = self.get_user_input("What is your username? ")
print("Getting initial user details")
req = requests.get(self.join_url)
response = json.loads(req.text)
print(response)
self.user_details['user_name'] = username
self.user_details['user_id'] = response['user_id']
#tornado.gen.coroutine
def connect_on_websocket(self):
try:
self.wsconn = yield tornado.websocket.websocket_connect(self.play_url)
except Exception as e:
print("Connection error: {}".format(e))
else:
print("Server has connected to ")
yield self.send_wsmessage()
#tornado.gen.coroutine
def send_wsmessage(self):
msg = self.generate_wsmessage()
if not self.wsconn:
raise RuntimeError('Web socket connection is closed.')
yield self.wsconn.write_message(json.dumps(msg))
yield self.communicate_with_websocket()
#tornado.gen.coroutine
def communicate_with_websocket(self):
recv_msg = None
while True:
recv_msg = yield self.wsconn.read_message()
if recv_msg is None:
self.wsconn.close()
break
print("Server has replied with message=", recv_msg)
yield self.send_wsmessage()
print("IoLoop terminate")
#tornado.gen.coroutine
def main(self):
choice = input("Do you want to continue(y/n)? ")
if choice == "y":
print("Yup")
self.init()
yield self.connect_on_websocket()
if choice == "n":
sys.exit()
if __name__ == "__main__":
try:
client = Client("http://localhost:8888/join", "ws://localhost:8888/game")
tornado.ioloop.IOLoop.instance().add_callback(client.main)
tornado.ioloop.IOLoop.instance().start()
except (SystemExit, KeyboardInterrupt):
print("Client closed")
For (a), (b) check out here. For (c), another time.

Why is this zmq code not working?

This test doesn't work.
class PrintHandler(MessageHandler):
def handle_message(self, message):
print(message)
class FileHandler(MessageHandler):
def handle_message(self, message):
with open('nana', 'w') as f:
f.write(message)
class SubscribeProcess(Process):
def __init__(self, handler):
super(SubscribeProcess, self).__init__(group=None, target=None, name=None, args=(), kwargs={})
self.handler = handler
def run(self):
self.address = TcpAddress(host='127.0.0.1', port=5555)
subscriber = ZmqSubscriber(ZmqBlockingConnection(address=self.address, bind=False))
subscriber.set_message_handler(self.handler)
print('............')
class TestZmqSubscriber(TestCase):
def test_set_message_handler(self):
address = TcpAddress(host='127.0.0.1', port=5555)
pub_connection = ZmqBlockingConnection(address, bind=True)
publisher = ZmqPublisher(pub_connection)
p = SubscribeProcess(handler=PrintHandler())
p.start()
while True:
publisher.publish('Message number {}'.format(2))
I now that's this is not the unit test actually. But I want to see the received messages in console first. Then I will write proper test.
While this two scripts work perfectly.
connection = ZmqBlockingConnection(TcpAddress(host='127.0.0.1', port=5555), bind=False)
sub = ZmqSubscriber(connection)
sub.set_message_handler(PrintHandler())
address = TcpAddress(host='127.0.0.1', port=5555)
pub_connection = ZmqBlockingConnection(address, bind=True)
publisher = ZmqPublisher(pub_connection)
while True:
publisher.publish('Message number {}'.format(2))
Inside of subscriber.set_message_handler(handler) is actually this
def start_receiving_messages(self, message_handler):
while True:
message_handler.handle_message(self.socket.recv())
And in debugger I see that the code hangs infinitely in socket.recv()
Maybe I'm using multiprocessing wrong?
EDIT1
class ZmqBlockingConnection(Connection):
def start_receiving_messages(self, message_handler):
while True:
message_handler.handle_message(self.socket.recv())
def send_message(self, message):
self.socket.send(message)
def __init__(self, address, bind, hwm=1000):
self.hwm = hwm
self.bind = bind
self.address = address
self.socket = None
def set_hwm(self, hwm):
self.socket.set_hwm(hwm)
def configure(self, socket_type):
self.socket = zmq.Context().socket(socket_type)
if self.bind:
self.socket.bind(str(self.address))
else:
self.socket.connect(str(self.address))
self.set_hwm(self.hwm)
OK, the problem was in
def configure(self, socket_type):
self.socket = zmq.Context().instance().socket(socket_type)
if self.bind:
self.socket.bind(str(self.address))
else:
self.socket.connect(str(self.address))
self.set_hwm(self.hwm)
so instead of using singleton I started to create context instances and now it's working.

Accessing a non-global variable in Python

I'm trying to change the state of a Gtk status icon from a thread as specified in MailThread.run() below, but I don't know how to reach the status icon object from the method in order to change set_visible to either True or False.
Basically I would like to know what to write in place of "# set status icon visible off/on".
#!/usr/bin/env python
import gtk, sys, pynotify, imaplib, time, threading
from email import parser
class Mail:
def check_mail(self):
obj = imaplib.IMAP4_SSL('imap.gmail.com','993')
acc = 'email'
pwrd = 'pass'
obj.login(acc, pwrd)
obj.select()
num = str(len(obj.search(None,'UnSeen')[1][0].split()))
return acc, num
class MailThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
gtk.gdk.threads_init()
def run(self):
while True:
print "hello"
mail = Mail()
num = mail.check_mail()[1]
if num < 1:
# set status icon visible off
else:
# set status icon visible on
time.sleep(60)
class StatusIcon:
# activate callback
def activate( self, widget, data=None):
mail = Mail()
acc, num = mail.check_mail()
pynotify.init("myapp")
n = pynotify.Notification(acc, "You have " + num + " unread e-mails.", "emblem-mail")
n.show()
# Show_Hide callback
def show_hide(self, widget,response_id, data= None):
if response_id == gtk.RESPONSE_YES:
widget.hide()
else:
widget.hide()
# destroyer callback
def destroyer(self, widget,response_id, data= None):
if response_id == gtk.RESPONSE_OK:
gtk.main_quit()
else:
widget.hide()
# popup callback
def popup(self, button, widget, data=None):
dialog = gtk.MessageDialog(
parent = None,
flags = gtk.DIALOG_DESTROY_WITH_PARENT,
type = gtk.MESSAGE_INFO,
buttons = gtk.BUTTONS_OK_CANCEL,
message_format = "Do you want to close e-mail notifications?")
dialog.set_title('Exit')
dialog.connect('response', self.destroyer)
dialog.show()
def __init__(self):
# create a new Status Icon
self.staticon = gtk.StatusIcon()
self.staticon.set_from_icon_name("emblem-mail")
self.staticon.connect("activate", self.activate)
self.staticon.connect("popup_menu", self.popup)
self.staticon.set_visible(True)
# starting thread
thread = MailThread()
thread.setDaemon(True)
thread.start()
# invoking the main()
gtk.main()
if __name__ == "__main__":
# status icon
statusicon = StatusIcon()
You can accept the status icon in the thread's __init__():
class MailThread(threading.Thread):
def __init__(self, status_icon = None):
threading.Thread.__init__(self)
gtk.gdk.threads_init()
self.status_icon = status_icon
And then you can use it in run().
Additionally, you need to do all the GUI work from the main thread. The main thread has a queue maintained by GTK you can use to tell it to go do some GUI work. This is how it works:
def run(self):
# <...>
if num < 1:
gobject.idle_add(self.set_status_icon, False)
else:
gobject.idle_add(self.set_status_icon, True)
# <...>
def set_status_icon(self, state = False):
# code that changes icon state goes here
pass
idle_add basically means "add that to the queue and do it when you have some free time".

Twisted task.loop and pb auth

Learn Twisted. I decided to write a server and client that once a second to share data.
Wrote one implementation, but it seems to me that it is not correct.
# -*- coding: utf-8 -*-
from twisted.spread import pb
from twisted.internet import reactor, task
from twisted.cred import credentials
from win32com.server import factory
class login_send:
def __init__(self):
self.count=0
self.timeout = 1.0
self.factory = pb.PBClientFactory()
reactor.connectTCP("localhost", 8800, self.factory)
def testTimeout(self):
self.count+=1
print self.count
def1 = self.factory.login(credentials.UsernamePassword("test1","bb1b"))
def1.addCallbacks(self.good_connected, self.bad_connected)
def1.addCallback(self.send_data)
def1.addErrback(self.disconnect)
if self.count>10:def1.addBoth(self.disconnect)
def start(self):
l = task.LoopingCall(self.testTimeout)
l.start(self.timeout)
reactor.run()
def good_connected(self, perspective):
print 'good login and password', perspective
return perspective
def bad_connected(self, perspective):
print 'bad login or password', perspective
return perspective
def send_data(self, perspective):
print 'send'
return perspective.callRemote("foo", self.count)
def disconnect(self, perspective):
print 'disconnect'
reactor.stop()
if __name__ == "__main__":
st=login_send()
st.start()
Code: if login and password True -> send self.count, if login or password False -> disconnect, if self.count>10 -> disconnect
The first mistake, in my opinion is that I have to login every time.
def1 = self.factory.login(credentials.UsernamePassword("test1", "bb1b"))
How to make one authorization, and continue to send data every second?
simple test server code:
from zope.interface import implements
from twisted.spread import pb
from twisted.cred import checkers, portal
from twisted.internet import reactor
class MyPerspective(pb.Avatar):
def __init__(self, name):
self.name = name
def perspective_foo(self, arg):
print "I am", self.name, "perspective_foo(",arg,") called on", self
return arg
class MyRealm:
implements(portal.IRealm)
def requestAvatar(self, avatarId, mind, *interfaces):
if pb.IPerspective not in interfaces:
print 'qqqq'
raise NotImplementedError
return pb.IPerspective, MyPerspective(avatarId), lambda:None
p = portal.Portal(MyRealm())
c = checkers.InMemoryUsernamePasswordDatabaseDontUse(test1="bbb",
user2="pass2")
p.registerChecker(c)
reactor.listenTCP(8800, pb.PBServerFactory(p))
reactor.run()
I believe this should do the trick.
# Upper case first letter of class name is good policy.
class Login_send:
def __init__(self):
# initialize the state variable to False.
self.connection = False
self.count=0
self.timeout = 1.0
self.factory = pb.PBClientFactory()
reactor.connectTCP("localhost", 8800, self.factory)
def testTimeout(self):
self.count+=1
print self.count
# no connection -- create one.
if not self.connection:
self.assign_connection()
# cached connection exists, call send_data manually.
elif self.count > 10:
self.disconnect(self.connection)
else:
#you probably want to send data only if it it should be valid.
self.send_data(self.connection)
def assign_connection(self):
''' Creates and stores a Deffered which represents the connection to
the server. '''
# cache the connection.
self.connection = self.factory.login(
credentials.UsernamePassword("test1","bb1b"))
# add connection callbacks as normal.
self.connection.addCallbacks(
self.good_connected, self.bad_connected)
self.connection.addCallback(self.send_data)
self.connection.addErrback(self.disconnect)
def disconnect(self, perspective):
# be sure to cleanup after yourself!
self.connection = False
print 'disconnect'
reactor.stop()
# the rest of your class goes here.

Categories

Resources