[英]QObject: Cannot create children for a parent that is in a different thread. PyQt5
[英]PyQt5 QObject: Cannot create children for a parent that is in a different thread
我正在使用PyQt5在菜單系統托盤中工作。 我對PyQt5非常陌生,我想做的是在不阻止菜單(多線程)的情況下觸發操作。 在許多地方閱讀完之后,我得出的結論是使用Qthread
應該是Qthread
的方法(但是,如果我能理解該類的工作原理……)。 但是,考慮到我的應用程序非常簡單,使用threading
也不會很糟糕。 因此,我嘗試使用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
方法,並且我的菜單中會填充找到的列表。 我還可以看到我的應用程序在進行搜索時不會被阻塞,但是完成時會出現以下錯誤:
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.
此后,菜單仍然具有“響應性”,但不能觸發更多操作,因此它仍然是“功能性”的。 此外,似乎沒有更多的線程被創建。 如果有人可以解釋我為什么會發生這種情況,我將感到非常高興。 我看不到光...
更新 :
我現在創建了一個worker.py
,其中包含:
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
添加了以下內容:
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
我將繼續回答自己。 受https://stackoverflow.com/a/33453124/1995261的啟發,我通過實現以下方法解決了這一問題:
1)我創建了一個worker.py
,它執行阻止菜單的_search_cast_
方法。 當此方法完成搜索時,它將發出兩個信號:a)一個通知他已恢復list
,以及b)該方法已完成。
#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
我轉儲了以下內容,並嘗試在代碼內用注釋進行解釋:
#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
,菜單不會被阻止,屏幕上不會顯示任何錯誤,並且在activity monitor
threads
時, threads
數保持正確。
希望對大家有幫助。 有關更精確的信息(我仍在學習PyQt,並且措辭可能不太好),建議您檢查上面發布的鏈接。
由於這是該錯誤的Google最佳答案,並且花費了我比預期的時間才能正確解決此問題,因此,我將分享我針對Python 3和PyQt 5的非常簡單的解決方案(如果您更改某些導入,它也應該在PyQt4中工作) )。
我遇到的情況是帶有右鍵單擊菜單的系統托盤圖標,當其他線程請求它時應重新構建該圖標。 您當然可以將其應用於要通過線程限制進行通信的其他問題。
import time
import sys
import threading
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from PyQt5 import QtCore
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
def __init__(self, icon=None, parent=None):
icon = QtGui.QIcon(QtWidgets.QApplication.style().standardPixmap(QtWidgets.QStyle.SP_MediaPlay))
QtWidgets.QSystemTrayIcon.__init__(self, icon, parent)
self.menu = QtWidgets.QMenu(parent)
self.setContextMenu(self.menu)
self.build_menu()
self.show()
# see http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html for more information
self.signal = MySignal()
self.signal.sig_no_args.connect(self.build_menu)
self.signal.sig_with_str.connect(self.print_string)
def build_menu(self):
''' This function should be called in order to rebuild
the right-click menu for the systray icon'''
global list_dict_streams
self.menu.clear()
exitAction = self.menu.addAction("Exit")
exitAction.triggered.connect(self._exit)
for x in list_dict_streams :
self.menu.addAction(x)
def print_string(self, str):
print(str)
def _exit(self):
QtCore.QCoreApplication.exit()
class MySignal(QtCore.QObject):
''' Why a whole new class? See here:
https://stackoverflow.com/a/25930966/2441026 '''
sig_no_args = QtCore.pyqtSignal()
sig_with_str = QtCore.pyqtSignal(str)
list_dict_streams = ["1"]
def work_thread(trayIcon):
''' Will add one menu item to the systray menu every 5 seconds
and will send a signal with a string '''
global list_dict_streams
while True:
trayIcon.signal.sig_no_args.emit()
trayIcon.signal.sig_with_str.emit("String emitted")
list_dict_streams.append(str(len(list_dict_streams)+1))
time.sleep(5)
def main():
app = QtWidgets.QApplication(sys.argv)
trayIcon = SystemTrayIcon()
t = threading.Thread(target=work_thread, args=(trayIcon,))
t.daemon = True # otherwise the 'Exit' from the systray menu will not work
t.start()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
基本上,您必須創建一個新class MySignal(QtCore.QObject)
why 。 我創建了一個包含兩個示例的類-一個示例不發送任何參數,而另一個示例則可以傳遞字符串。 您當然可以定義其他參數 。 然后,在目標線程中,創建該類的新實例,並將該類中的函數連接到目標內部的函數(本例中為systray圖標)。 在那之后,您現在可以像在while循環中一樣調用emit(...)
函數。
現在,Qt很高興,因為與直接從其他線程調用trayIcon.build_menu()
相比,您只發出一個信號即可。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.