How to make QSplitter responds to double clicking?

Heads up! You've already completed this tutorial.

Scoodood | 2020-09-25 05:16:36 UTC | #1

The default QSplitter Widget only has splitterMoved signal. I would like to add a doubleClicked signal so that I can make the QSplitter widget can move all the way to the left on a double clicking event, and then double clicking it again will restore to its previous position. Below are my starter code. Any idea how to go from here?

python
from PySide2 import QtWidgets, QtCore

class Example(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()      
        splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal)
        textedit1 = QtWidgets.QTextEdit()
        textedit2 = QtWidgets.QTextEdit()
        splitter.addWidget(textedit1)
        splitter.addWidget(textedit2)
        splitter.setSizes([200,200])
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(splitter)
        self.setLayout(layout)        
        splitter.splitterMoved.connect(self.handler)

        # trying to do something like this...
        #splitter.doubleClicked.connect(self.handler)

    def handler(self, pos):
        print('pos', pos)

if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    w = Example()
    w.show()
    app.exec_()

martin | 2020-09-30 20:45:37 UTC | #2

Hey @Scoodood I knocked together a simple custom widget for this -- it implements a double click event handler which stores and restores a slide position.

python

class ToggleSplitter(QtWidgets.QSplitter):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Store the previos size of the left hand panel.
        self._previous_state = None

    def mouseDoubleClickEvent(self, e):
        sizes = self.sizes()
        if sizes[0] == 0 and self._previous_state:
            sizes = self._previous_state
        else:
            # Store the size so we can return to it.
            self._previous_state = sizes[:] # store copy so change below doesn't affect stored.
            sizes[0] = 0

        self.setSizes(sizes)

If you run it you'll notice the slight problem -- you can't actually double click on the splitter itself, just on the (tiny space) in the frame next to it. Move your mouse around the edge of the splitter until it turns into an arrow, then double click.

The problem is that QSplitter is a compound widget with the handles themselves QSplitterHandle objects. However, QSplitter has a createHandle method we can override allowing us to return our own custom handles -- and we can put our double-click handler on there instead.

Full working example ...

Create GUI Applications with Python & Qt5 by Martin Fitzpatrick — (PyQt5 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!

More info Get the book

python
from PySide2 import QtWidgets, QtCore



class ToggleSplitterHandle(QtWidgets.QSplitterHandle):

    def mouseDoubleClickEvent(self, e):
        self.parent().toggle_collapse()


class ToggleSplitter(QtWidgets.QSplitter):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # Store the previos size of the left hand panel.
        self._previous_state = None

    def createHandle(self):
        return ToggleSplitterHandle(self.orientation(), self)

    def toggle_collapse(self):
        sizes = self.sizes()
        if sizes[0] == 0 and self._previous_state:
            sizes = self._previous_state
        else:
            # Store the size so we can return to it.
            self._previous_state = sizes[:] # store copy so change below doesn't affect stored.
            sizes[0] = 0

        self.setSizes(sizes)



class Example(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()      
        splitter = ToggleSplitter(QtCore.Qt.Horizontal)
        textedit1 = QtWidgets.QTextEdit()
        textedit2 = QtWidgets.QTextEdit()
        splitter.addWidget(textedit1)
        splitter.addWidget(textedit2)
        splitter.setSizes([200,200])
        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(splitter)
        self.setLayout(layout)        
        splitter.splitterMoved.connect(self.handler)

    def handler(self, pos):
        print('pos', pos)

if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    w = Example()
    w.show()
    app.exec_()

Scoodood | 2020-09-30 20:59:09 UTC | #3

Thanks @martin. This is awesome!!


Create GUI Applications with Python & Qt6 by Martin Fitzpatrick — (PyQt6 Edition) The hands-on guide to making apps with Python — Over 10,000 copies sold!

More info Get the book

Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

How to make QSplitter responds to double clicking? was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.