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

PyQt5 and Wing IDE: QThread freezes application

我最近想尝试使用 Wing IDE 而不是 Eclipse+Pydev 来使用 PyQt5 编写简单的 roguelike 游戏。该游戏使用单独的 QThread 和其中的 QObject 来处理游戏状态而不冻结 GUI。但是,我当前的应用程序在标准和 Eclipse 的解释器上运行良好,但在 Wing IDE 中冻结。这里我post代表问题的简单代码:

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 的数字,然后冻结。尝试按下停止按钮时它也会冻结。有没有办法继续使用 QThread with Wing IDE?

我的系统是Windows 8 (x64), Python 3.7.1, Wing IDE Personal 6.1; PyQt5.11(也用 PyQt 5.7 检查过); Qt 5.7.

我可以复制这个,看起来 moveToThread() 没有得到正确支持。这是另一个包含三个测试的测试用例,一个是 QThread 的子类,另一个是使用 moveToThread() 的子类,第三个是 QRunnable 的子类。第一个也是最后一个对我有用,而使用 moveToThread() 的那个也不起作用:

# From 
# 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 的未来版本中解决这个问题。感谢您发帖!