PyQt,QThread,GIL,GUI


问题内容

我有用Python编写的GUI和程序逻辑。我urllib.requests经常通过调用(等等)从Web请求信息,这在GUI无响应但此调用用包裹时会引起问题QThread。我认为这是因为发生GIL。但是,当我不能QThread在PyQt应用程序中使用时,PyQt如果我不能使代码异步工作,该如何使用呢?

- 编码 -

qtthreaddecorator.py:

from PyQt4 import QtCore

class Worker(QtCore.QThread):
    def __init__(self, thread_name, finished_slot, function, *args, **kwargs):
        QtCore.QThread.__init__(self)

        self._thread_name = thread_name
        self._function = function
        self._args = args
        self._kwargs = kwargs

        self._finished_slot = finished_slot

    def run(self):
        self._function(*self._args, **self._kwargs)

        self._finished_slot()

        return

def qt_thread_decorator(slot):
    def decorator(function):
        def wrapper(*args, **kwargs):
            worker = Worker(function.__name__, slot, function, *args, **kwargs)
            worker.start()

            return
        return wrapper
    return decorator

还有我使用它的地方:

import qtthreaddecorator

class MainWindow(QtGui.QMainWindow, form_class):

def __init__(self, parent=None):
    QtGui.QMainWindow.__init__(self, parent)
    self.setupUi(self)

    self.init()

def init(self):
    @qtthreaddecorator.qt_thread_decorator(self._fill_servers)
    def _get_servers():
        self._get_my_servers()
    @qtthreaddecorator.qt_thread_decorator(self._fill_user_info)
    def _get_user_info():
        self._get_user_info()

    _get_servers()
    _get_user_info()

在我的情况,_get_servers()_get_user_info()呼吁为了,但我想同时执行它们。


问题答案:

我认为您在使用装饰器方面过于复杂。您可以使用大约3-4行设置代码轻松地将代码包装在新线程中。另外,我不认为您应该直接从另一个线程调用完成的插槽。您应该使用连接的信号将其激活。

import sys
from time import sleep
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

class Signals(QObject):
    update = pyqtSignal(int)
    enable_button = pyqtSignal(bool)

class Window(QWidget):
    def __init__(self, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)

        self.button = QPushButton("Run", self)
        self.button.clicked.connect(self.onButton)

        self.progress = QProgressBar(self)
        self.progress.setTextVisible(False)

        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(5, 5, 5, 5)
        self.layout.addWidget(self.button)
        self.layout.addWidget(self.progress)
        self.layout.addStretch()

        self.worker_thread = QThread()
        self.worker_thread.run = self.worker
        self.worker_thread.should_close = False

        self.signals = Signals()
        self.signals.update.connect(self.progress.setValue)
        self.signals.enable_button.connect(self.button.setEnabled)

        self.setLayout(self.layout)
        self.show()
        self.resize(self.size().width(), 0)

    # Override
    def closeEvent(self, e):
        self.worker_thread.should_close = True
        self.worker_thread.wait()

    @pyqtSlot()
    def onButton(self):
        self.button.setDisabled(True)
        self.worker_thread.start()

    # Worker thread, no direct GUI updates!
    def worker(self):
        for i in range(101):
            if self.worker_thread.should_close:
                break
            self.signals.update.emit(i)
            sleep(0.1)
        self.signals.enable_button.emit(True)

app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())