为什么外部函数导致PyQt5窗口冻结?

时间:2017-03-26 03:47:38

标签: python pyqt python-3.5 pyqt5 qthread

以下是一些打破的示例代码:

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并保持主窗口响应。但是,我不知道如何使用self.progress.setValue更新值,因为它无法在课程External中访问。

到目前为止,据我所知,我必须使用信号来实现这一目标。 PyQt4的大部分文档都使得找到解决方案变得更加困难。

我面临的另一个问题是能够从班级External内启动Actions主题。

此问题的答案也将成为PyQt5的有价值的文档。 提前谢谢。

2 个答案:

答案 0 :(得分:1)

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

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_())

答案 1 :(得分:0)

这是一个从类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_())