Python PyQt、QThread、GIL、GUI
我有用Python编写的GUI和程序逻辑。我经常通过调用Python PyQt、QThread、GIL、GUI,python,pyqt,qthread,gil,Python,Pyqt,Qthread,Gil,我有用Python编写的GUI和程序逻辑。我经常通过调用urllib.requests(等等)从web请求信息,当GUI没有响应但调用被QThread包装时,这会导致问题。我认为这是因为GIL。但是当我可以在PyQt应用程序中使用QThread时,如果我不能使代码异步工作,那么在PyQt中如何使用它呢 --代码-- qtthreaddecorator.py: from PyQt4 import QtCore class Worker(QtCore.QThread): def __ini
urllib.requests
(等等)从web请求信息,当GUI没有响应但调用被QThread
包装时,这会导致问题。我认为这是因为GIL。但是当我可以在PyQt应用程序中使用QThread
时,如果我不能使代码异步工作,那么在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()
在我的例子中,
\u get\u servers()
和\u get\u user\u 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_())
虽然Fenikso已经提供了解决方案,但是如何使用装饰器来解决这个问题也很有趣 我已将我的
qtthreaddecorator.py
更正为以下内容:
from PyQt4 import QtCore
class Worker(QtCore.QThread):
threads = []
def __init__(self, thread_name, function, *args, **kwargs):
QtCore.QThread.__init__(self)
self._thread_name = thread_name
self._function = function
self._args = args
self._kwargs = kwargs
def run(self):
Worker.threads.append(self.currentThreadId())
self._function(*self._args, **self._kwargs)
self.emit(QtCore.SIGNAL('finished()'))
Worker.threads.remove(self.currentThreadId())
def qt_thread_decorator():
def decorator(function):
def wrapper(*args, **kwargs):
worker = Worker(function.__name__, function, *args, **kwargs)
def on_finish():
worker.quit()
worker.finished.connect(on_finish)
worker.start()
return worker
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()
def _get_servers():
self._get_my_servers()
@qtthreaddecorator.qt_thread_decorator()
def _get_user_info():
self._get_user_info()
_get_servers().finished.connect(self._fill_servers)
_get_user_info().finished.connect(self._fill_user_info)
是否正确启动线程,例如使用
辅助线程.start()
?因为如果您执行worker\u thread.run()
它将运行。只是不在另一个线程中。正是worker\u thread.start()。等等,我要发布代码。装饰器的有趣用法。。。您是否介意创建一个小的可运行示例,其中button在另一个线程中运行一些睡眠?或者我可以用一个工作线程示例来回答你的问题,但我不确定它是否能回答你的书面问题。我同意@Fenikso,这绝对是装饰师的一个有趣用法。我认为您的问题可能源于这样一个事实,wrapper
不返回worker,因此它不存在于该函数之外。仅从wrapper
返回它是不够的。最终,您需要工人存在于您正在装饰的函数之外,否则,当这些函数返回时,您最终会遇到同样的问题。我认为你使用装饰器会使你自己的事情过于复杂。我已经解决了装饰器的问题,它不起作用,但是是的,装饰器的使用可能有点复杂。另一个问题是,通过连接到decorator创建的线程的finished
信号,可以解决更新GUI(我的代码中没有发布)的问题。我将用解决方案更新我的问题,以帮助那些想知道如何通过使用decorator使方法并发工作的人。我看不到代码中的信号。我只看到了self.\u finished\u slot()
@VictorPolevoy不使用修复程序更新问题。改为写一个答案。但我仍然在想,在通知每个finished()
-订阅者之后,worker.quit()
何时会真正破坏线程?可能会引起问题吗?我建议对信号使用新的符号。我想在PyQt5中删除了旧的。