2017-02-24 60 views
1

我想圍繞如何正確使用線程和信號與PyQt5Python3,但不知何故設法瞭解這一切如何工作。我在這裏找到了一個示例代碼,現在正試圖使它在PyQt5中工作。PyQt信號和插槽:「新風格」散發?

這裏是GUI文件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_()) 

這裏是主腳本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_()) 

從最初的代碼示例中,我不得不改變 「舊式」

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

以「新風格」

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

但是,現在我得到以下AttributeError: 'GenericThread' object has no attribute 'itemSelectionChanged'。我想這條線從test_slotting.py仍然是「舊式」:

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

但是,什麼是它的新的風格的版本?任何幫助將不勝感激...

回答

2

錯誤是相當期望。您的GenericThread沒有名爲itemSelectionChanged()的信號,因此您無法將不存在的信號連接到插槽。這個信號屬於您的QListWidget,而不是您的GenericThread

我建議您在決定創建自定義之前詳細瞭解QThread的工作原理。特別是如果您的線程適用於實際執行「重處理」的插槽和信號,由於QThread的性質,您將被擰緊 - 只有run()內部的內容實際上在單獨的線程內運行。其餘的(插槽,信號,班員等)屬於你自己產生的線索QThread - 在你的情況下,這是主線程。

我已經上傳了一個使用PyQt和線程here的例子,在這裏你可以看到事情是如何完成的。我也在那裏使用新的風格。


關於新風格簡短解釋:

假設你有一個QPushButton一個QWidget派生類在它所謂button,並且要觸發每當按鈕被點擊插槽do_work() 。爲了建立這種連接,你必須做到以下幾點:

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

在這種情況下self是接收器,因此我們使用self.do_work說,插槽do_work屬於self和它的信號clicked其中,通過發射button,必須登陸。 PS:我已經注意到,你嘗試使用裝飾器,但只做部分。如果你想使用這些(我強烈推薦這麼做),你需要在你的fill_List2()函數中添加一個插槽裝飾器。

+0

感謝您詳盡的解釋。爲此,使用'GenericThread'上的'itemSelectionChanged()'而不是'QListWidget'來犯愚蠢的錯誤。我仍然需要閱讀'QThread',thx的提示。 Thx也爲代碼示例,也將hv更詳細地檢查。我設法修補一下,讓我的例子運行。然而,仍然有一些我不是很瞭解(代碼中的評論,如果你喜歡,請隨時提供幫助:))。再次,thx爲您的幫助! – dliv

+0

哦和P.S .:插圖真的非常有幫助! – dliv

+0

很高興我能幫到你。正如我在我的答案中寫的 - 請看看我使用線程的git存儲庫。它應該足以讓你更好地掌握所有這些。 – rbaleksandar

0

更新的代碼

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_())