PyQt 信号和插槽:"new style" 发射?
PyQt Signals and Slots: "new style" emit?
我正在努力思考如何通过 PyQt5
和 Python3
正确使用线程和信号,但不知何故无法理解这一切是如何工作的。我在这里找到了一个示例代码,现在正试图让它在 PyQt5
.
中运行
这是 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_())
从最初的代码示例开始,我不得不更改 "old style"
self.connect(genericThread, QtCore.SIGNAL("itemSelectionChanged()"), self.fill_List2 )
到"new style"
self.ui.List1.itemSelectionChanged.connect(self.start_heavy_processing_thread)
但是,现在我得到以下 AttributeError: 'GenericThread' object has no attribute 'itemSelectionChanged'
。我猜 test_slotting.py
中的这一行仍然是 "old style":
self.emit(QtCore.pyqtSignal('itemSelectionChanged()'))
但是它的新款式会是什么呢?任何帮助将不胜感激...
这个错误是意料之中的。您的 GenericThread
没有名为 itemSelectionChanged()
的信号,因此您无法将不存在的信号连接到插槽。此信号属于您的 QListWidget
,而不属于您的 GenericThread
。
我建议您在决定创建自定义的之前阅读更多有关 QThread
工作原理的信息。特别是如果你的那个线程与你实际执行 "heavy processing" 的插槽和信号一起工作,你会因为 QThread
的性质而被搞砸 - 只有 run()
里面的东西实际上在一个单独的内部运行线。其余的(插槽、信号、class 成员等)属于您从中生成自己的 QThread
的线程 - 在您的情况下,这是主线程。
我上传了一个使用 PyQt 和线程的示例 here,您可以在其中了解通常是如何完成的。我也在那里使用新样式。
关于新样式的简短说明:
假设您有一个 QWidget
派生的 class,其中有一个名为 button
的 QPushButton
和一个您想要触发的插槽 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()
函数中添加一个插槽装饰器。
更新后的代码
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_())
我正在努力思考如何通过 PyQt5
和 Python3
正确使用线程和信号,但不知何故无法理解这一切是如何工作的。我在这里找到了一个示例代码,现在正试图让它在 PyQt5
.
这是 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_())
从最初的代码示例开始,我不得不更改 "old style"
self.connect(genericThread, QtCore.SIGNAL("itemSelectionChanged()"), self.fill_List2 )
到"new style"
self.ui.List1.itemSelectionChanged.connect(self.start_heavy_processing_thread)
但是,现在我得到以下 AttributeError: 'GenericThread' object has no attribute 'itemSelectionChanged'
。我猜 test_slotting.py
中的这一行仍然是 "old style":
self.emit(QtCore.pyqtSignal('itemSelectionChanged()'))
但是它的新款式会是什么呢?任何帮助将不胜感激...
这个错误是意料之中的。您的 GenericThread
没有名为 itemSelectionChanged()
的信号,因此您无法将不存在的信号连接到插槽。此信号属于您的 QListWidget
,而不属于您的 GenericThread
。
我建议您在决定创建自定义的之前阅读更多有关 QThread
工作原理的信息。特别是如果你的那个线程与你实际执行 "heavy processing" 的插槽和信号一起工作,你会因为 QThread
的性质而被搞砸 - 只有 run()
里面的东西实际上在一个单独的内部运行线。其余的(插槽、信号、class 成员等)属于您从中生成自己的 QThread
的线程 - 在您的情况下,这是主线程。
我上传了一个使用 PyQt 和线程的示例 here,您可以在其中了解通常是如何完成的。我也在那里使用新样式。
关于新样式的简短说明:
假设您有一个 QWidget
派生的 class,其中有一个名为 button
的 QPushButton
和一个您想要触发的插槽 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()
函数中添加一个插槽装饰器。
更新后的代码
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_())