PyQt5 и Wing IDE: приложение QThread останавливает работу - PullRequest
0 голосов
/ 27 ноября 2018

Недавно я хотел попробовать Wing IDE вместо Eclipse + Pydev для программирования простой игры типа roguelike с использованием PyQt5.Эта игра использует отдельный QThread с QObject внутри для обработки состояния игры без зависания графического интерфейса.Однако мое текущее приложение, которое хорошо работает со стандартным и интерпретатором Eclipse, зависает в Wing IDE.Здесь я публикую простой код, который представляет проблему:

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 с Wing IDE?

Моя система - Windows 8 (x64), Python 3.7.1, Wing IDE Personal 6.1;PyQt5.11 (также проверено с помощью PyQt 5.7);Qt 5.7.

1 Ответ

0 голосов
/ 27 ноября 2018

Я могу повторить это, и похоже, что moveToThread () не поддерживается правильно.Вот еще один тест, в котором есть три теста: один, который подклассов QThread, другой, который использует moveToThread (), и третий, который подклассов QRunnable.Первая и последняя работа для меня, и та, которая использует moveToThread (), также не работает:

# From /7673691/fonovyi-potok-s-qthread-v-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.Спасибо за публикацию этого!

...