PyQt5和Wing IDE:QThread冻结了应用程序

时间:2018-11-27 14:17:34

标签: python python-3.x pyqt qthread

我最近想尝试使用Wing IDE而不是Eclipse + Pydev来使用PyQt5编写简单的类似rogue的游戏。该游戏使用单独的QThread及其内部的QObject处理游戏状态,而不会冻结GUI。但是,我当前的应用程序在Wing IDE中冻结,该应用程序与标准和Eclipse的解释器一起很好地工作。在这里,我发布了代表问题的简单代码:

import sys, time
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QThread
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLCDNumber, QVBoxLayout, QWidget

class Counter(QObject):
    '''
    QObject-based class which works inside separate thread and
    emits numbers from 1 to 10 to the GUI thread and then stops.
    '''

    new_value = pyqtSignal(int)
    ended = pyqtSignal()

    def __init__(self):
        QObject.__init__(self)
        self.isStopped = False

    @pyqtSlot()
    def start(self):
        self.isStopped = False
        for n in range(1, 11):
            if not self.isStopped:
                self.new_value.emit(n)
                time.sleep(0.3)
            else:
                break
        self.ended.emit()

    @pyqtSlot()
    def stop(self):
        self.isStopped = True


class SimpleWindow(QMainWindow):
    '''
    Application window with 3 buttons and 1 LCD display.
    '''

    def __init__(self):
        QMainWindow.__init__(self)

        # Adding and configuring widgets
        self.central = QWidget(self)
        self.central.resize(100, 150)
        self.resize(100, 150)
        self.layout = QVBoxLayout()
        self.central.setLayout(self.layout)
        self.start_QBtn = QPushButton()
        self.start_QBtn.setText('Start')
        self.stop_QBtn = QPushButton()
        self.stop_QBtn.setText('Stop')
        self.number_LCD = QLCDNumber()
        self.status_QBtn = QPushButton()
        self.status_QBtn.setText('Status')
        self.layout.addWidget(self.start_QBtn)
        self.layout.addWidget(self.stop_QBtn)
        self.layout.addWidget(self.status_QBtn)
        self.layout.addWidget(self.number_LCD)

        # Creating new thread and adding QObject-based object to it
        self.thread = QThread()
        self.counter = Counter()
        self.counter.moveToThread(self.thread)

        # Connecting button signals to slots
        self.start_QBtn.clicked.connect(self.thread.start)
        self.status_QBtn.clicked.connect(self.status)
        self.stop_QBtn.clicked.connect(lambda: self.counter.stop())        

        # Connecting thread signals to slots
        self.counter.new_value.connect(self.show_value)
        self.counter.ended.connect(self.thread.quit)
        self.thread.started.connect(self.counter.start)

        self.thread.start()

    @pyqtSlot(int)
    def show_value(self, number):
        '''
        Display value obtained from Counter() in the LCD widget.
        '''
        self.number_LCD.display(number)

    @pyqtSlot()
    def status(self):
        '''
        Print thread status in the console.
        '''
        print('Thread is running: ', self.thread.isRunning())


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

应用程序启动,按预期显示从1到10的数字,然后冻结。当尝试按下“停止”按钮时,它也会冻结。是否可以通过Wing IDE继续使用QThread?

我的系统是Windows 8(x64),Python 3.7.1,Wing IDE Personal 6.1; PyQt5.11(也已与PyQt 5.7一起检查); Qt 5.7。

1 个答案:

答案 0 :(得分:1)

我可以复制它,并且好像不能正确支持moveToThread()。这是另一个测试用例,其中包含三个测试,一个是QThread的子类,另一个是使用moveToThread()的子类,第三个是QRunnable的子类。对我来说,第一个和最后一个工作以及使用moveToThread()的那个都不起作用:

# From http://stackoverflow.com/questions/6783194/background-thread-with-qthread-in-pyqt
# with minor modifications

import time
import sys

from PyQt5 import QtCore
# Hack needed to avoid _NotifyModule bug
from PyQt5.QtCore import *

# Subclassing QThread
# http://qt-project.org/doc/latest/qthread.html
class AThread(QtCore.QThread):

    def run(self):
        count = 0
        while count < 5:
            time.sleep(1)
            print("Increasing")
            count += 1

# Subclassing QObject and using moveToThread
# http://blog.qt.digia.com/blog/2007/07/05/qthreads-no-longer-abstract
class SomeObject(QtCore.QObject):

    finished = QtCore.pyqtSignal()

    def longRunning(self):
        count = 0
        while count < 5:
            time.sleep(1)
            print("Increasing")
            count += 1
        self.finished.emit()

# Using a QRunnable
# http://qt-project.org/doc/latest/qthreadpool.html
# Note that a QRunnable isn't a subclass of QObject and therefore does
# not provide signals and slots.
class Runnable(QtCore.QRunnable):

    def run(self):
        count = 0
        app = QtCore.QCoreApplication.instance()
        while count < 5:
            print("Increasing")
            time.sleep(1)
            count += 1
        app.quit()


def usingQThread():
    app = QtCore.QCoreApplication([])
    thread = AThread()
    thread.finished.connect(app.exit)
    thread.start()
    sys.exit(app.exec_())

def usingMoveToThread():
    app = QtCore.QCoreApplication([])
    objThread = QtCore.QThread()
    obj = SomeObject()
    obj.moveToThread(objThread)
    obj.finished.connect(objThread.quit)
    objThread.started.connect(obj.longRunning)
    objThread.finished.connect(app.exit)
    objThread.start()
    sys.exit(app.exec_())

def usingQRunnable():
    app = QtCore.QCoreApplication([])
    runnable = Runnable()
    QtCore.QThreadPool.globalInstance().start(runnable)
    sys.exit(app.exec_())

if __name__ == "__main__":
    usingQThread()
    #usingMoveToThread()
    #usingQRunnable()

请注意,您不必取消注释即可进行测试,因为它们都调用sys.exit,因此一次只能尝试其中的一个。

我将看看我们是否可以在以后的Wing版本中解决此问题。感谢您发布!!