2016-04-06 11 views
1

私はPyQt5のメニューシステムトレイで作業しています。私はPyQt5で非常に新しいです。私がしたいのは、メニューがブロックされていない状態でアクションをトリガーすることです(マルチスレッド)。多くの場所を読んだ後、私はQthreadを使って行くべきだという結論に達しました(しかし、そのクラスの仕組みを理解できれば...)。しかし、threadingの使用は、私のアプリケーションが非常に単純であることを考慮すると、それほど悪くないでしょう。だから、私はimport threadingを使用して、次のコードを試してみました:私は私のメニューを起動すると、私はそれを期待してPyQt5 QObject:別のスレッドにある親の子を作成できません

from PyQt5 import QtCore, QtGui, QtWidgets 
import threading 

class menubar(object): 
    def __init__(self): 
    signal.signal(signal.SIGINT, signal.SIG_DFL) 
    self.systray = True 
    self.stopped = False 

    def search_menu(self): 
     self.SearchAction = menu.addAction("Search") 
     self.SearchAction.triggered.connect(self.search_cast) 

    def _search_cast_(self): 
     args.select_cc = True 
     self.cc.initialize_cast() 
     self.cast_list() 

    def search_cast(self): 
     threading.Thread(target=self._search_cast_).start() 

#some more methods here... 

def main(): 

    menubar() 
    app = QtWidgets.QApplication(sys.argv) 
    tray = QtWidgets.QSystemTrayIcon(icon) 

    menu = QtWidgets.QMenu() 
    start = menubar() 
    start.search_menu() 
    start.separator_menu() 
    start.populating_menu() 
    start.separator_menu() 
    start.stop_menu() 
    start.resetaudio_menu() 
    start.about_menu() 
    start.exit_menu() 

    tray.setContextMenu(menu) 
    tray.show() 
    app.exec_() 

if __name__ == '__main__': 
    main() 

、すべてが所定の位置にあります。次に、メニューSearchをクリックすると、アクションによってself.search_castメソッドがトリガされ、見つかったリストがメニューに表示されます。私も自分のアプリケーションがブロックされることなく、検索を行って見ることができますが、それは私が次のエラーを取得が完了したとき:この後

QObject: Cannot create children for a parent that is in a different thread. 
(Parent is QMenu(0x7fcef497c160), parent's thread is  QThread(0x7fcef2603d10), current thread is QThread(0x7fcef4a89360) 
QObject: Cannot create children for a parent that is in a different thread. 
(Parent is QMenu(0x7fcef497c160), parent's thread is QThread(0x7fcef2603d10), current thread is QThread(0x7fcef4a89360) 
QObject: Cannot create children for a parent that is in a different thread. 

を、メニューはまだそれが応答性であるという意味が、これ以上のアクションで「機能性」でありますトリガーすることができます。さらに、それ以上のスレッドが作成されていないようです。誰かがなぜ私にこのことが起こっているのか説明できたら嬉しいです。私は更新 ...

を光が表示されない:

私が含まれていることになりましたworker.pyを作成しました:

from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot 
#some other imports 


class Worker(QObject): 
    finished = pyqtSignal() 


@pyqtSlot() 
def _search_cast_(self): 
    self.cc = casting() 
    self.cc.initialize_cast() 
    self.finished.emit() 

は、それから私はclass menubarに次のように追加しました:

class menubar(object): 
    def __init__(self): 
     self.cc = casting() 
     signal.signal(signal.SIGINT, signal.SIG_DFL) 
     self.cc.cast = None 
     self.systray = True 
     self.stopped = False 

     self.obj = worker.Worker() # no parent! 
     self.thread = QThread() # no parent! 
     self.obj.moveToThread(self.thread) 
     self.obj.finished.connect(self.thread.quit) 
     self.thread.started.connect(self.obj._search_cast_) 

    def search_menu(self): 
     self.SearchAction = menu.addAction("Search") 
     self.SearchAction.triggered.connect(self.search_cast) 

    def search_cast(self): 
    self.thread.start() 
    self.cast_list() 

    def cast_list(self): 
    if len(self.cc.availablecc) == 0: 
    # some actions here. 

次に、次のエラーが表示されます。

AttributeError: 'casting' object has no attribute 'availablecc' 

実際にworkerが、ccと呼ばれる外部クラスからavailableccを回復していることを確認します。しかし何らかの理由で、menubarクラスでは受信されていません。私はこれに基づいて作業中ですhttps://stackoverflow.com/a/33453124/1995261

答えて

1

私は自分自身に答えるつもりです。 https://stackoverflow.com/a/33453124/1995261に触発されて、私は以下を実装してこれを解決しました:

1)メニューをブロックしていた_search_cast_メソッドを実行するworker.pyを作成しました。このメソッドが検索を終了すると、2つの信号、a)listを回復したことを通知するもの、およびb)メソッドが終了したものが出力されます。 main.py

#worker.py 
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot 


class Worker(QObject): 
    finished = pyqtSignal() 
    intReady = pyqtSignal(list) 
    def __init__(self): 
     QObject.__init__(self) 

    @pyqtSlot() 
    def _search_cast_(self): 
     self.cc = casting() 
     self.cc.initialize_cast() 
     availablecc = self.cc.availablecc 
     self.intReady.emit(availablecc) 
     self.finished.emit() 

2) は、私は次のことをダンプし、私はコメントをコード内で説明しよう:

#main.py 
from PyQt5.QtCore import QThread, QObject, pyqtSignal, pyqtSlot 
import worker # This is to import worker.py 
class menubar(object): 
    def __init__(self): 
     signal.signal(signal.SIGINT, signal.SIG_DFL) 
     self.cc.cast = None 
     self.systray = True 
     self.stopped = False 

     self.obj = worker.Worker() # The worker is started with no parent! 
     self.thread = QThread() # We initialise the Qthread class with no parent! 
     self.obj.intReady.connect(self.onIntReady) # We receive the signal that the list is ready 
     self.obj.moveToThread(self.thread) # Moving the object to the thread 
     self.obj.finished.connect(self.thread.quit) # When the method is finished we receive the signal that it is finished 
     self.thread.started.connect(self.obj._search_cast_) # We need to connect the above with the desired method inside the work.py 

     self.app = QtWidgets.QApplication(sys.argv) 

     def search_menu(self): 
      self.SearchAction = self.menu.addAction("Search") 
      self.SearchAction.triggered.connect(self.search_cast) 

     def onIntReady(self, availablecc):  # This method receives the list from the worker 
      print ('availablecc', availablecc) # This is for debugging reasons to verify that I receive the list with the correct content 
      self.availablecc = availablecc 

     def search_cast(self): #This method starts the thread when self.SearchAction is triggered 
      args.select_cc = True 
      self.thread.start() 

このように、メニューはブロックされませんlistを検索する際に、 activity monitorでエラーを監視しているときに、画面にエラーが表示されず、threadsの番号が正しく表示されます。

私はこれが人々に役立つことを願っています。より正確な情報(私はまだPyQtを学んでいて、私の表現はあまり良くないかもしれません)については、上記のリンクを確認することをお勧めします。

+0

コードを共有してくれてありがとう。私はあなたの言ったことをやろうとしましたが、私のGUIは応答しません。私の接続されたメソッド(あなたの場合はonIntReady)がコンソールで動作しています。 – Hilal

0

これは、このエラーのためのGoogleの最上位の回答で、これを正しく動作させるには予想以上の時間がかかりました。Python 3とPyQt 5の非常に単純なソリューションを共有します(インポートを変更した場合、 PyQt4も私は推測する)。

私が持っていた状況は、右クリックメニューのあるシステムトレイのアイコンでした。これは、別のスレッドが要求したときに再構築する必要があります。もちろん、これをスレッドの制限によって通信したい他の問題にも適用できます。

import time 
import sys 
import threading 
from PyQt5 import QtGui 
from PyQt5 import QtWidgets 
from PyQt5 import QtCore 



class SystemTrayIcon(QtWidgets.QSystemTrayIcon): 
    def __init__(self, icon=None, parent=None): 
     icon = QtGui.QIcon(QtWidgets.QApplication.style().standardPixmap(QtWidgets.QStyle.SP_MediaPlay)) 
     QtWidgets.QSystemTrayIcon.__init__(self, icon, parent) 

     self.menu = QtWidgets.QMenu(parent) 
     self.setContextMenu(self.menu) 

     self.build_menu() 
     self.show() 

     # see http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html for more information 
     self.signal = MySignal() 
     self.signal.sig_no_args.connect(self.build_menu) 
     self.signal.sig_with_str.connect(self.print_string) 


    def build_menu(self): 
     ''' This function should be called in order to rebuild 
     the right-click menu for the systray icon''' 
     global list_dict_streams 
     self.menu.clear() 

     exitAction = self.menu.addAction("Exit") 
     exitAction.triggered.connect(self._exit) 

     for x in list_dict_streams : 
      self.menu.addAction(x) 


    def print_string(self, str): 
     print(str) 


    def _exit(self): 
     QtCore.QCoreApplication.exit() 



class MySignal(QtCore.QObject): 
    ''' Why a whole new class? See here: 
    https://stackoverflow.com/a/25930966/2441026 ''' 
    sig_no_args = QtCore.pyqtSignal() 
    sig_with_str = QtCore.pyqtSignal(str) 


list_dict_streams = ["1"] 
def work_thread(trayIcon): 
    ''' Will add one menu item to the systray menu every 5 seconds 
    and will send a signal with a string ''' 
    global list_dict_streams 

    while True: 
     trayIcon.signal.sig_no_args.emit() 
     trayIcon.signal.sig_with_str.emit("String emitted") 
     list_dict_streams.append(str(len(list_dict_streams)+1)) 
     time.sleep(5) 


def main(): 
    app = QtWidgets.QApplication(sys.argv) 
    trayIcon = SystemTrayIcon() 

    t = threading.Thread(target=work_thread, args=(trayIcon,)) 
    t.daemon = True  # otherwise the 'Exit' from the systray menu will not work 
    t.start() 

    sys.exit(app.exec_()) 


if __name__ == '__main__': 
    main() 

基本的には、新しいclass MySignal(QtCore.QObject)whyを作成する必要があります。私は2つの例を持つクラスを作成しました - それは、文字列を渡すことができる別のものに引数を送らないクラスを作成しました。もちろんdefine other argumentsです。次に、ターゲットスレッドでこのクラスの新しいインスタンスを作成し、そのクラスの関数をターゲット内の関数(私の場合はシステムトレースアイコン)に接続します。その後、私はwhileループのようにemit(...)関数を呼び出すことができます。
他のスレッドから直接trayIcon.build_menu()に電話をかけた場合と比べて、信号を発信するだけでQtがうれしいです。

関連する問題