在 Qt (PyQt5) 中创建自定义 signal/slot
Creating a custom signal/slot in Qt (PyQt5)
我是使用 Qt 和一般构建 GUI 的完全初学者,如果这是一个愚蠢的问题但我无法在 Internet 上找到答案,我深表歉意。我希望你能帮我解决几个问题:
首先,我使用 Qt Designer 创建了一个 GUI,并且 运行 遇到了一个关于“连接它”的问题。我有一个带有多种选项的组合框,我想做的是让框中的选项更改一堆行编辑框中的文本。问题是,当组合框的 textActivated()
信号被发送到行编辑的 setText()
槽时,行编辑被组合框中的文本填充。
下面我举一个小例子来说明我的意思。此组合框具有值 A、B 和 C。选择一个选项后,该字母将出现在框中。 我想要做的是让组合框的每个条目都是一个 'key' 到一个值 ,所以如果例如A 被选中,然后行编辑填充 选项 1 或类似的东西。我之所以要这样做是因为组合框会根据输入设置一堆default参数,然后用户可以tweak 值(如果需要)。
Screenshot of Window
我知道负责 signal/slot 连接的 modify/the 线路是
self.comboBox.textActivated['QString'].connect(self.lineEdit.setText)
但我真的不明白当 .connect 是一个实例函数时我将如何传递组合框的值。
有谁知道如何使用自定义 function/slot 来做到这一点?完整代码在 post.
的底部
其次,一个相关的问题,为什么这些signal/slot会进入window的__init__()
函数? app.exec_()
连续运行__init__()
?实例化一个对象然后在应用程序循环中具有 运行 的“更新”功能不是更明智吗?
提前致谢!
完整代码:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'practice.ui'
#
# Created by: PyQt5 UI code generator 5.15.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(493, 124)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.horizontalLayout.addWidget(self.comboBox)
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 493, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.comboBox.textActivated['QString'].connect(self.lineEdit.setText)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.comboBox.setItemText(0, _translate("MainWindow", "A"))
self.comboBox.setItemText(1, _translate("MainWindow", "B"))
self.comboBox.setItemText(2, _translate("MainWindow", "C"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
更新
from PyQt5 import QtCore, QtGui, QtWidgets
#here you make dict in advance.
dic = {'A':'You choose A',
'B':'You choose B',
'C':'You choose C'}
# a list of the dict key. #[*dic] is also ok instead of lis
lis = dic.keys()
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(493, 124)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setObjectName("comboBox")
#addItems is for multiple items.
self.comboBox.addItems(lis)
self.horizontalLayout.addWidget(self.comboBox)
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 493, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.comboBox.activated['QString'].connect(self.new_selection)
#I think it is not bothered with .emit of my first answer.
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
# number and value you can use enumerate func.
for num, key in enumerate(lis):
self.comboBox.setItemText(num, _translate("MainWindow", key))
def new_selection(self):
self.lineEdit.setText(dic[self.comboBox.currentText()])
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv) # Whole application
MainWindow = QtWidgets.QMainWindow() # create the actual window
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show() # Actually show the window
sys.exit(app.exec_())
你在某处口授。
你总是提到它。
我不知道你扩展键值连接多久了,但你现在在任何地方设置相同的键值 directly.It 是冗长的,你也可以尽可能多地从一个对象中获取键。如果你这样做,无论你在字典中添加新的键和值,这些变化都会在不改变其他代码的情况下反映出来。
当你想扩展你的key&value时,你只需要将dic
的数据改写一次即可。
幸运的是,dict 可以保存来自 ...python3.6
的键的顺序
您可以像列表一样使用键。
请附加 'D': 'You choose D' 或其他数据并尝试!
很高兴我的回答是你想要的。
很简单,你能通过这段代码达到你的目的吗?
我从你的问题中了解到的是第一时间在lineedit上设置第一项。
我不知道你到底想设置什么,但是,textActivated() since (PyQt5.14)
(我可以激活它(),因为我的版本是 5.9.x)...
`signal.connect(somthing slot)` means that make a connection between signal & slot.
`signal.emit(object)` means that to emit the connected signal.
因为您已经将 textActivated
(signal) 与 lineEdit.setText
(slot) 连接起来,所以,如果您在对象发出时设置 currentText() ,setText 会捕获 object.If 你将信号连接到三个 lineedit.setText
,所有的 lineedits 通过相同的信息设置文本。
发射前必须先建立连接,默认设置除外
如果我的回答是你想要的,我认为你不需要制作自定义信号和插槽。
Second, a related question, why do these signal/slots go in the init() function of the window? Does app.exec_() continuously run init()? Wouldn't it be more sensible to instantiate an object and then have an "update" function that is run in the app loop?
您可以从 init() 连接 signal/slots。
但是在那个地方建立联系往往是很方便的。
大多数情况下,如果你不想连接,就没有机会disconnect.And,你可以使用blockSignals()
方法阻止连接。
因此,为了证明这一点,您可以尝试在某处编写测试代码以在 __init__
之后建立连接。
例如signal&slot的signal&slot。
app.exec_()
运行一个循环而不是运行__init__()
一遍又一遍。
app.exec_()
正在等待用户输入,eventHandler,例如,keyPressEvent
、mousePressEvent
。
特别是 paintEvent()
用于显示 GUI。
init 只被调用一次。它的变量分配到你计算机的内存地址中。
一遍又一遍地调用init意味着内存被一遍又一遍地重写again.Memory主要是为了避免这种浪费
如果通过keyPressEvent
或mousePressEvent
或信号&插槽机制在使用应用程序后更改变量的内容,就是将分配的内存地址重写为其他内容。
我认为更新是在更改之后调用的。
这是为了根据新变量更新GUI表面信息。
感谢@Haru 的帮助,我明白了:
这是相关代码:
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
...
...
...
self.comboBox.textActivated['QString'].connect(self.new_selection)
def new_selection(self):
if self.comboBox.currentText() == 'A':
self.lineEdit.setText('You chose B')
elif self.comboBox.currentText() == 'B':
self.lineEdit.setText('You chose B')
elif self.comboBox.currentText() == 'C':
self.lineEdit.setText('You chose C')
其中,在选择组合框项目时,一些文本(不是组合框中的选项之一)会显示在行编辑中。上面的代码适用于 window 的初始化,但在进行新选择时无效。
完整代码在这里:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(493, 124)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.horizontalLayout.addWidget(self.comboBox)
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 493, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.comboBox.textActivated['QString'].connect(self.new_selection)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.comboBox.setItemText(0, _translate("MainWindow", "A"))
self.comboBox.setItemText(1, _translate("MainWindow", "B"))
self.comboBox.setItemText(2, _translate("MainWindow", "C"))
def new_selection(self):
if self.comboBox.currentText() == 'A':
self.lineEdit.setText('You chose B')
elif self.comboBox.currentText() == 'B':
self.lineEdit.setText('You chose B')
elif self.comboBox.currentText() == 'C':
self.lineEdit.setText('You chose C')
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv) # Whole application
MainWindow = QtWidgets.QMainWindow() # create the actual window
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show() # Actually show the window
sys.exit(app.exec_())
我是使用 Qt 和一般构建 GUI 的完全初学者,如果这是一个愚蠢的问题但我无法在 Internet 上找到答案,我深表歉意。我希望你能帮我解决几个问题:
首先,我使用 Qt Designer 创建了一个 GUI,并且 运行 遇到了一个关于“连接它”的问题。我有一个带有多种选项的组合框,我想做的是让框中的选项更改一堆行编辑框中的文本。问题是,当组合框的 textActivated()
信号被发送到行编辑的 setText()
槽时,行编辑被组合框中的文本填充。
下面我举一个小例子来说明我的意思。此组合框具有值 A、B 和 C。选择一个选项后,该字母将出现在框中。 我想要做的是让组合框的每个条目都是一个 'key' 到一个值 ,所以如果例如A 被选中,然后行编辑填充 选项 1 或类似的东西。我之所以要这样做是因为组合框会根据输入设置一堆default参数,然后用户可以tweak 值(如果需要)。
Screenshot of Window
我知道负责 signal/slot 连接的 modify/the 线路是
self.comboBox.textActivated['QString'].connect(self.lineEdit.setText)
但我真的不明白当 .connect 是一个实例函数时我将如何传递组合框的值。
有谁知道如何使用自定义 function/slot 来做到这一点?完整代码在 post.
的底部其次,一个相关的问题,为什么这些signal/slot会进入window的__init__()
函数? app.exec_()
连续运行__init__()
?实例化一个对象然后在应用程序循环中具有 运行 的“更新”功能不是更明智吗?
提前致谢!
完整代码:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'practice.ui'
#
# Created by: PyQt5 UI code generator 5.15.0
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(493, 124)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.horizontalLayout.addWidget(self.comboBox)
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 493, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.comboBox.textActivated['QString'].connect(self.lineEdit.setText)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.comboBox.setItemText(0, _translate("MainWindow", "A"))
self.comboBox.setItemText(1, _translate("MainWindow", "B"))
self.comboBox.setItemText(2, _translate("MainWindow", "C"))
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
更新
from PyQt5 import QtCore, QtGui, QtWidgets
#here you make dict in advance.
dic = {'A':'You choose A',
'B':'You choose B',
'C':'You choose C'}
# a list of the dict key. #[*dic] is also ok instead of lis
lis = dic.keys()
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(493, 124)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setObjectName("comboBox")
#addItems is for multiple items.
self.comboBox.addItems(lis)
self.horizontalLayout.addWidget(self.comboBox)
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 493, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.comboBox.activated['QString'].connect(self.new_selection)
#I think it is not bothered with .emit of my first answer.
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
# number and value you can use enumerate func.
for num, key in enumerate(lis):
self.comboBox.setItemText(num, _translate("MainWindow", key))
def new_selection(self):
self.lineEdit.setText(dic[self.comboBox.currentText()])
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv) # Whole application
MainWindow = QtWidgets.QMainWindow() # create the actual window
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show() # Actually show the window
sys.exit(app.exec_())
你在某处口授。
你总是提到它。
我不知道你扩展键值连接多久了,但你现在在任何地方设置相同的键值 directly.It 是冗长的,你也可以尽可能多地从一个对象中获取键。如果你这样做,无论你在字典中添加新的键和值,这些变化都会在不改变其他代码的情况下反映出来。
当你想扩展你的key&value时,你只需要将dic
的数据改写一次即可。
幸运的是,dict 可以保存来自 ...python3.6
的键的顺序
您可以像列表一样使用键。
请附加 'D': 'You choose D' 或其他数据并尝试!
很高兴我的回答是你想要的。
很简单,你能通过这段代码达到你的目的吗?
我从你的问题中了解到的是第一时间在lineedit上设置第一项。
我不知道你到底想设置什么,但是,textActivated() since (PyQt5.14) (我可以激活它(),因为我的版本是 5.9.x)...
`signal.connect(somthing slot)` means that make a connection between signal & slot.
`signal.emit(object)` means that to emit the connected signal.
因为您已经将 textActivated
(signal) 与 lineEdit.setText
(slot) 连接起来,所以,如果您在对象发出时设置 currentText() ,setText 会捕获 object.If 你将信号连接到三个 lineedit.setText
,所有的 lineedits 通过相同的信息设置文本。
发射前必须先建立连接,默认设置除外
如果我的回答是你想要的,我认为你不需要制作自定义信号和插槽。
Second, a related question, why do these signal/slots go in the init() function of the window? Does app.exec_() continuously run init()? Wouldn't it be more sensible to instantiate an object and then have an "update" function that is run in the app loop?
您可以从 init() 连接 signal/slots。
但是在那个地方建立联系往往是很方便的。
大多数情况下,如果你不想连接,就没有机会disconnect.And,你可以使用blockSignals()
方法阻止连接。
因此,为了证明这一点,您可以尝试在某处编写测试代码以在 __init__
之后建立连接。
例如signal&slot的signal&slot。
app.exec_()
运行一个循环而不是运行__init__()
一遍又一遍。
app.exec_()
正在等待用户输入,eventHandler,例如,keyPressEvent
、mousePressEvent
。
特别是 paintEvent()
用于显示 GUI。
init 只被调用一次。它的变量分配到你计算机的内存地址中。
一遍又一遍地调用init意味着内存被一遍又一遍地重写again.Memory主要是为了避免这种浪费
如果通过keyPressEvent
或mousePressEvent
或信号&插槽机制在使用应用程序后更改变量的内容,就是将分配的内存地址重写为其他内容。
我认为更新是在更改之后调用的。 这是为了根据新变量更新GUI表面信息。
感谢@Haru 的帮助,我明白了:
这是相关代码:
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
...
...
...
self.comboBox.textActivated['QString'].connect(self.new_selection)
def new_selection(self):
if self.comboBox.currentText() == 'A':
self.lineEdit.setText('You chose B')
elif self.comboBox.currentText() == 'B':
self.lineEdit.setText('You chose B')
elif self.comboBox.currentText() == 'C':
self.lineEdit.setText('You chose C')
其中,在选择组合框项目时,一些文本(不是组合框中的选项之一)会显示在行编辑中。上面的代码适用于 window 的初始化,但在进行新选择时无效。
完整代码在这里:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(493, 124)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setObjectName("comboBox")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.comboBox.addItem("")
self.horizontalLayout.addWidget(self.comboBox)
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 493, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
self.comboBox.textActivated['QString'].connect(self.new_selection)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.comboBox.setItemText(0, _translate("MainWindow", "A"))
self.comboBox.setItemText(1, _translate("MainWindow", "B"))
self.comboBox.setItemText(2, _translate("MainWindow", "C"))
def new_selection(self):
if self.comboBox.currentText() == 'A':
self.lineEdit.setText('You chose B')
elif self.comboBox.currentText() == 'B':
self.lineEdit.setText('You chose B')
elif self.comboBox.currentText() == 'C':
self.lineEdit.setText('You chose C')
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv) # Whole application
MainWindow = QtWidgets.QMainWindow() # create the actual window
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show() # Actually show the window
sys.exit(app.exec_())