PyQt5 QObject:无法为不同线程中的父级创建子级
问题描述
我正在使用 PyQt5 在菜单系统托盘中工作.我对 PyQt5 很陌生,我想做的是在不阻塞菜单的情况下触发一个动作(多线程).在阅读了很多地方之后,我得出的结论是使用 Qthread
应该是要走的路(但如果我能理解那个类是如何工作的......).但是,考虑到我的应用程序非常简单,使用 threading
也不会那么糟糕.因此,我使用 import threading
尝试了以下代码:
I am working in a menu system tray with PyQt5. I am very new with PyQt5, and what I want to do is to trigger an action without the menu being blocked (multithreading). After having read in many places, I have come to the conclusion that using Qthread
should be the way to go (but if only I could understand how that class works...). However, the use of threading
wouldn't be that bad either given that my application is very simple. So, I have tried the following code using import threading
:
from PyQt5 import QtCore, QtGui, QtWidgets
import threading
class menubar(object):
def __init__(self):
signal.signal(signal.SIGINT, signal.SIG_DFL)
self.systray = True
self.stopped = False
def search_menu(self):
self.SearchAction = menu.addAction("Search")
self.SearchAction.triggered.connect(self.search_cast)
def _search_cast_(self):
args.select_cc = True
self.cc.initialize_cast()
self.cast_list()
def search_cast(self):
threading.Thread(target=self._search_cast_).start()
#some more methods here...
def main():
menubar()
app = QtWidgets.QApplication(sys.argv)
tray = QtWidgets.QSystemTrayIcon(icon)
menu = QtWidgets.QMenu()
start = menubar()
start.search_menu()
start.separator_menu()
start.populating_menu()
start.separator_menu()
start.stop_menu()
start.resetaudio_menu()
start.about_menu()
start.exit_menu()
tray.setContextMenu(menu)
tray.show()
app.exec_()
if __name__ == '__main__':
main()
当我开始菜单时,一切都如我所愿.然后,当我单击菜单 Search
时,该操作会触发 self.search_cast
方法,并且我的菜单会填充它找到的列表.我还可以看到我的应用程序在没有被阻止的情况下进行搜索,但是当它完成时出现以下错误:
When I start my menu, everything is in place as I expect it. Then, when I click on the menu Search
the action triggers the self.search_cast
method, and my menu gets populated with the list it finds. I can also see my application doing the searching without getting blocked but when it finishes I get the following errors:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QMenu(0x7fcef497c160), parent's thread is QThread(0x7fcef2603d10), current thread is QThread(0x7fcef4a89360)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QMenu(0x7fcef497c160), parent's thread is QThread(0x7fcef2603d10), current thread is QThread(0x7fcef4a89360)
QObject: Cannot create children for a parent that is in a different thread.
在此之后,菜单仍然是功能性的",因为它是响应式的,但无法触发更多操作.此外,似乎不再创建线程.如果有人能解释我为什么会这样,我会很高兴?我没有看到光...
After this, the menu is still "functional" in the sense that it is responsive but no more action can be triggered. Additionally, it seems that no more threads are created. I would be glad if somebody could explain me why is this happening?. I don't see the light...
更新:
我现在创建了一个 worker.py
,其中包含:
I have created now a worker.py
that contains:
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot
#some other imports
class Worker(QObject):
finished = pyqtSignal()
@pyqtSlot()
def _search_cast_(self):
self.cc = casting()
self.cc.initialize_cast()
self.finished.emit()
然后我在 class menubar
中添加了以下内容:
Then I have added in the class menubar
the following:
class menubar(object):
def __init__(self):
self.cc = casting()
signal.signal(signal.SIGINT, signal.SIG_DFL)
self.cc.cast = None
self.systray = True
self.stopped = False
self.obj = worker.Worker() # no parent!
self.thread = QThread() # no parent!
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj._search_cast_)
def search_menu(self):
self.SearchAction = menu.addAction("Search")
self.SearchAction.triggered.connect(self.search_cast)
def search_cast(self):
self.thread.start()
self.cast_list()
def cast_list(self):
if len(self.cc.availablecc) == 0:
# some actions here.
现在我收到以下错误:
AttributeError: 'casting' object has no attribute 'availablecc'
我确保 worker
实际上正在从我称为 cc
的外部类中恢复 availablecc
.但由于某种原因,menubar
类没有接收到.我正在根据这个 https://stackoverflow.com/a/33453124/1995261
I make sure that actually the worker
is recovering availablecc
from an external class that I called cc
. But for some reason is not being received by the menubar
class. I am working based on this https://stackoverflow.com/a/33453124/1995261
解决方案
我会继续回答自己.受 https://stackoverflow.com/a/33453124/1995261 的启发,我通过实施以下方法解决了这个问题:
I will proceed to answer myself. Inspired by https://stackoverflow.com/a/33453124/1995261, I solved this by implementing the following:
1) 我创建了一个 worker.py
来执行阻塞菜单的方法 _search_cast_
.当这个方法完成搜索时,它会发出两个信号:a)一个通知他恢复了 list
,b)这个方法已经完成.
1) I created a worker.py
that executes the method _search_cast_
that was blocking the menu. When this method finishes searching, it emits two signals: a) one informing that he recovered the list
, and b) that the method has finished.
#worker.py
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(list)
def __init__(self):
QObject.__init__(self)
@pyqtSlot()
def _search_cast_(self):
self.cc = casting()
self.cc.initialize_cast()
availablecc = self.cc.availablecc
self.intReady.emit(availablecc)
self.finished.emit()
2) 在 main.py
中,我转储了以下内容,并尝试在代码中用注释进行解释:
2) In the main.py
I dumped the following and I try to explain inside the code with comments:
#main.py
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot
import worker # This is to import worker.py
class menubar(object):
def __init__(self):
signal.signal(signal.SIGINT, signal.SIG_DFL)
self.cc.cast = None
self.systray = True
self.stopped = False
self.obj = worker.Worker() # The worker is started with no parent!
self.thread = QThread() # We initialise the Qthread class with no parent!
self.obj.intReady.connect(self.onIntReady) # We receive the signal that the list is ready
self.obj.moveToThread(self.thread) # Moving the object to the thread
self.obj.finished.connect(self.thread.quit) # When the method is finished we receive the signal that it is finished
self.thread.started.connect(self.obj._search_cast_) # We need to connect the above with the desired method inside the work.py
self.app = QtWidgets.QApplication(sys.argv)
def search_menu(self):
self.SearchAction = self.menu.addAction("Search")
self.SearchAction.triggered.connect(self.search_cast)
def onIntReady(self, availablecc): # This method receives the list from the worker
print ('availablecc', availablecc) # This is for debugging reasons to verify that I receive the list with the correct content
self.availablecc = availablecc
def search_cast(self): #This method starts the thread when self.SearchAction is triggered
args.select_cc = True
self.thread.start()
这样,当搜索list
时,菜单不会被阻塞,屏幕上不会显示错误,threads
在<代码>活动监视器代码>保持正确.
In this way, when searching for the list
the menu does not get blocked, no errors are shown on the screen and the number of threads
when monitoring them in activity monitor
stay correct.
我希望这对人们有所帮助.如需更准确的信息(我还在学习 PyQt,我的措辞可能不是很好),建议您查看我在上面发布的链接.
I hope this helps people. For more precise information (I am still learning PyQt and my wording may not be very good), I suggest you to check the link that I posted above.
相关文章