【问题标题】:PyQt5 run same thread dynamicallyPyQt5 动态运行同一个线程
【发布时间】:2021-01-27 18:30:04
【问题描述】:

我正在使用 python 3.7 和 pyqt5

我想要做的是多次使用不同的线程运行相同的 QObject。

这是我的主要内容

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import threading
import sys
from Worker_Starting import Worker_Starting


class Main(QMainWindow):
    def __init__(self) :
        super().__init__()
        self.setupUI()
        self.initSignal()

    def setupUI(self):
        self.resize(400, 400)
        self.pushButton = QPushButton("Start", self)
        self.pushButton_2 = QPushButton("Stop", self)
        self.pushButton.move(0,0)
        self.pushButton_2.move(120,0)


    def initSignal(self) :
        self.pushButton.clicked.connect(self.Start)
        self.pushButton_2.clicked.connect(self.Stop)

    @pyqtSlot()
    def Start(self) :
        for i in range(3) :  
            print('main', threading.get_ident())
            input = ["userID", "userNAME"]

            self.my_thread = QThread()
            self.my_worker = Worker_Starting(passing_list=input)
            self.my_worker.moveToThread(self.my_thread)
            self.my_thread.started.connect(self.my_worker.my_fn)
            self.my_thread.start()
            time.sleep(3)


    @pyqtSlot()
    def Stop(self) :
        pass

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Main()
    window.show()
    app.exec_()

还有我的 Qobject

from PyQt5 import QtCore
from PyQt5.QtCore import *
import time
import threading

class Worker_Starting(QObject):
    def __init__(self, passing_list, parent=None):
        QtCore.QThread.__init__(self, parent=parent)
        self.userid = passing_list[0]
        self.username = passing_list[1]

    def my_fn(self):
        print(self.username, threading.get_ident())
        for i in range(10) :
            Now = time.strftime("%Y/%m/%d %H:%M:%S")
            print(str(i) + " : " + self.username + "  running" + " : " + Now)
            time.sleep(0.5)

我把for i in range(3):放在def start(self):下面

因为在我的实际主脚本中,其他线程多次向def start(self): 发出输入。

如果我只发出一次,def start(self): 可以正常工作。 但是当我发射几次时,它就会崩溃。 因为我必须多次发射,我需要my_thread 才能同时连续工作

有没有办法将相同的 Qobject 设置到不同的线程?

以及如何知道或设置线程ID。

以及如何通过线程 id 停止线程。

*我已经尝试过 QRunnable,我现在可以使用多线程了。但我必须不断地向我的主发出信号。据我所知 QRunnable 不适合自定义信号。

【问题讨论】:

  • 请注意,Worker_Starting 的声明继承自 QObject,但您改为调用 QtCore.QThread.__init__。此外,没有必要创建一个新线程并将 QObject 移动到它,因为您通常可以只将 QThread 子类化。此外,你不应该在主线程中使用像 time.sleep 这样的阻塞函数(就像你在 Start() 中所做的那样)。
  • @musicamante 所以,如果我使用 QThread 而不是 QObject,那会解决我的问题吗? time.sleep 和 for i in range(3) 只是注意到我有几个 eimt 信号持续地启动()。
  • 1.不,因为(正如我的回答中所解释的)问题是您不能在另一个线程上移动 QObject 而它仍在另一个线程上处理;我的反对意见是您(通常)不需要创建 QObject 子类并使用moveToThread,因为从 QThread 子类化通常就足够了(只要按顺序调用每个“工作”函数,也就是没有并发来电)。 2. 没关系:no 阻塞函数应该永远发生在主 GUI 线程中,因为它们应该 总是 尽快返回;使用time.sleep不是。

标签: python multithreading pyqt qthread


【解决方案1】:

问题是您不断地通过覆盖现有线程来创建新线程,这会导致以前的线程在完成之前被垃圾收集,从而导致崩溃。

如果你想并发执行同一个“worker”函数,你不能为多个线程使用一个对象,因为你应该每次都使用moveToThread,这是不可能的。

解决方案是改用QRunnable,在其run()方法中实现函数,并调用QThreadPool来运行它。

class Worker_Starting(QRunnable):
    def __init__(self, passing_list):
        QRunnable.__init__(self)
        self.setAutoDelete(False)
        self.userid = passing_list[0]
        self.username = passing_list[1]

    def run(self):
        print(self.username, threading.get_ident())
        for i in range(10) :
            Now = time.strftime("%Y/%m/%d %H:%M:%S")
            print(str(i) + " : " + self.username + "  running" + " : " + Now)
            time.sleep(0.5)


class Main(QMainWindow):
    def __init__(self) :
        super().__init__()
        self.setupUI()
        self.initSignal()
        self.worker = None

    # ...

    def Start(self) :
        if not self.worker:
            input = ["userID", "userNAME"]
            self.worker = Worker_Starting(passing_list=input)
        for i in range(3):
            print('main', threading.get_ident())
            QThreadPool.globalInstance().start(self.worker)

请记住,您应该不在主 Qt 线程中使用阻塞函数(这就是我从 Start() 中删除 time.sleep(3) 的原因)。如果您想定期多次启动该功能,请使用 QTimer:

        interval = 3000 # interval is in milliseconds
        for i in range(3):
            print('main', threading.get_ident())
            QTimer.singleShot(interval * i, lambda:
                QThreadPool.globalInstance().start(self.worker))

请注意,pyqtSlot() 装饰器通常仅用于特殊情况和高级实现,因此您应该删除它。另请注意,只有类和常量的名称应大写,而函数、变量和属性应小写(首选start 和stop,而不是Start 和Stop)。

【讨论】:

  • 我知道 Qrunnable 做并发线程。但是可以从 QRrunnable 制作自定义信号吗?以及如何从 QRunnable 退出特定线程?
  • QRunnable 不从 QObject 继承:不能创建信号(显然也不能发出信号)。您可以做的是使用自定义信号创建 QObject 子类,为 QRunnable 子类创建该信号的实例并在需要时发出它。但是如果你想退出一个特定的QRunnable.run 调用,那就是另一回事了,它可能甚至过于复杂:一个更简单的选择是创建唯一的 QThread 工作线程,跟踪它们并在需要时停止它们。显然,为了实现这一点,您需要一些方法来不断检查线程是否应该停止(通常使用 Queue 对象)。
猜你喜欢
  • 2018-07-20
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 2013-08-13
  • 1970-01-01
  • 1970-01-01
  • 2010-10-20
相关资源
最近更新 更多