PyQt5 QObject:无法为不同线程中的父级创建子级

时间:2023-03-12
本文介绍了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.

这篇关于PyQt5 QObject:无法为不同线程中的父级创建子级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一篇:如何制作一个可以在 Qt 中将其行折叠成类别的表格? 下一篇:在 QWidget 上的 QPixmap 上绘制点(pyqt5)

相关文章