0

I am creating a Server-Client GUI application in Python using PyQt5. Whenever a new clients is connected to server I need to display the client details at server side. I am using a separate thread for sockets. Whenever I call the client_connect function to add the widget at server side I get error due to which widget is not displayed. I think since the GUI and socket codes are in different threads due to this I am getting the error.

QObject::setParent: Cannot set parent, new parent is in a different thread

QObject::installEventFilter(): Cannot filter events for objects in a different thread.

QBasicTimer::start: QBasicTimer can only be used with threads started with QThread

QBasicTimer::start: QBasicTimer can only be used with threads started with QThread

QBasicTimer::start: QBasicTimer can only be used with threads started with QThread

QObject::startTimer: Timers can only be used with threads started with QThread

QBasicTimer::start: QBasicTimer can only be used with threads started with QThread

Main Function

if __name__ == "__main__":
    thread1 = threading.Thread(target = ui.server_socket)
    thread1.start()

client_connect Function - I have created a separate file for the widget and I am inserting the widget in tableWidget. It works if i directly call the function but if i call it from the socket code it gives me error.

def client_connect(self,clientid):
            
            self.clientCount = self.clientCount + 1
            self.clientList.append(clientid)
            self.clientDict[clientid] = QtWidgets.QWidget()
            self.clientDict[clientid].ui = clients()
            self.clientDict[clientid].ui.setupUi(self.clientDict[clientid])
            self.clientDict[clientid].ui.label_clientid.setText(str(clientid))
            self.tableWidget_client.setRowCount(self.clientCount)
            self.tableWidget_client.setCellWidget(self.clientCount-1,0,self.clientDict[clientid])

Socket programming

 def start_socket(self):
        self.ssock.listen(20)
        while True:
            conn, c_addr = self.ssock.accept()
            print(conn,c_addr)

            thread2 = threading.Thread(target=self.handle_client, args=(conn,c_addr))
            thread2.start()

    def handle_client(self, conn, c_addr):
        try:
            self.c = conn.recv(1024).decode(self.FORMAT)

            thread3 = threading.Thread(target = self.client_connect, args = (self.c_id,))
            thread3.start()
           
        except socket.error as e:
            print(e)

        
Nivea
  • 11
  • 2

1 Answers1

0

As you suspected, you shouldn't directly modify PyQt objects from different threads. You may read the docs about threads and objects. Docs specifically mention that:

If you are calling a function on an QObject subclass that doesn't live in the current thread and the object might receive events, you must protect all access to your QObject subclass's internal data with a mutex; otherwise, you may experience crashes or other undesired behavior. [.......] On the other hand, you can safely emit signals from your QThread::run() implementation, because signal emission is thread-safe.

The way PyQt wants you to do it is to use Signals and Slots. Below is a small example of emitting a signal from worker's run function.

class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        # Some Ui stuff is happening here
        self.worker = Worker(self.startParm)  
        self.worker.client_connected.connect(self.display_client_info)     
        self.worker.start() # When you want to process a new client connection

    def display_client_info(self, client_id):
        # Code here should be similar to what you're trying to do in client_connect

class Worker(QThread):
    def __init__(self):
        super(Worker, self).__init__()
        self.client_connected = pyqtSignal(int)

    def run(self, clientid):
        # The code you want to be executed in a thread
        # For example whatever happens in handle_client, except in your design you're going
        # through a middle thread and this is a basic example.
        # Eventually emit client_connected signal with the client id:
        self.client_connected.emit(client_id)

The main concepts to take from this:

  • Only modify PyQt objects from the main thread
  • Create signals and connect them to functions that will update UI for you in the main thread
  • Emit signals from within threads when you need to

Overriding QThread's run function is a more simple but can be limited way of using this. You may look into creating QObjects and using moveToThread function for more options.