Как сделать расширяемый по нажатию кнопки виджет?

Ситуация такая:

Имеется QScrollArea c установленным Layout Vertically. Внутри находятся несколько QWidget с установленными значениями minimumSize-Height.
Последним в scrollAreaWidgetContents находится Vertical Spacer, который не дает виджетам расширяться, если их суммарная высота будет меньше высоты самого QScrollArea.

Устройство QScrollArea


Сами виджеты имеют Layout in Grid и представляют собой несколько QLabel и одну QPushButton с установленной icon1:

Устройство QWidget

После нажатия на кнопку в виджете должно появиться еще несколько строк Label, а кнопка должна сменить icon1 на icon2:

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


Как это реализовать? Можно ли это реализовать с какой-то анимацией, а не скачком из состояния 1 в состояние 2?

Или может быть имеется способ сделать это как-то иначе и с умом?

Спасибо, что уделили время!

UPD: Реализовал какими-то костылями. ИМХО. Предположил, что можно создать виджеты заранее в состоянии 2, а потом ненужные QLabels скрыть, через hide(), по нажатию соответствующей кнопки. Обозвал все скрываемые QLabels по шаблону more_info_{}.

Логично было бы делать hide() при создании QLabel в виджете, однако, почему-то в этот момент hide() не меняет размер виджета, в котором он находится, поэтому пришлось написать еще скрипт, который проходится по всем виджетам и их закрывает.

    # Тут привязываем функции к кнопкам
    
    self.pushButton.clicked.connect(self.click_button_down)
    self.pushButton_2.clicked.connect(self.click_button_down)
    self.pushButton_3.clicked.connect(self.click_button_down)
    
    # А тут обновляем чтобы виджеты появились сразу со скрытыми QLabel
    
    self.close_opened_widgets()

Далее функция для открытия:

def click_button_down(self):
    button = self.sender()
    widget = button.parent()
    children = widget.children()
    for child in children:
        if type(child) == QtWidgets.QPushButton:
            icon = QtGui.QIcon()
            icon.addPixmap(QtGui.QPixmap("components/icons/ArrowTop.png"),
                           QtGui.QIcon.Normal, QtGui.QIcon.Off)
            child.setIcon(icon)
            child.setIconSize(QtCore.QSize(50, 50))
        if child.objectName().find('more_info') != -1:
            child.show()

    button.clicked.connect(self.click_button_up)  # Перепривязываем к другой функции, чтобы виджет можно было вернуть в предыдущее состояние
    widget.setMinimumSize(QtCore.QSize(0, 238))

Функция для закрытия:

def click_button_up(self):
    button = self.sender()
    widget = button.parent()
    children = widget.children()
    for child in children:
        if type(child) == QtWidgets.QPushButton:
            icon = QtGui.QIcon()
            icon.addPixmap(QtGui.QPixmap("components/icons/ArrowBottom.png"),
                           QtGui.QIcon.Normal, QtGui.QIcon.Off)
            child.setIcon(icon)
            child.setIconSize(QtCore.QSize(50, 50))
        if child.objectName().find('more_info') != -1:
            child.hide()

    button.clicked.connect(self.click_button_down)
    widget.setMinimumSize(QtCore.QSize(0, 126))

И функция для первоначального обновления:

def close_opened_widgets(self):
    for child_scroll in self.scrollAreaWidgetContents.children():
        if type(child_scroll) == QtWidgets.QFrame:
            child_scroll.setMinimumSize(QtCore.QSize(0, 126))
            for child in child_scroll.children():
                if child.objectName().find('more_info') != -1:
                    child.hide()

Собственно, вроде как-то работает, но я думаю, что можно лучше и проще. Если есть замечания, пишите. Спасибо.

UPD2:
ТО, что я придумал выше, работает отвратительно и после 6-7 нажатий туда-сюда программа замедляется после каждого нажатия, вплоть до полного зависания.


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

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

Код, который вы предоставили, воспроизвести невозможно. Ориентируясь на ваше второе и третье изображения, один из возможных вариантов может выглядеть примерно так:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qt import *


class CustomWidget(QWidget):
    def __init__(self, item, *args, **kwargs):
        super(CustomWidget, self).__init__(*args, **kwargs)
        self.item = item
        self.setMaximumHeight(100)
        
        layout = QtWidgets.QGridLayout(self)
        layout.addWidget(QtWidgets.QLabel('6'), 0, 0)
        layout.addWidget(QtWidgets.QLabel('7'), 0, 1)
        layout.addWidget(QtWidgets.QLabel('8'), 0, 2)
        layout.addWidget(QtWidgets.QLabel('9'), 1, 0)
        layout.addWidget(QtWidgets.QLabel('10'), 1, 1)
        layout.addWidget(QtWidgets.QLabel('11'), 1, 2)


class FirstWidget(QWidget):
    def __init__(self, item, *args, **kwargs):
        super(FirstWidget, self).__init__(*args, **kwargs)
        self.item = item
       
        self.button = QPushButton()
        self.button.setCheckable(True)
        self.button.setFixedSize(90, 70)
        self.button.setIcon(QIcon("head3.png"))
        self.button.setIconSize(QSize(90, 70))

        layout = QtWidgets.QGridLayout(self)
        layout.addWidget(QtWidgets.QLabel('1'), 0, 0)
        layout.addWidget(QtWidgets.QLabel('2'), 0, 1)
        layout.addWidget(self.button, 0, 2)
        layout.addWidget(QtWidgets.QLabel('4'), 1, 0)
        layout.addWidget(QtWidgets.QLabel('5'), 1, 1)
        layout.addWidget(QtWidgets.QLabel('3'), 1, 2)

    def resizeEvent(self, event):
        super(FirstWidget, self).resizeEvent(event)
        self.item.setSizeHint(QSize(self.minimumWidth(), self.height()))
        

class Window(QListWidget):
    def __init__(self, *args, **kwargs):
        super(Window, self).__init__(*args, **kwargs)
        
        item = QListWidgetItem(self)
        self.firstWidget = FirstWidget(item, self)
        self.firstWidget.button.toggled.connect(self.hideChild)
        self.setItemWidget(item, self.firstWidget)

        self.item = QListWidgetItem(self)
        self.customWidget = CustomWidget(self.item, self)
        self.setItemWidget(self.item, self.customWidget)

        self.state = False
        QtCore.QTimer.singleShot(100, lambda: self.hideChild(self.state))

    def hideChild(self, state):
        self.state = state
        if state:
            self.firstWidget.button.setIcon(QIcon("cat.jpg"))
        else:
            self.firstWidget.button.setIcon(QIcon("head3.png"))
        self.item.setHidden(not state)
        
    def resizeEvent(self, event):
        super(Window, self).resizeEvent(event)
        self.hideChild(self.state) 
        self.item.setSizeHint(QSize(self.minimumWidth(), self.height()))        
        

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Window()
    w.resize(310, 200)
    w.show()
    sys.exit(app.exec_())

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

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

→ Ссылка