Search code examples
qtqt4pyqtpyqt4qsplitter

pyqt qt4 How to add a tiny arrow/collapse button to QSplitter


You can customize the thickness and background image of the splitter handle

http://qt-project.org/doc/qt-4.8/stylesheet-examples.html#customizing-qsplitter

But is there a way to add a small graphical arrow button so when the user clicks on it, I can call splitter.setSizes([0, 1]) to collapse it.

collapsebutton

I could use a vbox layout with a QButton but it takes up too much space and doesn't look as nice.


Solution

  • You could subclass QSplitter and reimplement its createHandle method. This would allow you to return an instance of your own QSplitterHandle class, with, say, a reimplemented paintEvent.

    Alternatively, and more simply, you could add buttons directly to an existing splitter handle, by setting your own layout on it.

    Here's a basic demo to get you started (probably needs some tweaking to make it prettier):

    PyQt5:

    from PyQt5 import QtCore, QtWidgets
    
    class Window(QtWidgets.QWidget):
        def __init__(self):
            super().__init__()
            self.splitter = QtWidgets.QSplitter(self)
            self.splitter.addWidget(QtWidgets.QTextEdit(self))
            self.splitter.addWidget(QtWidgets.QTextEdit(self))
            layout = QtWidgets.QVBoxLayout(self)
            layout.addWidget(self.splitter)
            handle = self.splitter.handle(1)
            layout = QtWidgets.QVBoxLayout()
            layout.setContentsMargins(0, 0, 0, 0)
            button = QtWidgets.QToolButton(handle)
            button.setArrowType(QtCore.Qt.LeftArrow)
            button.clicked.connect(
                lambda: self.handleSplitterButton(True))
            layout.addWidget(button)
            button = QtWidgets.QToolButton(handle)
            button.setArrowType(QtCore.Qt.RightArrow)
            button.clicked.connect(
                lambda: self.handleSplitterButton(False))
            layout.addWidget(button)
            handle.setLayout(layout)
    
        def handleSplitterButton(self, left=True):
            if not all(self.splitter.sizes()):
                self.splitter.setSizes([1, 1])
            elif left:
                self.splitter.setSizes([0, 1])
            else:
                self.splitter.setSizes([1, 0])
    
    if __name__ == '__main__':
    
        import sys
        app = QtWidgets.QApplication(sys.argv)
        window = Window()
        window.setGeometry(500, 300, 300, 300)
        window.show()
        sys.exit(app.exec_())
    

    PyQt4:

    from PyQt4 import QtCore, QtGui
    
    class Window(QtGui.QWidget):
        def __init__(self):
            QtGui.QWidget.__init__(self)
            self.splitter = QtGui.QSplitter(self)
            self.splitter.addWidget(QtGui.QTextEdit(self))
            self.splitter.addWidget(QtGui.QTextEdit(self))
            layout = QtGui.QVBoxLayout(self)
            layout.addWidget(self.splitter)
            handle = self.splitter.handle(1)
            layout = QtGui.QVBoxLayout()
            layout.setContentsMargins(0, 0, 0, 0)
            button = QtGui.QToolButton(handle)
            button.setArrowType(QtCore.Qt.LeftArrow)
            button.clicked.connect(
                lambda: self.handleSplitterButton(True))
            layout.addWidget(button)
            button = QtGui.QToolButton(handle)
            button.setArrowType(QtCore.Qt.RightArrow)
            button.clicked.connect(
                lambda: self.handleSplitterButton(False))
            layout.addWidget(button)
            handle.setLayout(layout)
    
        def handleSplitterButton(self, left=True):
            if not all(self.splitter.sizes()):
                self.splitter.setSizes([1, 1])
            elif left:
                self.splitter.setSizes([0, 1])
            else:
                self.splitter.setSizes([1, 0])
    
    if __name__ == '__main__':
    
        import sys
        app = QtGui.QApplication(sys.argv)
        window = Window()
        window.setGeometry(500, 300, 300, 300)
        window.show()
        sys.exit(app.exec_())