【问题标题】:Correctly display thread ID with QThread使用 QThread 正确显示线程 ID
【发布时间】:2019-10-03 08:21:33
【问题描述】:

我正在尝试使用 QThread 正确显示线程的 ID,但我得到了一些令人困惑的结果。

我已经读过这个:PyQt: Connecting a signal to a slot to start a background operation,但它涵盖了一个关于插槽和信号的特定问题。不是这种情况。我对插槽连接顺序不感兴趣,重点实际上是了解代码的哪一部分在特定线程中运行。

请考虑以下代码:

from PyQt5.QtCore import QObject, QThread, pyqtSignal


class Worker(QObject):
    started = pyqtSignal()
    finished = pyqtSignal()
    def __init__(self, message):
        super(Worker, self).__init__()
        self.message = message
    def process(self, message):
        i = 0
        cicle = 10000
        j = 0
        while j < 5:
            i = i + 1
            if i % cicle == 0:
                j = float(i/cicle)
                print(message, j)
        print("Worker", int(QThread.currentThread().currentThreadId()))
        self.finished.emit()

worker1 = Worker("Worker 1:")
thread1 = QThread()
thread1.started.connect(lambda: worker1.process(worker1.message))
worker1.finished.connect(thread1.quit)
worker1.finished.connect(worker1.deleteLater)
thread1.finished.connect(thread1.deleteLater)
worker1.moveToThread(thread1)
print("Main app:", int(QThread.currentThread().currentThreadId()))


thread1.start()

我得到以下信息:

Main App: 13420
Worker 1: 1.0
Worker 1: 2.0
Worker 1: 3.0
Worker 1: 4.0
Worker 1: 5.0
Worker 13420

我想知道为什么第一行和最后一行显示相同的整数 id:为什么如果我将 worker1 移动到另一个线程中会得到相同的结果?

编辑: 我再次编辑此问题以(再次)说明问题不在连接顺序中。我试过这段代码:

class Worker(QObject):
    started = pyqtSignal()
    finished = pyqtSignal()
    def __init__(self, message):
        super(Worker, self).__init__()
        self.message = message
    def process(self, message):
        i = 0
        cicle = 10000
        j = 0
        while j < 5:
            i = i + 1
            if i % cicle == 0:
                j = float(i/cicle)
                print(message, j)
        # print(int(QThread.currentThread().currentThreadId()))
        print("Worker", int(QThread.currentThread().currentThreadId()))
        self.finished.emit()

worker1 = Worker("Worker 1:")
thread1 = QThread()
worker1.moveToThread(thread1)
print("Main app:", int(QThread.currentThread().currentThreadId()))
thread1.started.connect(lambda: worker1.process(worker1.message))
worker1.finished.connect(thread1.quit)
worker1.finished.connect(worker1.deleteLater)
thread1.finished.connect(thread1.deleteLater)

thread1.start()

结果相同。

如果对网站不够好,请随时编辑问题,感谢您的任何建议。

【问题讨论】:

  • 问题是您在将工作线程移动到线程之前 连接了信号。请参阅上面给出的副本的已接受答案,以解释为什么有时会导致问题。要解决此问题,请在 移动 worker 之后连接信号,或确保任何纯 Python 插槽(如 process)都用 pyqtSlot 装饰。
  • 你能删除重复的标志吗?我已经多次编辑我的问题,以解释为什么它是一个不同且更具体的问题。我认为现在区别很明显了。
  • 我看到你已经编辑了你的问题,但你错过了最重要的一点,那就是如果你在移动工人之前连接信号,它会强制槽在主线程中执行。您需要非常仔细地阅读整个已接受的答案(尤其是“编辑”部分),以了解为什么会发生这种情况。如果这样做,您将看到您的示例具有完全相同的问题(和解决方案)。
  • 我用第二个代码示例再次修改了我的问题。我也在尝试使用装饰器@pyqtSlot,但结果仍然相同。正如我已经说过的,您接受的答案对我没有太大帮助。您能否提供一个解决问题的上述代码的工作示例?这比无用的 cmets 更快。非常感谢您!
  • 我认为您仍然忽略了这一点,即 pyqt 创建了一个内部代理插槽以用于 python 可调用对象。如果您以您正在做的方式建立连接,代理插槽将留在主线程中。所以去掉多余的lambda,将信号直接连接到process插槽(你还需要去掉多余的message参数,改用self.message)。

标签: python python-3.x pyqt5 qthread


【解决方案1】:

下面是一个完整的示例。请注意,这使用 pyqtSlot 装饰器来解决问题。如果该行被注释掉,该示例将不再有效,因为在插槽连接之后 会将工作线程移动到线程。

from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class Worker(QObject):
    started = pyqtSignal()
    finished = pyqtSignal()
    def __init__(self, message):
        super(Worker, self).__init__()
        self.message = message
    @pyqtSlot()
    def process(self):
        i = 0
        cicle = 10000
        j = 0
        while j < 5:
            i = i + 1
            if i % cicle == 0:
                j = float(i/cicle)
                print(self.message, j)
        print("Worker", int(QThread.currentThread().currentThreadId()))
        self.finished.emit()

app = QApplication(['test'])

worker1 = Worker("Worker 1:")
thread1 = QThread()
print("Main app:", int(QThread.currentThread().currentThreadId()))
thread1.started.connect(worker1.process)
worker1.finished.connect(thread1.quit)
worker1.finished.connect(worker1.deleteLater)
thread1.finished.connect(app.quit)
worker1.moveToThread(thread1)

thread1.start()

app.exec_()

【讨论】:

    【解决方案2】:

    好的,

    我发现问题出在 lambda 函数中。只需将其删除即可解决问题。连接顺序不影响输出。这是工作示例:

    from PyQt5.QtCore import QObject, QThread, pyqtSignal
    
    
    class Worker(QObject):
        started = pyqtSignal()
        finished = pyqtSignal()
        def __init__(self, message):
            super(Worker, self).__init__()
            self.message = message
        def process(self, message):
            i = 0
            cicle = 10000
            j = 0
            while j < 5:
                i = i + 1
                if i % cicle == 0:
                    j = float(i/cicle)
                    print(message, j)
            print("Worker", int(QThread.currentThread().currentThreadId()))
            self.finished.emit()
    
    worker1 = Worker("Worker 1:")
    thread1 = QThread()
    thread1.started.connect(worker1.process(worker1.message))
    worker1.finished.connect(thread1.quit)
    worker1.finished.connect(worker1.deleteLater)
    thread1.finished.connect(thread1.deleteLater)
    worker1.moveToThread(thread1)
    print("Main app:", int(QThread.currentThread().currentThreadId()))
    
    
    thread1.start()
    

    输出:

    Main App: 11624
    Worker 1: 1.0
    Worker 1: 2.0
    Worker 1: 3.0
    Worker 1: 4.0
    Worker 1: 5.0
    Worker 12552
    

    对于新用户来说,绝对没有办法从这里接受的答案中推断出这个解决方案:PyQt: Connecting a signal to a slot to start a background operation

    【讨论】:

    • 顺序肯定确实会影响输出(如果您不使用pyqtSlot 装饰器)。此外,您答案中当前的代码在几个方面被破坏,因此它不能用于测试任何东西。我同意你的观点,lambda 增加了一些混乱,但我认为一旦你理解了潜在的问题,就不难看出问题出在哪里。话虽如此,我认为这是一个 PyQt 的错误功能,需要从源头进行修复。用户无需了解晦涩的实现细节即可使用信号/插槽等核心功能。
    猜你喜欢
    • 1970-01-01
    • 2011-08-05
    • 2017-01-01
    • 2013-05-28
    • 2017-03-19
    • 2020-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多