python - PyQt5 QObject : Cannot create children for a parent that is in a different thread

标签 python multithreading pyqt5

我正在使用 PyQt5 在菜单系统托盘中工作。我对 PyQt5 很陌生,我想做的是在不阻止菜单的情况下触发一个 Action (多线程)。在阅读了很多地方之后,我得出的结论是使用 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(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 工作

最佳答案

因为这是这个错误的谷歌最佳答案,我花了比预期更长的时间来正确解决这个问题,我将分享我对 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 .我创建了一个包含两个示例的类 - 一个不发送参数,另一个可以传递字符串。你当然可以define other arguments .然后在目标线程中创建此类的新实例并将该类的函数连接到目标中的函数(在我的例子中是系统托盘图标)。之后,您现在可以像我在 while 循环中那样调用 emit(...) 函数。
现在 Qt 很高兴,因为与直接从不同线程调用 trayIcon.build_menu() 相比,您只需发出一个信号。

关于python - PyQt5 QObject : Cannot create children for a parent that is in a different thread,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36453462/

相关文章:

python - 为什么我会收到 "unwanted"日志条目?

python - 为什么一些 python 内置 "functions"实际上是类型?

Python Mechanize : Session has expired

Android NDK线程无效使用非静态成员函数

python - 在线程中的 Qdialog 上调用 exec() 效果不佳

python - PyQt5 中的 FocusReason?

Python:使用 'Null' 作为 mysql.connector 的端口参数

multithreading - 相同版本但具有不同配置选项的两个 perls 之间的兼容性?

启动新线程时.NET WPF MissingMethodException

python - 使用线程时 Pyqt5 GUI 仍然挂起