2017-02-24 3 views
1

Ich versuche, meinen Kopf darum zu wickeln, wie man richtig Threads und Signale mit und Python3 verwendet, aber irgendwie gelingt es nicht zu verstehen, wie das alles funktioniert. Ich habe hier einen Beispielcode gefunden und versuche jetzt, es in PyQt5 arbeiten zu lassen.PyQt Signale und Slots: "neuer Stil" emittieren?

Hier die gui-Datei ist ui.py:

from PyQt5 import QtCore, QtWidgets 

class Ui_Win(object): 
    def setupUi(self, MainWindow): 
     MainWindow.setObjectName("MainWindow") 
     MainWindow.resize(416, 292) 
     self.centralWidget = QtWidgets.QWidget(MainWindow) 
     self.centralWidget.setObjectName("centralWidget") 
     MainWindow.setCentralWidget(self.centralWidget) 

if __name__ == "__main__": 
    import sys 
    app = QtWidgets.QApplication(sys.argv) 
    MainWindow = QtWidgets.QMainWindow() 
    ui = Ui_Win() 
    ui.setupUi(MainWindow) 
    MainWindow.show() 
    sys.exit(app.exec_()) 

Und hier ist das Hauptskript test_slotting.py:

from ui import Ui_Win 
from PyQt5 import QtGui, QtCore, QtWidgets 
from PyQt5.QtCore import pyqtSlot 
import time 

class GenericThread(QtCore.QThread): 
    def __init__(self, parent=None): 
     QtCore.QThread.__init__(self, parent) 

    def __del__(self): 
     self.quit() 
     self.wait() 

    def run(self): 
     #Do all your heavy processing here 
     #I'll just wait for 2 seconds 
     time.sleep(2) 
     self.emit(QtCore.pyqtSignal('itemSelectionChanged()')) 
     return 


class MainUI(QtWidgets.QMainWindow): 
    def __init__(self, parent=None): 
     QtWidgets.QMainWindow.__init__(self) 
     self.ui = Ui_Win() 
     self.ui.setupUi(self) 
     self.ui.List1 = QtWidgets.QListWidget(self) 
     self.ui.List2 = QtWidgets.QListWidget(self) 

     hbox = QtWidgets.QHBoxLayout() 
     hbox.addStretch(1) 
     hbox.addWidget(self.ui.List1) 
     hbox.addWidget(self.ui.List2) 

     self.ui.centralWidget.setLayout(hbox) 

     self.ui.List1.addItems(['alpha','beta','gamma','delta','epsilon']) 
     self.ui.List2.addItems(['Item1','Item2']) 

     self.ui.List1.itemSelectionChanged.connect(self.start_heavy_processing_thread) 

    @pyqtSlot() 
    def start_heavy_processing_thread(self): 
     genericThread = GenericThread(self) 
     # self.connect(genericThread, QtCore.SIGNAL("itemSelectionChanged()"), self.fill_List2) 
     genericThread.itemSelectionChanged.connect(self.fill_List2) 
     genericThread.start() 

    def fill_List2(self): 
     self.ui.List2.clear() 
     list1SelectedItem = str(self.ui.List1.currentItem().text()) 
     self.ui.List2.addItem(list1SelectedItem) 

if __name__ == "__main__": 
    import sys 
    app = QtWidgets.QApplication(sys.argv) 
    MainWindow = MainUI() 
    MainWindow.show() 
    sys.exit(app.exec_()) 

Von dem ursprünglichen Code Beispiel hatte ich "alten Stil"

zu ändern
self.connect(genericThread, QtCore.SIGNAL("itemSelectionChanged()"), self.fill_List2) 

zu "neuen Stil"

self.ui.List1.itemSelectionChanged.connect(self.start_heavy_processing_thread) 

Aber jetzt bekomme ich die folgenden AttributeError: 'GenericThread' object has no attribute 'itemSelectionChanged'. Ich denke, diese Linie von test_slotting.py ist immer noch „im alten Stil“:

self.emit(QtCore.pyqtSignal('itemSelectionChanged()')) 

Aber was wäre die neue Stil Version davon sein? Jede Hilfe würde sehr geschätzt ...

Antwort

2

Der Fehler ist durchaus zu erwarten. Ihr GenericThread hat kein Signal mit dem Namen itemSelectionChanged(), daher können Sie kein nicht vorhandenes Signal an einen Steckplatz anschließen. Dieses Signal gehört zu Ihrer QListWidget und nicht zu Ihrer GenericThread.

Ich würde vorschlagen, mehr darüber zu lesen, wie QThread funktioniert, bevor Sie sich entscheiden, Ihre benutzerdefinierte zu erstellen. Vor allem, wenn dieser Thread von Ihnen mit Slots und Signalen arbeitet, wo Sie tatsächlich die "schwere Verarbeitung" machen, werden Sie aufgrund der Natur von QThread geschraubt bekommen - nur was innerhalb run() läuft, läuft tatsächlich in einem separaten Thread. Der Rest (Slots, Signale, Klassenmitglieder etc.) gehört zu dem Thread, von dem du deinen eigenen QThread aus erzeugt hast - in deinem Fall ist das der Hauptthread.

Ich habe ein Beispiel für die Arbeit mit PyQt und Threads here hochgeladen, wo Sie sehen können, wie die Dinge normalerweise getan werden. Ich benutze dort auch den neuen Stil.


Kurze Erklärung über den neuen Stil:

Sagen wir, Sie mit einem QPushButtonQWidget abgeleiteten Klasse in es button und einen Schlitz genannt do_work(), die Sie auslösen soll, wenn die Schaltfläche geklickt wird . Um diese Verbindung herzustellen, werden Sie folgendes tun:

self.button.clicked.connect(self.do_work) 
     |  |    | | 
     |  |    | | 
     | signal of emitter | slot of receiver 
     |      | 
    signal emitter   signal receiver 

In diesem Fall self ist der Empfänger daher verwenden self.do_work wir zu sagen, dass der Schlitz do_work gehört self und es ist, wo das Signal clicked, emittiert von button, muss landen.

PS: Ich habe festgestellt, dass Sie versuchen, Dekorateure zu verwenden, aber das nur teilweise tun.Wenn Sie diese verwenden möchten (dies wird dringend empfohlen), müssen Sie einen Slot-Decorator zu Ihrer fill_List2()-Funktion hinzufügen.

+0

Vielen Dank für Ihre ausführliche Erklärung. Dummer Fehler, indem 'itemSelectionChanged()' auf 'GenericThread' anstelle von' QListWidget' verwendet wird, sry dafür. Ich muss noch einiges über 'QThread' nachlesen, danke für den Tipp. Thx auch für das Codebeispiel, wird auch hv das genauer überprüfen. Ich habe es geschafft, etw zusammenzufügen und mein Beispiel zum Laufen zu bringen. Allerdings gibt es immer noch einige Bits, die ich nicht wirklich verstehe (Kommentare im Code, zögern Sie nicht, wenn Sie möchten :)). Nochmals, Danke für Ihre Hilfe! – dliv

+0

oh und P.S .: Die Abbildung ist wirklich super hilfreich! – dliv

+0

Ich bin froh, dass ich helfen kann. Wie ich in meiner Antwort geschrieben habe - sieh dir das my git-Repository an, in dem ich Threads verwende. Es sollte dir genug geben, um alles besser in den Griff zu bekommen. – rbaleksandar

0

Der aktualisierte Code

ui.py:

from PyQt5 import QtWidgets 


    class Ui_Win(object): 
     def setupUi(self, MainWindow): 
      MainWindow.setObjectName("MainWindow") 
      MainWindow.resize(416, 292) 
      self.centralWidget = QtWidgets.QWidget(MainWindow) 
      self.centralWidget.setObjectName("centralWidget") 
      MainWindow.setCentralWidget(self.centralWidget) 

      self.List1 = QtWidgets.QListWidget() 
      self.List2 = QtWidgets.QListWidget() 

      self.hbox = QtWidgets.QHBoxLayout() 
      self.hbox.addStretch(1) 
      self.hbox.addWidget(self.List1) 
      self.hbox.addWidget(self.List2) 

      self.centralWidget.setLayout(self.hbox) 


    if __name__ == "__main__": 
     import sys 
     app = QtWidgets.QApplication(sys.argv) 
     MainWindow = QtWidgets.QMainWindow() 
     ui = Ui_Win() 
     ui.setupUi(MainWindow) 
     MainWindow.show() 
     sys.exit(app.exec_()) 

test_slotting.py:

from ui import Ui_Win 
from PyQt5 import QtWidgets, QtCore 
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject 

class Trigger(QObject): # --> or QtCore.QThread? What's the difference? 
    trigger = pyqtSignal() 

    def connect_emit(self, pressed_item, list_to_update): 
     self.pressed_item = pressed_item # --> this kind of looks ugly to me... hmmm... 
     self.list_to_update = list_to_update # --> this kind of looks ugly to me... hmmm... 

     self.trigger.connect(self.run) 
     self.trigger.emit() 

    def run(self): 
     self.list_to_update.clear() 
     self.list_to_update.addItem(self.pressed_item) 


class MainUI(QtWidgets.QMainWindow): 
    def __init__(self, parent=None): 
     QtWidgets.QMainWindow.__init__(self) 
     self.ui = Ui_Win() 
     self.ui.setupUi(self) 

     self.ui.List1.addItems(['alpha', 'beta', 'gamma', 'delta', 'epsilon']) 
     self.ui.List2.addItems(['Item1', 'Item2']) 

     self.ui.List1.itemSelectionChanged.connect(self.start_heavy_processing_thread) 

    @pyqtSlot() # --> what does this actually do? code also works without it... 
    def start_heavy_processing_thread(self): 
     genericThread = Trigger() 
     myitem = [str(x.text()) for x in self.ui.List1.selectedItems()][0] 
     mylist = self.ui.List2 
     genericThread.connect_emit(myitem, mylist) 


if __name__ == "__main__": 
    import sys 
    app = QtWidgets.QApplication(sys.argv) 
    MainWindow = MainUI() 
    MainWindow.show() 
    sys.exit(app.exec_())