Avoid : Cannot create graphics instruction outside the main Kivy thread

Viewed 62

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)
1 Answers

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