2016-10-05 1 views
1

私は、自分のコントロール下にないスレッドから呼び出されたコールバックを介して外部ソースから情報を受け取り、QThreadではないPyQtアプリケーションを持っています。このような情報をポーリングせずにメインスレッドに渡す正しい方法は何ですか?特に、新しいデータの到着時にメインスレッド(または別のQThread)を起動できるように、Qtシグナルを出したいと思います。PyQtは非QThreadのメインスレッドをウェイクします

+0

理解しないようにしてください、あなたは非qtのスレッドのソースコードを変更することはできません?完了したら信号を出すのが好きですか? – PyNico

+0

私の制御下にあるのは、通常のPython関数であるコールバック関数だけです。私。信号を発信することはできますが、これは許可されますか?その関数を実行するスレッドは私のコードでは作成されず、Qtの概念も全くありません。だから、放射シグナルがスレッドセーフである(たとえQt以外のスレッドからでも)かどうか尋ねるべきでしょうか? – burnpanck

+0

これはベストプラクティスか悪いのか分かりませんが、それは私のやり方です。あなたのコールバックからシグナルを送ります。私はそれがうまくいくと思う。 (Qtスレッドの安全性は接続の種類によって異なります:http://doc.qt.io/qt-4.8/threads-qobject.html#signals-and-slots-across-threads – PyNico

答えて

1

信号のデフォルトの接続タイプは、ドキュメントは、このように記述Qt.AutoConnectionある:

の信号を受信 オブジェクトとは異なるスレッドから出射された場合、信号は次のように動作し、キューイングされQt :: QueuedConnection。 それ以外の場合は、スロットが直接呼び出され、 Qt :: DirectConnectionとして動作します。接続のタイプは、 信号が放出されたときに決定されます。

ので、信号を発する前に、Qtは、単にそれをキューイングするかどうかを決定する前に、送信者と受信者の現在スレッドの親和性を比較します。重要ではない元のスレッドが最初に開始された方法。ここで

はPythonのワーカースレッドを使用して、簡単なデモです:

import sys, time, threading 
from PyQt4 import QtCore, QtGui 

class Worker(object): 
    def __init__(self, callback): 
     self._callback = callback 
     self._thread = None 

    def active(self): 
     return self._thread is not None and self._thread.is_alive() 

    def start(self): 
     self._thread = threading.Thread(target=self.work, name='Worker') 
     self._thread.start() 

    def work(self): 
     print('work: [%s]' % threading.current_thread().name) 
     for x in range(5): 
      time.sleep(1) 
      self._callback(str(x)) 

class Window(QtGui.QPushButton): 
    dataReceived = QtCore.pyqtSignal(str) 

    def __init__(self): 
     super(Window, self).__init__('Start') 
     self.clicked.connect(self.start) 
     self.dataReceived.connect(self.receive) 
     self.worker = Worker(self.callback) 

    def receive(self, data): 
     print('received: %s [%s]' % (data, threading.current_thread().name)) 

    def callback(self, data): 
     print('callback: %s [%s]' % (data, threading.current_thread().name)) 
     self.dataReceived.emit(data) 

    def start(self): 
     if self.worker.active(): 
      print('still active...') 
     else: 
      print('start: [%s]' % threading.current_thread().name) 
      self.worker.start() 

if __name__ == '__main__': 

    app = QtGui.QApplication(sys.argv) 
    window = Window() 
    window.show() 
    print('show: [%s]' % threading.current_thread().name) 
    sys.exit(app.exec_()) 

典型的な出力:

$ python test.py 
show: [MainThread] 
start: [MainThread] 
work: [Worker] 
callback: 0 [Worker] 
received: 0 [MainThread] 
still active... 
callback: 1 [Worker] 
received: 1 [MainThread] 
still active... 
callback: 2 [Worker] 
received: 2 [MainThread] 
still active... 
callback: 3 [Worker] 
received: 3 [MainThread] 
callback: 4 [Worker] 
received: 4 [MainThread] 
+0

よあなたはQtを "送信者と受信者の現在のスレッドアフィニティと比較しました"と言っています:送信者のスレッドアフィニティはどのように定義されていますか?それは実際のスレッド(例えば 'threading.current_thread()')か、 'QObject'を送信するスレッド(あなたの例では' Worker'が作成されたときに間違ったスレッドになります)ですか?前者は、Qtのドキュメントの「スレッドアフィニティ」の定義に従っているのに対して、後者は「スレッドアフィニティ」の定義に準拠していますが、あなたの解決策を正しいものにすることができます。http://doc.qt.io/qt-5/qobject.html#thread-affinity – burnpanck

+0

私の間違いは、あなたの例の送信オブジェクトは実際に受信オブジェクトと同じです、それは 'Window'です(信号' dataReceived'とスロット 'callback'は' Window'の一部です)。したがって、実際にドキュメントごとに「スレッドアフィニティ」を使用すると、間違ってダイレクトコールが実行されます。したがって、Qtは実際に実行中のスレッド(送信側のスレッドアフィニティではなく)を受信側のスレッドアフィニティと比較することになります。 @ burnpanck。 – burnpanck

+0

はい、「送信者」は、実際にコールバック関数を呼び出すためのものです(これは参照先です)。 – ekhumoro

0

外部デバイスやライブラリをポーリングするのと同じ方法で処理します。コールバックを処理する別のワーカースレッドを作成し、メインのGUIスレッドに信号を送信します。

class Worker(QObject): 

    data_ready = pyqtSignal(object, object) 

    def callback(self, *args, **kwargs): 
     self.data_ready.emit(args, kwargs) 


class Window(...) 

    def __init__(self): 
     ... 
     self.worker = Worker(self) 
     # Connect worker callback function to your external library 
     library.register_callback(self.worker.callback) #?? 
     ... 

     # Mover worker to thread and connect signal 
     self.thread = QThread(self) 
     self.worker.data_ready.connect(self.handle_data) 
     self.worker.moveToThread(self.thread) 
     self.thread.start() 

    @pyqtSlot(object, object) 
    def handle_data(self, args, kwargs): 
     # do something with data 
+0

スレッド 'self.thread'は本当に必要なのでしょうか?それは何をしますか?' Worker.callback'関数はもちろん 'ライブラリによって作成された未知のエイリアンスレッド上で実行されます'... – burnpanck

関連する問題