为什么外部函数会导致 PyQt5 window 冻结?

Why do external functions cause the PyQt5 window to freeze?

这是一些中断的示例代码:

import sys
import time
from PyQt5.QtWidgets import (QApplication, QDialog,
                             QProgressBar)

class Actions(QDialog):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.progress = QProgressBar(self)
        self.progress.setGeometry(0, 0, 300, 25)
        self.show()

        self.count = 0

        while self.count < 100:
            self.count += 1
            time.sleep(1) # Example external function
            self.progress.setValue(self.count)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Actions()
    sys.exit(app.exec_())

运行 这将导致它冻结并变得无响应,特别是在 windows 环境中。用任何非 PyQt5 函数替换 time.sleep 函数将产生相同的结果。

据我了解,这与未使用 QThread 在单独的线程中调用该函数有关。我使用 this answer 作为参考并提出了部分解决方案。

import sys
import time

from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import (QApplication, QDialog,
                             QProgressBar)

class External(QThread):

    def run(self):
        count = 0

        while count < 100:
            count += 1
            print(count)
            time.sleep(1)

class Actions(QDialog):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.progress = QProgressBar(self)
        self.progress.setGeometry(0, 0, 300, 25)
        self.show()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Actions()
    calc = External()
    calc.finished.connect(app.exit)
    calc.start()
    sys.exit(app.exec_())

这将在后台 运行 time.sleep 并保持主要 window 响应。但是,我不知道如何使用 self.progress.setValue 更新值,因为在 class External.

中无法访问它

据我所知,我必须使用信号来完成此操作。大多数文档都是针对 PyQt4 的,这使得找到解决方案变得更加困难。

我面临的另一个问题是能够从 class Actions.

中启动 External 线程

此问题的答案也将作为 PyQt5 的宝贵文档。 提前致谢。

您必须使用信号来更新值。

import sys
import time

from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import (QApplication, QDialog,
                             QProgressBar)

class External(QThread):
    countChanged = pyqtSignal(int)
    def run(self):
        count = 0

        while count < 100:
            count += 1
            self.countChanged.emit(count)
            print(count)
            time.sleep(1)

class Actions(QDialog):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.progress = QProgressBar(self)
        self.progress.setGeometry(0, 0, 300, 25)
        self.show()

    def onCountChanged(self, value):
        self.progress.setValue(value)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Actions()
    calc = External()
    calc.countChanged.connect(window.onCountChanged)
    calc.start()
    sys.exit(app.exec_())

这是一个从 class Actions 内部启动线程并使用按钮的版本:

import sys
import time

from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import (QApplication, QDialog,
                             QProgressBar, QPushButton)

class External(QThread):

    countChanged = pyqtSignal(int)

    def run(self):
        count = 0

        while count < 100:
            count += 1
            time.sleep(1)
            print(count)
            self.countChanged.emit(count)

class Actions(QDialog):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.progress = QProgressBar(self)
        self.progress.setGeometry(0, 0, 300, 25)
        self.button = QPushButton('Start', self)
        self.button.move(0, 30)
        self.show()

        self.button.clicked.connect(self.onButtonClick)

    def onButtonClick(self):
        self.calc = External()
        self.calc.countChanged.connect(self.onCountChanged)
        self.calc.start()

    def onCountChanged(self, value):
        self.progress.setValue(value)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Actions()
    sys.exit(app.exec_())