在PySide中区分信号源

5 投票
3 回答
2616 浏览
提问于 2025-04-17 12:20

有没有简单或者优雅的方法来区分在PySide/PyQt中多个相同类型的信号源?

我正在学习PySide。我写了一个简单的应用程序,它可以将两个不同的QLineEdit()对象中的数字相乘,结果显示在第三个QLineEdit中。

乘数和被乘数的QLineEdit.textChanged()信号都连接到一个方法(TxtChanged)。在这个方法中,我需要区分信号的来源。经过几次尝试,我找到了一种基于占位符文本的解决方法(在我代码中“还有其他方法吗?”评论下方的4行)。

代码:

import sys
from PySide import QtGui, QtCore

class myGUI(QtGui.QWidget):

    def __init__(self, *args, **kwargs):
        QtGui.QWidget.__init__(self, *args, **kwargs)

        self.multiplier = 0
        self.multiplicand = 0

        self.myGUIInit()

    def myGUIInit(self):
        # input forms
        a1_label = QtGui.QLabel("a1")
        a1_edit = QtGui.QLineEdit()
        a1_edit.setPlaceholderText("a1")

        a2_label = QtGui.QLabel("a2")
        a2_edit = QtGui.QLineEdit()
        a2_edit.setPlaceholderText("a2")

        # output form
        a1a2_label = QtGui.QLabel("a1*a2")
        self.a1a2_edit = QtGui.QLineEdit()
        self.a1a2_edit.setReadOnly(True)


        # forms events
        a1_edit.textChanged.connect(self.TxtChanged)
        a2_edit.textChanged.connect(self.TxtChanged)

        # grid
        grid = QtGui.QGridLayout()
        grid.setSpacing(10)

        grid.addWidget(a1_label,1,0)
        grid.addWidget(a1_edit,1,1)

        grid.addWidget(a2_label,2,0)
        grid.addWidget(a2_edit,2,1)

        grid.addWidget(a1a2_label,3,0)
        grid.addWidget(self.a1a2_edit,3,1)

        self.setLayout(grid)
        self.setGeometry(100,100,200,200)
        self.setWindowTitle("a*b")
        self.show()

    def TxtChanged(self,text):
        sender = self.sender()
        sender_text = sender.text()
        if sender_text == '': sender_text = '0'

        # is there another way?
        if sender.placeholderText() == 'a1':
            self.multiplicand = sender_text
        else:
            self.multiplier = sender_text

        product = int(self.multiplier) * int(self.multiplicand)

        print(self.multiplier,self.multiplicand,product)

        self.a1a2_edit.setText(str(product))


def main():
    app = QtGui.QApplication(sys.argv)
    mainWindow = myGUI()
    sys.exit(app.exec_())

main()

最好的祝福,

ostrzysz

3 个回答

2

虽然@jsbueno@Avaris已经回答了你关于信号源的直接问题,但在你的具体情况下,我不建议依赖这些信号源。你可以把实例成员a1_edita2_edit定义出来:

...
self.a1_edit = QtGui.QLineEdit()
...
self.a2_edit = QtGui.QLineEdit()
...

这样可以简化你的TxtChanged函数:

def TxtChanged(self,text):
    try:
        multiplier = int(self.a1_edit.text())
        multiplicand = int(self.a2_edit.text())
    except ValueError:
        self.a1a2_edit.setText('Enter two numbers')
        return
    product = multiplier * multiplicand
    print(multiplier, multiplicand, product)
    self.a1a2_edit.setText(str(product))

另外,与你处理ValueError异常相比,你可以使用QIntValidator来处理输入控制:

self.int_validator = QtGui.QIntValidator()
self.a1_edit.setValidator(self.int_validator)
self.a2_edit.setValidator(self.int_validator)
6

你可以使用 functools.partial 这个函数,这样就能把你的信号直接连接到你的方法或函数,而不是连接到一个 Python 对象,这个对象会自动用你传入的一些额外数据来调用你的函数:

from functools import partial
...
        ....
        a1_edit.textChanged.connect(partial(self.TxtChanged, a1_edit))
        a2_edit.textChanged.connect(partial(self.TxtChanged, a2_edit))
        ...

    def TxtChanged(self,sender, text):
        # and here you have the "sender" parameter as it was filled in the call to "partial"
        ...

partial 是标准库的一部分,使用起来很简单明了,不过你也可以用 lambda 来达到同样的效果 -

a1_edit.textChanged.connect(lambda text: self.TxtChanged(a1_edit, text))

这样,lambda 表达式产生的对象就会是一个临时函数,它会使用当前局部变量中的 "self" 和 "a1_edit" 的值(在按钮被点击的时候),而名为 "text" 的变量则会由 Pyside 的回调提供。

4

你代码中让我最困惑的一点是,你用 placeholderText 来区分不同的对象。其实,QObject 还有一个叫 objectName 的属性,更适合你这个任务。而且,你不需要用 sender.text() 来获取 QLineEdit 的文本,因为 textChanged 事件已经把文本发送过来了,所以你可以直接在 text 参数中找到它。

另外,使用字典来代替两个单独的变量(multipliermultiplicand)会让你的代码更简洁。

下面是修改后的代码:

class myGUI(QtGui.QWidget):

    def __init__(self, *args, **kwargs):
        QtGui.QWidget.__init__(self, *args, **kwargs)

        self.data = {"multiplier": 0,
                     "multiplicand": 0}

        self.myGUIInit()

    def myGUIInit(self):
        a1_label = QtGui.QLabel("a1")
        a1_edit = QtGui.QLineEdit()
        a1_edit.setObjectName("multiplicand")

        a2_label = QtGui.QLabel("a2")
        a2_edit = QtGui.QLineEdit()
        a2_edit.setObjectName("multiplier")

        # skipped the rest because same

    def TxtChanged(self, text):
        sender = self.sender()

        # casting to int while assigning seems logical.
        self.data[sender.objectName()] = int(text)

        product = self.data["multiplier"] * self.data["multiplicand"]

        print(self.data["multiplier"], self.data["multiplicand"], product)

        self.a1a2_edit.setText(str(product))

撰写回答