2014-09-08 2 views
2

У меня есть GUI и программная логика, написанная на Python. Я запрашиваю информацию из Интернета, вызывая частое сообщение urllib.requests (и т. Д.), И это вызывает проблему, когда GUI не отвечает, но эти вызовы завернуты в QThread. Я думаю, что это происходит из-за GIL. Но как я могу использовать QThread в приложении PyQt, какое использование его в PyQt, если я не могу сделать код для работы асинхронно?PyQt, QThread, GIL, GUI

--The code--

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() звонки в порядке, но я хотите выполнить их одновременно.

+0

Вы начинаете нить правильно, например, используя 'worker_thread.start()'? Потому что, если вы выполняете 'worker_thread.run()', он будет работать. Просто не в другой теме. – Fenikso

+0

Точно worker_thread.start(). Подождите, я отправлю код. –

+0

Интересное использование декораторов ... Не могли бы вы создать небольшой runnable пример с кнопкой, запускающей некоторый сон в другом потоке? Или я могу ответить на ваш вопрос рабочим примером, но я не уверен, ответит ли он на ваш вопрос, как написано. – Fenikso

ответ

0

Я думаю, что вы чрезмерно усложняете использование декораторов. Вы можете легко обернуть свой код в новый поток, используя примерно 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_()) 
+0

Я исправил проблему с декоратором, но он не работает, но да, использование декораторов может быть немного сложным. Другая проблема - обновление графического интерфейса (которое не было опубликовано в моем коде) решено путем подключения к сигналу «готовый» тех потоков, которые я создал декоратором.Я обновляю свой вопрос с помощью решения для других, которые хотят знать, как заставить методы работать одновременно, используя декораторы. –

+0

Я не вижу сигнала в коде. Я вижу только 'self._finished_slot()'. – Fenikso

+0

@VictorPolevoy Не обновляйте вопрос с исправлением. Вместо этого напишите ответ. – Fenikso

0

Хотя решение уже предлагают 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) 
+0

Но я все еще думаю о том, когда 'worker.quit()' фактически уничтожит поток - после того, как вы заметите каждого из 'законченных()' -подписчиков или нет? Может ли это вызвать проблему? –

+0

Я бы рекомендовал использовать новые обозначения для сигналов. Я думаю, что старый был удален в PyQt5. – Fenikso

Смежные вопросы