【问题标题】:access instance variable by name from function从函数中按名称访问实例变量
【发布时间】:2017-02-26 09:37:33
【问题描述】:

我需要为一个从一组变量进行大量计算的类制作 UI。代码大致是这样的:

class MainWindow:
    def __init__(self):
        pulse = Pulse()

        self.var1_lineEdit.textEdited.connect(self.set_attribute(pulse, "var1", new_value))
        self.var2_lineEdit.textEdited.connect(self.set_attribute(pulse, "var2", new_value))
        self.var3_lineEdit.textEdited.connect(self.set_attribute(pulse, "var3", new_value))
        ....

    def set_attribute(pulse, var_name, value):
        # get value from line edit
        # update value in pulse
        # update other calculations

class Pulse:
    def __init__(self):
        var1 = 1
        var2 = 2
        var3 = 3
        ....

我的问题是如何编码 set_attribute。有没有办法通过将名称作为函数参数传递来访问另一个类的各个变量?我想避免为每个变量定义一个新函数。

或者有更简单的方法吗?我对 OOP 和编写 UI 的正确方法不是很熟悉。

【问题讨论】:

  • 您似乎只想要内置的setattr 函数。
  • 如果我正确理解@waterboy5281,那就更复杂了。我正在尝试将多个信号连接到一个插槽,根据信号发射器产生不同的结果

标签: python-3.x pyqt5


【解决方案1】:

编辑

您建议的信号/插槽连接会给您带来麻烦。使用 connect 函数将信号连接到插槽,该函数接受 python 可调用作为其参数 - 而不是函数调用本身。这就是区别

widget.textChanged.connect(self.set_attribute) # right syntax
widget.textChanged.connect(self.set_attribute()) # wrong syntax

那么,你如何告诉set_attribute 做什么呢? ekhumoro 告诉我我的方式错误:使用QSignalMapper。这是一种过时的技术。最好简单地使用lambda 函数(或partials)来获得想要的效果。

我更新了我的演示以使用lambdas

出于本演示的目的,我创建了一个简单的QMainWindow,其中包含两个QLineEdit 小部件和一个QPushButton

# file ui_main.py
from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(359, 249)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.formLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.formLayoutWidget.setGeometry(QtCore.QRect(0, 0, 351, 51))
        self.formLayoutWidget.setObjectName("formLayoutWidget")
        self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
        self.formLayout.setObjectName("formLayout")
        self.configParam1Label = QtWidgets.QLabel(self.formLayoutWidget)
        self.configParam1Label.setObjectName("configParam1Label")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.configParam1Label)
        self.configEntry = QtWidgets.QLineEdit(self.formLayoutWidget)
        self.configEntry.setObjectName("configEntry")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.configEntry)
        self.configParam2Label = QtWidgets.QLabel(self.formLayoutWidget)
        self.configParam2Label.setObjectName("configParam2Label")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.configParam2Label)
        self.configEntry2 = QtWidgets.QLineEdit(self.formLayoutWidget)
        self.configEntry2.setObjectName("configEntry2")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.configEntry2)
        self.pulseButton = QtWidgets.QPushButton(self.centralwidget)
        self.pulseButton.setGeometry(QtCore.QRect(130, 140, 75, 23))
        self.pulseButton.setObjectName("pulseButton")
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.configParam1Label.setText(_translate("MainWindow", "Config Param 1"))
        self.configParam2Label.setText(_translate("MainWindow", "Config Param 2"))
        self.pulseButton.setText(_translate("MainWindow", "GetPulse"))

在另一个文件中,经过一些导入,定义Pulse--一个包含两个属性的简单类

# file main.py
from PyQt5.QtCore import (QSignalMapper, pyqtSlot)
from PyQt5.QtWidgets import (QApplication, QMainWindow, QMessageBox)

from ui_main import Ui_MainWindow

class Pulse:
    def __init__(self):
        self.param_one = None
        self.param_two = None

现在,定义MainWindow,设置它的用户界面并给它一个Pulse

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.pulse = Pulse()

        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.widget_one = self.ui.configEntry
        self.widget_two = self.ui.configEntry2
        self.pulse_button = self.ui.pulseButton

        self.widget_one.textChanged.connect(lambda: self.widget_handler('param_one', self.widget_one.text()))
        self.widget_one.textChanged.connect(lambda: self.widget_handler('param_two', self.widget_two.text()))

    def widget_handler(self, attr, value):
        setattr(self.pulse,attr,value)

    def handler(self, idx):
        widget, attr = self.attribute_widget_list[idx]
        widget_value = widget.text()
        setattr(self.pulse,attr,widget_value)

setattr 等价于例如self.pulse.param_one = widget_value

为了证明它有效,让我们使用QPushButton 来探测Pulse 的状态:

    @pyqtSlot()
    def on_pulseButton_clicked(self):
        message = QMessageBox()
        message.setText("{} - {}".format(self.pulse.param_one, self.pulse.param_two))
        message.exec()

这里我使用了不同的信号/槽连接方案,你可以阅读here

下面是我们运行脚本的方式:

if __name__ == '__main__':
    import sys

    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

希望有帮助!

【讨论】:

  • 感谢连接功能的提醒。同时,我编写了一个函数,该函数在每个编辑行更改时盲目更新每个属性,它可以完成工作,但它很草率。您的解决方案似乎很有趣,一旦我运行了我的第一个版本的 ui,我会试一试.
  • 这太复杂了。标准习惯用法是使用lambdapartial:例如widget.signal.connect(lambda: func(arg1, arg2)).
  • 谢谢,@ekhumoro。我更新了演示以使用 lambdas。我不知道为什么我曾经认为 QSignalMapper 是要走的路。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-28
  • 1970-01-01
  • 1970-01-01
  • 2011-07-25
  • 1970-01-01
  • 2021-04-25
相关资源
最近更新 更多