如何从不同的线程访问 MainThread 元素? [复制]
Posted
技术标签:
【中文标题】如何从不同的线程访问 MainThread 元素? [复制]【英文标题】:How to access MainThread elements from different thread? [duplicate] 【发布时间】:2021-06-03 19:00:03 【问题描述】:我正在使用 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()
client_connect 函数 - 我为小部件创建了一个单独的文件,并将小部件插入到 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)
【问题讨论】:
【参考方案1】:正如您所怀疑的,您不应该直接从不同的线程修改 PyQt 对象。您可以阅读关于线程和对象的docs。 文档特别提到:
如果您在不存在于当前线程中的 QObject 子类上调用函数并且该对象可能会接收事件,则必须使用互斥锁保护对 QObject 子类内部数据的所有访问;否则,您可能会遇到崩溃或其他不良行为。 [.......] 另一方面,您可以安全地从 QThread::run() 实现中发出信号,因为信号发出是线程安全的。
PyQt 希望你这样做的方式是使用 Signals 和 Slots。下面是一个从 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 函数是一种更简单但可能会受到限制的使用方式。您可以考虑创建 QObjects 并使用 moveToThread 函数获得更多选项。
【讨论】:
以上是关于如何从不同的线程访问 MainThread 元素? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
如何从异步上下文切换 MainThread 的 CurrentCulture?