PyQt6. Как получить доступ к конкретному виджету в списке сформированным циклом?

Создал в Qt Designer MainWindow, в котором располагается QScrollArea.

Также имеется отдельный виджет, который помещается циклом внутри QScrollArea.
На виджете расположены label и pushButton.

Задача следующая: при нажатии на кнопку, текст лейбла в том же виджете должен измениться.

В моем случае изменяется только текст последнего виджета в списке не зависимо от того какая кнопка была нажата.

Вот код главного окна: main_window.py

Вот код виджета: widget.py

Вот скрипт запускающий программу:

from PyQt6.QtWidgets import QApplication
from PyQt6 import QtWidgets
from mw import Ui_MainWindow
from fr import Ui_Form

import sys


class Form(QtWidgets.QWidget, Ui_Form):
    def __init__(self):
        super().__init__()
        self.setupUi(self)


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.vbox = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)


class App(MainWindow, Form):
    def __init__(self):
        super().__init__()
        self.stack_widget()
    
    # здесь виджеты стакаются в scrollarea
    def stack_widget(self):
        for i in range(10):
            self.form = Form()
            self.form.pushButton.clicked.connect(lambda: self.change_label(self.form.pushButton.text()))
            self.vbox.addWidget(self.form)

    # здесь изменяется текст в виджете при нажатии на кнопку
    def change_label(self, button):
        self.form.label.setText(button)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = App()
    window.show()
    sys.exit(app.exec())

На ум приходят всевозможные словари, хранящие данные типа:

{<PyQt6.QtWidgets.QPushButton object at 0x000001465DF945E0>: <__main__.Form object at 0x000001465DF94430>}

или enumerate(), но все это кажется очевидно неправильными методами требующими кучу проверок.

Мне ясна суть того почему изменения происходят именно в последнем виджете, но как наиболее простым методом получить доступ к нужному виджету я никак не соображу. Помогите. Спасибо.


Ответы (1 шт):

Автор решения: S. Nick

Вам незачем в цикле при создании экземпляра класса Form делать его атрибутом класса App.

Правильное использование функции lambda выглядит так:

        form.pushButton.clicked.connect(
            lambda ch, form=form, i=i: self.change_label(form, i))

а слот так:

def change_label(self, form, i):
    form.label.setText(f'{form.pushButton.text()} {i}')

void QAbstractButton::clicked(bool checked = false)


main.py

import sys
#from PyQt6 import QtWidgets
from PyQt5 import QtWidgets
from mw import Ui_MainWindow
from fr import Ui_Form


class Form(QtWidgets.QWidget, Ui_Form):
    def __init__(self):
        super().__init__()
        self.setupUi(self)


class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.vbox = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)


class App(MainWindow, Form):
    def __init__(self):
        super().__init__()
        self.stack_widget()
    
    # здесь виджеты стакаются в scrollarea
    def stack_widget(self):
        for i in range(10):
#            self.form = Form()
            form = Form()
            
            form.pushButton.clicked.connect(
                lambda ch, form=form, i=i: self.change_label(form, i))
                
            self.vbox.addWidget(form)

    # здесь изменяется текст в виджете при нажатии на кнопку
    def change_label(self, form, i):
        form.label.setText(f'{form.pushButton.text()} {i}')


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    window = App()
    window.show()
    sys.exit(app.exec())

mw.py

#from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt5 import QtCore, QtGui, QtWidgets 

 
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(412, 265)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName("scrollArea")
        self.scrollAreaWidgetContents = QtWidgets.QWidget()
        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 392, 245))
        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
        self.verticalLayout.addWidget(self.scrollArea)
        MainWindow.setCentralWidget(self.centralwidget)
 
        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
 
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))

fr.py

#from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt5 import QtCore, QtGui, QtWidgets 
 
class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(400, 41)
        self.horizontalLayout = QtWidgets.QHBoxLayout(Form)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label = QtWidgets.QLabel(Form)
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        self.pushButton = QtWidgets.QPushButton(Form)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
 
        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)
 
    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.label.setText(_translate("Form", "TextLabel"))
        self.pushButton.setText(_translate("Form", "PushButton"))

введите сюда описание изображения

→ Ссылка