在 Qt (PyQt5) 中创建自定义 signal/slot

Creating a custom signal/slot in Qt (PyQt5)

我是使用 Qt 和一般构建 GUI 的完全初学者,如果这是一个愚蠢的问题但我无法在 Internet 上找到答案,我深表歉意。我希望你能帮我解决几个问题:


首先,我使用 Qt Designer 创建了一个 GUI,并且 运行 遇到了一个关于“连接它”的问题。我有一个带有多种选项的组合框,我想做的是让框中的选项更改一堆行编辑框中的文本。问题是,当组合框的 textActivated() 信号被发送到行编辑的 setText() 槽时,行编辑被组合框中的文本填充。

下面我举一个小例子来说明我的意思。此组合框具有值 ABC。选择一个选项后,该字母将出现在框中。 我想要做的是让组合框的每个条目都是一个 '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,例如,keyPressEventmousePressEvent。 特别是 paintEvent() 用于显示 GUI。 init 只被调用一次。它的变量分配到你计算机的内存地址中。

一遍又一遍地调用init意味着内存被一遍又一遍地重写again.Memory主要是为了避免这种浪费

如果通过keyPressEventmousePressEvent或信号&插槽机制在使用应用程序后更改变量的内容,就是将分配的内存地址重写为其他内容。

我认为更新是在更改之后调用的。 这是为了根据新变量更新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_())