1

실험 설정을위한 PyQt GUI를 만들고 있습니다. 이것은 연산이 많은 연산을 포함하므로 멀티 프로세싱 모듈을 기반으로 한 아키텍처를 목표로하고 this answer에서 영감을 얻은 것입니다.임의의 서명으로 새로운 스타일의 PyQt 신호를 내 보냅니다.

QMainWindow는 메인 프로세스에 지침을 다시 보내 주 과정 및 공유 "콜백"대기열에서 지침을 얻을 수있는 개인 "작업"큐와

  1. 자식 프로세스를 생성
  2. QThread에 은 "콜백"큐를 폴링 예는 임의 시그너처 self.emit(QtCore.SIGNAL(signature), args) 오래 스타일 신호를 사용 QMainWindow

의 슬롯에 접속되는 신호들로 메시지를 번역. 내 질문 :이 기능을 새로운 스타일의 신호로 복제 할 수 있습니까?

나는 this questionthis one을 알고 있습니다. 그러나 일반 객체로 항상 valueChanged 신호를 내보내는 것은 자식 프로세스 중 하나에서받은 서명에 따라 다른 이름의 슬롯에 연결하려는 것이므로 내 필요에 맞지 않습니다. 여기

는 (하나의 자식 프로세스와 단순성에 대한 MainWindow를 하나 개의 슬롯이 있습니다 만, 완성 된 코드에서 몇 가지가있을 것입니다) 작업 코드 :

from multiprocessing import Process, Queue 
import sys 
from PyQt4 import QtGui, QtCore 


class CallbackQueueToSignal(QtCore.QThread): 

    def __init__(self, queue, parent=None): 
     super(CallbackQueueToSignal, self).__init__(parent) 
     self.queue = queue 

    def _emit(self, signature, args=None): 
     if args: 
      self.emit(QtCore.SIGNAL(signature), args) 
     else: 
      self.emit(QtCore.SIGNAL(signature)) 

    def run(self): 
     while True: 
      signature = self.queue.get() 
      self._emit(*signature) 


class WorkerProcess(Process): 

    def __init__(self, callback_queue, task_queue, daemon=True): 
     super(WorkerProcess, self).__init__() 
     self.daemon = daemon 
     self.callback_queue = callback_queue 
     self.task_queue = task_queue 

    def _process_call(self, func_name, args=None): 
     func = getattr(self, func_name) 
     if args: 
      func(args) 
     else: 
      func() 

    def emit_to_mother(self, signature, args=None): 
     signature = (signature,) 
     if args: 
      signature += (args,) 
     self.callback_queue.put(signature) 

    def run(self): 
     while True: 
      call = self.task_queue.get() 
      # print("received: {}".format(call)) 
      self._process_call(*call) 

    def text_upper(self, text): 
     self.emit_to_mother('data(PyQt_PyObject)', (text.upper(),)) 


class MainWin(QtGui.QMainWindow): 

    def __init__(self, parent=None): 
     super(MainWin, self).__init__(parent) 

     self.data_to_child = Queue() 
     self.callback_queue = Queue() 

     self.callback_queue_watcher = CallbackQueueToSignal(self.callback_queue) 
     self.callback_queue_watcher.start() 

     self.child = WorkerProcess(self.callback_queue, self.data_to_child) 
     self.child.start() 

     self.browser = QtGui.QTextBrowser() 
     self.lineedit = QtGui.QLineEdit('Type text and press <Enter>') 
     self.lineedit.selectAll() 
     layout = QtGui.QVBoxLayout() 
     layout.addWidget(self.browser) 
     layout.addWidget(self.lineedit) 
     self.layout_widget = QtGui.QWidget() 
     self.layout_widget.setLayout(layout) 
     self.setCentralWidget(self.layout_widget) 
     self.lineedit.setFocus() 
     self.setWindowTitle('Upper') 
     self.connect(self.lineedit, QtCore.SIGNAL('returnPressed()'), self.to_child) 
     self.connect(self.callback_queue_watcher, QtCore.SIGNAL('data(PyQt_PyObject)'), self.updateUI) 

    def to_child(self): 
     self.data_to_child.put(("text_upper",) + (self.lineedit.text(),)) 
     self.lineedit.clear() 

    def updateUI(self, text): 
     text = text[0] 
     self.browser.append(text) 

    def closeEvent(self, event): 
     result = QtGui.QMessageBox.question(
      self, 
      "Confirm Exit...", 
      "Are you sure you want to exit ?", 
      QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) 
     event.ignore() 

     if result == QtGui.QMessageBox.Yes: 
      # self.pipeWatcher.exit() 
      self.child.terminate() 
      event.accept() 

if __name__ == '__main__': 

    app = QtGui.QApplication(sys.argv) 

    form = MainWin() 
    form.show() 

    app.aboutToQuit.connect(app.deleteLater) 
    sys.exit(app.exec_()) 
+0

왜 연결이 서명에 의존해야합니까? 연결된 질문에서 제안 된대로 하나의 미리 정의 된 신호를 사용하고 식별자를 첫 번째 매개 변수로 보냅니다. – ekhumoro

+0

그런 경우 신호 사용의 이점을 상실하게됩니다. 예를 들어 프로그램을 실행하는 동안 특정 신호를 특정 슬롯에 연결하거나 연결 해제 할 수 없습니다. 하지만 내가 너의 요점을 놓치고 있을까? –

+0

새로운 스타일의 신호로 동적으로 방출되는 사용자 정의 신호를 복제하는 방법은 없습니다. 맞춤 신호는 클래스 속성으로 미리 정의해야합니다. 그러나 런타임시 신호 연결 및 연결 해제에 영향을 줄 수있는 이유는 없습니다. 또한 이것이 실제 질문과 어떻게 관련되는지 보지 못합니다. 코드 예제가 신호를 끊지 않습니다. – ekhumoro

답변

1

new-style signal and slot syntax는 신호가 있어야 QObject에서 상속받은 클래스의 클래스 속성으로 미리 정의됩니다. 클래스가 인스턴스화되면 bound-signal 객체가 인스턴스에 대해 자동으로 생성됩니다. 바운드 - 시그널 오브젝트는 connect/disconnect/emit 메쏘드를 가지며 __getitem__ 신택스는 다른 오버로드가 선택되도록합니다.

바운드 신호가 개체 인이기 때문에 더 이상 구식 구문으로 가능한 임의의 신호를 동적으로 방출 할 수 없습니다. 이는 단지 임의의 신호 (즉, 이 사전 정의되지 않은 신호)가 슬롯에 연결할 바운드 신호 객체를 가질 수 없었기 때문입니다.

질문의 예제 코드

은 여전히 ​​있지만, 새로운 스타일의 구문에 이식 할 수 있습니다

class CallbackQueueToSignal(QtCore.QThread): 
    dataSignal = QtCore.pyqtSignal([], [object], [object, object]) 
    ... 

    def _emit(self, signal, *args): 
     getattr(self, signal)[(object,) * len(args)].emit(*args) 

    def run(self): 
     while True: 
      args = self.queue.get() 
      self._emit(*args) 


class WorkerProcess(Process): 
    ... 

    def emit_to_mother(self, *args): 
     self.callback_queue.put(args) 

    def text_upper(self, text): 
     self.emit_to_mother('dataSignal', (text.upper(),)) 


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

     self.lineedit.returnPressed.connect(self.to_child) 
     self.callback_queue_watcher.dataSignal[object].connect(self.updateUI) 
+0

긴 응답 시간 동안 죄송합니다. 나는 당신이 의미하는 바를 이해합니다. 이것은 새로운 스타일 구문으로 포팅하는 정말 영리한 방법입니다. 감사합니다! 나는 그런 신호를 과부하하는 것에 대해 결코 생각하지 못했습니다. –

+0

그러나 이것은 구형 시그널을 가진 코드가 당신이 원하는 각각의 다른 시그널 이름을 위해'signal_name = QtCore.pyqtSignal ([], [object], [object, object])'붙여 넣기를 복사해야하기 때문에 덜 일반적입니다. 용도. 따라서 후속 질문은 포트를 정당화 할 수있는 새로운 스타일의 신호가 중요한 장점일까요? –

+0

@ ThibaudRuelle. 가장 큰 장점은 전진 호환성입니다. 이전 구문은 PyQt5에서 전혀 지원되지 않습니다. 그러나 더 일반적으로 새로운 스타일 구문은 훨씬 더 파이썬 적이면서 오류가 발생하기 쉽습니다. 구식 문법 (특히 초보자와 C++에 익숙하지 않은 사람들을 위해)에서 서명을 잘못 해석하는 것은 매우 쉽습니다. 더 나쁜 것은, 그것은 조용히 실패합니다. 새로운 스타일의 신호처럼 오류를 제기하는 것이 아닙니다. 나는 또한 "가독성 문제"라고 말할 가치가 있다고 생각합니다. 대부분의 사람들은 구식 구문이 매우 추악하고 장황한 것으로 생각합니다. 나는 그것을 전혀 놓치지 않는다. – ekhumoro