Python 如何在单击按钮后将小部件动态添加到布局中
无论何时单击+按钮,我都希望添加一个与第一行相同的新行(lineEdit旁边的一个新+按钮),并在相应的lineEdit中打印文本 我设法这样做,但我不明白程序如何知道我点击哪个按钮来打印旁边的文本Python 如何在单击按钮后将小部件动态添加到布局中,python,pyqt5,Python,Pyqt5,无论何时单击+按钮,我都希望添加一个与第一行相同的新行(lineEdit旁边的一个新+按钮),并在相应的lineEdit中打印文本 我设法这样做,但我不明白程序如何知道我点击哪个按钮来打印旁边的文本 from PyQt5 import QtCore, QtGui, QtWidgets count = 1 class Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form")
from PyQt5 import QtCore, QtGui, QtWidgets
count = 1
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(569, 176)
self.formLayout = QtWidgets.QFormLayout(Form)
self.formLayout.setObjectName("formLayout")
self.lineEdit_1 = QtWidgets.QLineEdit(Form)
self.lineEdit_1.setObjectName("lineEdit_1")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.lineEdit_1)
self.pushButton_1 = QtWidgets.QPushButton(Form)
self.pushButton_1.setObjectName("pushButton_1")
self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.pushButton_1)
self.pushButton_print = QtWidgets.QPushButton(Form)
self.pushButton_print.setObjectName("pushButton_print")
self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.pushButton_print)
self.pushButton_1.clicked.connect(self.add)
self.pushButton_print.clicked.connect(self.appear)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.pushButton_1.setText(_translate("Form", "+"))
self.pushButton_print.setText(_translate("Form", "print 4th"))
def add(self):
global count
count+=1
new_btn = QtWidgets.QPushButton("+", Form)
new_line = QtWidgets.QLineEdit(Form)
#new_btn.setObjectName(f"pushButton_{count}")
#new_line.setObjectName(f"lineEdit_{count}")
self.formLayout.setWidget(count, QtWidgets.QFormLayout.LabelRole, new_btn)
self.formLayout.setWidget(count, QtWidgets.QFormLayout.FieldRole, new_line)
new_btn.clicked.connect(lambda: print(new_line.text()))
new_btn.clicked.connect(self.add)
def appear(self):
lineEdit = Form.findChild(QtWidgets.QLineEdit, f"lineEdit_4")
print(lineEdit.text())
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())
另外,当我将新按钮绑定到“self”时,如“self.new_按钮”,它将只打印最新的lineEdit,而不打印较早的lineEdit
为什么会这样?有没有更好的方法来完成我想做的事情?你把局部变量的范围和实例属性混淆了
lambda函数始终使用创建它的函数的本地作用域。在您的示例中,
new\u line
是在创建时在函数app
中创建的引用。结果是它将始终引用该行编辑
如果创建实例属性(如self.new\u line
),则在对象树上解析引用;换句话说:查找“self
”的“new_line”属性(这是Ui_形式
实例,是任何实例方法的函数的第一个参数)。由于每次创建新行时总是覆盖该属性,因此它将始终引用最新的行编辑
我准备了一个小例子,可能会说明这两个方面的区别。文本字段将显示单击的结果:实际单击的行(在添加
功能范围内为行
)和为该行设置的实例属性,该属性在每次创建新行时被覆盖
from PyQt5 import QtWidgets
class ScopeTest(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.row = 0
layout = QtWidgets.QVBoxLayout(self)
self.logView = QtWidgets.QPlainTextEdit(readOnly=True, minimumHeight=200)
layout.addWidget(self.logView)
self.addButton = QtWidgets.QPushButton('Add row')
layout.addWidget(self.addButton)
layout.addWidget(QtWidgets.QFrame(frameShape=QtWidgets.QFrame.HLine))
self.addButton.clicked.connect(self.add)
def add(self):
self.row += 1
# note that the following line is important!
# "row" is a *local* variable! "self.row" is an *instance attribute*!
row = self.row
button = QtWidgets.QPushButton('Row {}'.format(self.row))
self.layout().addWidget(button)
button.clicked.connect(lambda: self.log(row, self.row))
def log(self, clickedRow, scopeRow):
self.logView.appendPlainText('Clicked: {}, Scope: {}'.format(
clickedRow, scopeRow))
self.logView.verticalScrollBar().setValue(
self.logView.verticalScrollBar().maximum())
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
test = ScopeTest()
test.show()
sys.exit(app.exec_())
一些进一步的建议:
- 研究类和实例,以及它们的方法和属性是如何工作的李>
- 搜索
和lambda
之间的差异:它们的行为相似(因为它们都返回对函数的引用),但对参数的求值不同;有些情况下,你可能需要一个或另一个李>functool.partial
- 无论何时动态创建对象,为它们创建实例属性几乎毫无意义,因为这些属性将始终引用上次创建的对象;除非您确实需要知道并引用最后一个对象,否则不应创建(或覆盖)此类属性李>
生成的文件不应被修改(也不应试图模仿它们的行为);这被认为是一种不好的做法有很多原因,因为它们不在主题中,所以我给你留下一个“除非你真的知道你在做什么,否则不要那样做”(这通常会导致:“如果你知道你在做什么,就不要编辑它们”);阅读更多关于这些文件正确使用的官方指南;幸运的是,最新的PyQt版本增加了一个更详细的警告,这个警告永远不应该被低估李>pyuic
(和findChild
)应仅用于Qt内部创建的小部件(例如QCalendarWidget的导航按钮);使用pyuic文件(或findChildren
module函数)自动为所有小部件生成属性;如果您在Designer中有一个名为uic
的小部件,并且您是从lineEdit_4
文件或使用pyuic创建GUI的,那么您已经可以使用.ui
访问该小部件了李>self.lineEdit_4
self.row
每次都被覆盖。您说过“在您的示例中,new_line是在函数应用程序中创建时创建的引用。结果是它将始终引用该行编辑。”。我们创建了一个新的lineEdit,并将其命名为variablenew\u line
,这让我很困惑,尽管lineEdit都有相同的变量名,但程序如何区分它们。另外,假设我们有一个按钮,如果第四个按钮存在,单击时会显示结果。我们如何引用第四个?@Sherafati 1。每次调用app
,都会创建一个新的new\u line
变量,该变量仅对该调用有效,并引用该“范围”中编辑的行。事实上,你总是使用相同的名字是有影响的;我建议你做一些关于“可变范围”的研究。2.我不明白你的意思,你能澄清一下吗?@Sherafati然后在\uuuu init\uuuuu
中创建一个空列表,并在每次创建小部件时将其添加到该列表中,然后如果你想访问第四个,只需使用该索引,例如:第四个按钮=self.widgetList[3]
@Sherafati如前所述,findChild只能在pyqt在内部创建对象时使用,使用它是非常不鼓励的,也是毫无意义的:只需创建一个