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 ...

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!!


Over 10,000 developers have bought Create GUI Applications with Python & Qt!

To support developers in [[ countryRegion ]] I give a [[ localizedDiscount[couponCode] ]]% discount with the code [[ couponCode ]] — Enjoy!

For [[ activeDiscount.description ]] I'm giving a [[ activeDiscount.discount ]]% discount with the code [[ couponCode ]] — Enjoy!

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