简体   繁体   中英

PyQt5 and Wing IDE: QThread freezes application

I recently wanted to try Wing IDE instead of Eclipse+Pydev for programming simple roguelike game using PyQt5. This game uses separate QThread with QObject inside it to process game state without freezing the GUI. However, my current application, which works well with standard and Eclipse's interpreter, freezes in Wing IDE. Here I post simple code which represents the problem:

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

Application starts, displays numbers from 1 to 10 as intended and then freezes. It also freezes when trying to push Stop button. Is there a solution to keep using QThread with Wing IDE?

My system is Windows 8 (x64), Python 3.7.1, Wing IDE Personal 6.1; PyQt5.11 (also checked with PyQt 5.7); Qt 5.7.

I can replicate this and it looks like moveToThread() is not being supported correctly. Here's another test case that has three tests in it, one that subclasses QThread, another that uses moveToThread(), and a third that subclasses QRunnable. The first and last work for me and the one using moveToThread() also does not work:

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

Note that you have to uncomment the one to test, since they all call sys.exit so only one can be tried at a time.

I'll see if we can fix this in a future release of Wing. Thanks for posting this!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM