Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a standard component for collapsible panel in Qt?

I'm trying to create a collapsible side panel similar to the one in Qt Designer (See attached screenshot). They appear to be QListViews placed in some sort of collapsible widget.

enter image description here

I googled for "Qt collapsible panel" and it seems that there is no standard component for this, or at least not under that name. So, does anyone know whether any standard Qt component has the same behavior? If not, can anyone suggests how I can go about building it?

Thanks.

like image 392
lightalchemist Avatar asked Jun 18 '12 06:06

lightalchemist


People also ask

How do you hide the spacer in Qt?

Try putting the button you want to hide and unhide in another layout. In that layout along with the button put a spacer. Call Button hide and spacer will take over. Spacer takes over hidden button's space.

What are Qt widgets?

Widgets are the primary elements for creating user interfaces in Qt. Widgets can display data and status information, receive user input, and provide a container for other widgets that should be grouped together. A widget that is not embedded in a parent widget is called a window.

How do I resize a layout in Qt?

Once you have add your layout with at least one widget in it, select your window and click the "Update" button of QtDesigner. The interface will be resized at the most optimized size and your layout will fit the whole window. Then when resizing the window, the layout will be resized in the same way.


2 Answers

I decided to follow the general approach laid out in the link provided by Joey.

Specifically, I created a widget for each collapsible list. This widget consists of a QPushButton at the top and a QListView at the bottom.

Then, I wired the button clicked signal to a handler to toggle the geometry of the QListView between having height of 0 when it is hidden and its original height when it reappears.

I find that this approach is much simpler compared to customizing the paint event as suggested by Claudio. Furthermore, I can use QAnimationProperty to animate the change in geometry to make the list appear to "slide" in and out of view.

But anyway thanks for the replies!

like image 122
lightalchemist Avatar answered Oct 12 '22 13:10

lightalchemist


I created the collapsible panel. Here's the Code:

import os
import sys 
from pathlib import Path
from PySide2.QtWidgets import QPushButton, QVBoxLayout, QApplication ,QListWidget, QListView, QLabel, QWidget, QAbstractItemView, QSplitter 
from PySide2.QtGui import QStandardItemModel, QStandardItem, QIcon, Qt, QDrag
from PySide2 import QtCore

PATH = os.path.join('.','img_src')
print('LayerSelector Loaded with path:' , PATH)

class CustomQStandardItem(QStandardItem):
    def __init__(self, icon, text):
        super().__init__(icon, text)
    
    def dropMimeData(self, data, action, row, column, parent):
        pass
        
class LayersList(QWidget):
    '''
    LayerList class which acts as collapsable list.
    '''
    def __init__(self, name, layers, expand = True):
        super().__init__()
        self.currently_expanded = True
        self.main_layout = QVBoxLayout()
        self.main_layout.setMargin(0)
        self.main_layout.setSpacing(0)
        self.main_layout.setContentsMargins(0, 0, 0, 0)
        self.expand_button = QPushButton(name)
        self.expand_button.setToolTip(f"List of {name} Layers")
        self.expand_button.setIcon(QIcon(os.path.join(PATH,'LayersList_Up.png')))
        self.layer_list = QListView()
        self.layer_list.setDragEnabled(True)
        self.container_model = QStandardItemModel()
        for l in layers:
            qs = CustomQStandardItem(QIcon(os.path.join(PATH,'LayersList_Layer_Icon.png')),l)
            self.container_model.appendRow(qs)
        self.layer_list.setModel(self.container_model)
        self.layer_list.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.main_layout.addWidget(self.expand_button,0, Qt.AlignTop)
        self.main_layout.addWidget(self.layer_list, 0, Qt.AlignTop)
        self.expand_button.clicked.connect(self.expand)
        self.setLayout(self.main_layout)
        self.resized_size = 16.5 * len(layers)
        self.set_styling()
        if not expand:
            self.expand()
            
    @QtCore.Slot()
    def expand(self):
        if self.currently_expanded:
            self.layer_list.setMaximumHeight(0)
            self.expand_button.setIcon(QIcon(os.path.join(PATH,'LayersList_Down.png')))
            self.currently_expanded = False
        else:
            self.layer_list.setMaximumHeight(self.resized_size)
            self.expand_button.setIcon(QIcon(os.path.join(PATH,'LayersList_Up.png')))
            self.currently_expanded = True
            
    def set_styling(self):
        self.setStyleSheet("background-color:aliceblue ;")
        self.expand_button.setStyleSheet("text-align:left;")
        self.layer_list.setStyleSheet("background-color:aliceblue; border:1px solid;border-style: ridge;")

class LayersSelectorWidget(QWidget):
    '''
    LayerChoiceWidget class provide widget plugin for picking layers.
    '''
    def __init__(self):
        super().__init__()
        names = ['Core Layers',
                 'Convolution Layers',
                 'Pooling Layers',
                 'Recurrent Layers',
                 'Preprocessing Layers',
                 'Attention Layers',
                 'Reshaping Layers',
                 'Locally-Connected Layers'
                 ]
        layers = [
            ['Dense', 'Activation', 'Embedding', 'Masking', 'Lambda'],
            ['Conv1D', 'Conv2D', 'Conv3D', 'SeparableConv1D', 'SeparableConv2D', 'DepthwiseConv2D', 'Conv2DTranspose', 'Conv3Dtranspose'],
            ['MaxPooling1D', 'MaxPooling2D', 'MaxPooling3D', 'AveragePooling1D', 'AveragePooling2D', 'AveragePooling3D', 'GlobalMAxPooling1D', 'GlobalMAxPooling2D', 'GlobalMAxPooling3D', 'GlobalAveragePooling1D', 'GlobalAveragePooling2D', 'GlobalAveragePooling3D'],
            ['LSTM', 'GRU', 'SimpleRNN', 'TimeDistributed', 'BiDirectional', 'ConvLSTM2D'],
            ['TextToVector', 'Normalization'],
            ['Attention', 'AdditiveAttention'],
            ['Reshape', 'Flatten', 'Cropping1D', 'Cropping2D', 'Cropping3D', 'UpSampling1D', 'UpSampling2D', 'UpSampling3D', 'ZeroPadding1D', 'ZeroPadding2D', 'ZeroPadding3D'],
            ['LocallyConnected1D', 'LocallyConnected2D']
        ]
        self.main_layout = QVBoxLayout()
        for i in range(len(names)):
            if i > 3:
                self.main_layout.addWidget(LayersList(names[i], layers[i], False))
            else:
                self.main_layout.addWidget(LayersList(names[i], layers[i]))                
        self.main_layout.setSpacing(0)
        self.main_layout.setAlignment(Qt.AlignTop)        
        self.main_layout.setMargin(0)
        self.main_layout.setSpacing(0)
        self.main_layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self.main_layout)
        del(names)
        del(layers)
        self.set_styling()
        
    def set_styling(self):
        self.setStyleSheet("background-color:aliceblue; border:0px solid;border-style: solid;")

Below is the result of above Code :

Result

You can also play with set_styling to change look and feel.

like image 36
Deep Raval Avatar answered Oct 12 '22 11:10

Deep Raval