0

I'm creating a chat app with PyQt and socketio.
Whenever I want to add a new message widget to the scroll area this error appears "QObject::setParent: Cannot set parent, new parent is in a different thread"
This is my code:

import sys
from html import escape
from socketio import Client
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QWidget, QScrollArea, QVBoxLayout,
QLabel, QLineEdit, QPushButton)

class Message(QWidget):
    def __init__(self, sender, text):
        super().__init__()
        layout = QVBoxLayout()
        self.setLayout(layout)
        senderLbl = QLabel(sender)
        layout.addWidget(senderLbl)
        layout.addWidget(QLabel(text))

class ChatField(QScrollArea):
    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setWidgetResizable(True)

        widget = QWidget()
        self.layout = QVBoxLayout()
        widget.setLayout(self.layout)

        self.setWidget(widget)
        for x in range(50):
            self.layout.addWidget(Message('sender', 'text'))
    
    def addMessage(self, sender, text):
        self.layout.addWidget(Message(sender, text))

class App(QWidget):
    def __init__(self):
        super().__init__()
        self.connected = False
        layout = QVBoxLayout()
        self.setLayout(layout)

        self.output = ChatField(self)
        self.input = QLineEdit()
        self.input.returnPressed.connect(self.send_message)
        self.input.setEnabled(False)

        layout.addWidget(self.output)
        layout.addWidget(self.input)

        self.setWindowTitle('MyChat-Connecting...')
        self.show()
    
    def send_message(self):
        sio.emit('send_message', {'username':'anonymouse', 'message':self.input.text()})

app = QApplication([])
sio = Client()
window = App()

@sio.event
def connect():
    window.setWindowTitle('MyChat')
    window.input.setEnabled(True)

@sio.event
def get_message(data):
    window.output.addMessage(data['from'], data['message'])

sio.connect('http://localhost:8080')
sys.exit(app.exec_())

and this is my server code:

from socketio import AsyncServer
from aiohttp.web import Application, run_app

app = Application()
sio = AsyncServer(async_mode='aiohttp')
sio.attach(app)

@sio.event
async def connect(sid, environ, auth):
    sio.enter_room(sid, 'chat-room')
    print('user', sid, 'has joined the chat')

@sio.event
async def disconnect(sid):
    sio.leave_room(sid, 'chat-room')
    print('user', sid, 'has left the chat')

@sio.event
async def send_message(sid, data):
    await sio.emit('get_message', {'from':data['username'], 'message':data['message']}, room='chat-room')

run_app(app, port=8080)

I can't understand why does it happen and I'm not using any other thread.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • "I'm not using any other thread", well, according to the [AsyncServer](https://python-socketio.readthedocs.io/en/latest/api.html#asyncserver-class) documentation, you are: if the `async_handlers` is set to `True` (the default), "event handlers for a client are executed in separate threads". – musicamante Apr 24 '21 at 14:05
  • Are you saying I should turn my async server into a normal server? – Mohammad Armin Niknami Apr 24 '21 at 14:07
  • No, I'm just saying that you *are* using threads. Have you read that part of the documentation? Since it says that the default uses separate thread *for client event handlers*, can't you just try to set it to False? Otherwise, create a QThread for the client and use a Qt signal to communicate with a function in the main thread that will add the message to the scroll area. – musicamante Apr 24 '21 at 14:10
  • I can't understand what are you saying. Can you write an answer wand write the code in it? – Mohammad Armin Niknami Apr 24 '21 at 14:32
  • No, as I cannot test your code so I couldn't provide a reliable answer, and I've already clearly explained what you should try first. It doesn't matter if you *think* that you're not using threads, if you get that error it means that threads are being used, and accessing UI elements from external threads is *always* forbidden. If setting that argument for the server doesn't work, do some research on how to implement a QThread, as there are literally hundreds of posts about that topic. – musicamante Apr 24 '21 at 14:37
  • The Socket.IO handlers will always run a threads. To make this work you have to use a custom signal. So your Socket.IO handler will just emit this signal. Then in the main thread you connect this signal to a slot function that is going to run in the main thread. – Miguel Grinberg Apr 25 '21 at 15:48

0 Answers0