如何从不同的线程访问主线程元素?

2024-06-07 06:08:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在使用PyQt5用Python创建一个服务器客户端GUI应用程序。每当一个新的客户端连接到服务器时,我都需要在服务器端显示客户端的详细信息。我使用了一个单独的插座线程。每当我调用client_connect函数来添加服务器端的小部件时,我都会得到一个错误,原因是没有显示哪个小部件。我认为,由于GUI和套接字代码位于不同的线程中,因此我得到了错误

QObject::setParent:无法设置父级,新父级位于不同的线程中

QObject::installEventFilter():无法为不同线程中的对象筛选事件

QBasicTimer::start:QBasicTimer只能与使用QThread

QBasicTimer::start:QBasicTimer只能与使用QThread

QBasicTimer::start:QBasicTimer只能与使用QThread

QObject::startTimer:计时器只能用于以QThread

QBasicTimer::start:QBasicTimer只能与使用QThread

主要功能

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

客户端连接功能-我已经为小部件创建了一个单独的文件,我正在将小部件插入tableWidget中。如果我直接调用函数,它就可以工作,但是如果我从套接字代码调用它,它会给我错误

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])

套接字编程

 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)

        

Tags: selfclientui客户端部件connectconn线程
1条回答
网友
1楼 · 发布于 2024-06-07 06:08:37

正如您所怀疑的,您不应该直接修改来自不同线程的PyQt对象。您可以阅读关于线程和对象的docs。 文件特别提到:

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.

PyQt希望您这样做的方式是使用信号和插槽。下面是从worker的run函数发出信号的一个小示例

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)

主要概念如下:

  • 仅从主线程修改PyQt对象
  • 创建信号并将它们连接到将在主线程中为您更新UI的函数
  • 需要时从线程内发出信号

重写QThread的run函数是一种更简单的方法,但使用这种方法可能会受到限制。您可以考虑创建QObject并使用moveToThread函数获得更多选项

相关问题 更多 >