pythonqtpyqtpyqt5qtoolbutton

Button is not clickable when window is small


I'm creating an expandable/collapsible widget because Qt doesn't provide such feature until now. My problem is, that the button to expand/collapse the widget seems to be only clickable, if you make the window very large (only the width of the window is relevant). The elements of the the main widget are working as intended. Does anyone know the reason for it?

import sys
from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QLabel, QMainWindow, QVBoxLayout, QHBoxLayout, QToolButton
from PyQt5.QtCore import Qt


class ExpandableWidget(QWidget):
    '''A widget that can be expanded / collapsed by the user'''

    def __init__(self):
        super(ExpandableWidget, self).__init__()
        
        self.titleWidget = QWidget() # widget containing the title bar (always visible)
        self.mainWidget = QWidget()  # widget containing the collapsible content (sometimes visible, but this isn't implemented yet)

        self.expandButton = QToolButton()
        self.expandButton.setCheckable(True)
        self.expandButton.setChecked(False)
        self.expandButton.setArrowType(Qt.LeftArrow)

        self.titleLayout = QHBoxLayout()
        self.titleLayout.addWidget(self.titleWidget)
        self.titleLayout.addWidget(self.expandButton)

        self.wrapperLayout = QVBoxLayout()
        self.wrapperLayout.addLayout(self.titleLayout)
        self.wrapperLayout.addWidget(self.mainWidget)

        self.setLayout(self.wrapperLayout)

    def setMainWidget(self, widget):
        self.wrapperLayout.replaceWidget(self.mainWidget, widget)
        self.mainWidget = widget

    def setTitleWidget(self, widget):
        self.titleLayout.replaceWidget(self.titleWidget, widget)
        self.titleWidget = widget


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWindow = QMainWindow()

    expandable = ExpandableWidget()
    mainWindow.setCentralWidget(expandable)

    mainWidget = QPushButton("Button")
    titleLabel = QLabel("Title Label")

    expandable.setTitleWidget(titleLabel)
    expandable.setMainWidget(mainWidget)

    mainWindow.show()
    sys.exit(app.exec_())

GIF demonstrating the effect


Solution

  • That one widget replaces another in a layout does not imply that the second will be removed or hidden but that the widget will not be handled by the layout. So in your case that old widget is on the button causing the click to not be transmitted to the new button.

    In your case a possible solution is to remove the default widget.

    def setMainWidget(self, widget):
        self.wrapperLayout.replaceWidget(self.mainWidget, widget)
        if self.mainWidget.parent() is self:
            self.mainWidget.deleteLater()
        else:
            self.mainWidget.setParent(None)
        self.mainWidget = widget
    
    def setTitleWidget(self, widget):
        self.titleLayout.replaceWidget(self.titleWidget, widget)
        if self.titleWidget.parent() is self:
            self.titleWidget.deleteLater()
        else:
            self.titleWidget.setParent(None)
        self.titleWidget = widget