2014-09-08 7 views
2

나는 GUI와 프로그램 로직을 파이썬으로 작성했다. 웹에서 정보를 요청하는 중 urllib.requests (기타 등등)을 자주 호출하면 GUI가 응답하지 않을 때 문제가 발생하지만이 호출은 QThread으로 래핑됩니다. 나는 그것이 GIL 때문에 일어난다고 생각한다. 하지만 내가 어떻게 PyQt 응용 프로그램에서 QThread을 사용할 수 있습니까? PyQt에서 비동기 적으로 작동하도록 코드를 만들 수 없다면 어떻게 사용합니까? 위해 내 경우PyQt, QThread, GIL, GUI

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() 호출하지만 I :

--The는

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 

그리고 내가 사용하고있는 장소를 code-- 동시에 실행하려고합니다.

+0

이 올바르게 스레드를 시작 있습니까 :

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 

을 그리고이 장식을 사용하는 코드 'worker_thread.start()'를 사용하고 있습니까? 왜냐하면 만약 당신이'worker_thread.run()'을 실행하면 그것은 실행될 것이기 때문이다. 다른 스레드가 아닙니다. – Fenikso

+0

정확히 worker_thread.start()입니다. 잠깐, 코드를 게시 할게. –

+0

흥미로운 데코레이터 사용 ... 다른 스레드에서 잠을 자고있는 작은 실행 가능한 예제를 작성 하시겠습니까? 또는 실용적인 스레딩 예제로 질문에 대답 할 수는 있지만 질문이 서면으로 답할지는 확실하지 않습니다. – 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

나는 데코레이터의 문제를 해결했지만 작동하지는 않는다. 그렇다. 데코레이터를 사용하는 것은 약간 복잡 할 수있다. 다른 문제 - GUI 업데이트 (내 코드에 게시되지 않음)는 데코레이터에서 만든 스레드의 'finished'신호에 연결하여 해결됩니다.데코레이터를 사용하여 메서드를 동시에 수행하는 방법을 알고 싶어하는 사람들을 위해 내 질문을 솔루션으로 업데이트 할 것입니다. –

+0

코드에 신호가 표시되지 않습니다. 나는'self._finished_slot()'만 보았다. – Fenikso

+0

@VictorPolevoy 질문을 픽스로 업데이트하지 마십시오. 대신 대답을 작성하십시오. – Fenikso

0

솔루션이 이미 에 의해 Fenikso을 제공하지만, 장식을 사용하여 문제를 해결하는 방법도 재미있을 수 있습니다.

내가 수정 한 내 다음 qtthreaddecorator.py : 예를 들어,

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()'가'finished()'구독자 각각에게 통지 한 후에 실제로 스레드를 파기 할 때를 생각하고 있습니까? 문제가 생길 수 있습니까? –

+0

신호에 새로운 표기법을 사용하는 것이 좋습니다. 나는 PyQt5에서 오래된 것을 제거했다고 생각한다. – Fenikso