How to make QSplitter respond to double clicking?

Add double-click collapse and restore to QSplitter by creating a custom QSplitterHandle
Heads up! You've already completed this tutorial.

The QSplitter widget in Qt lets you divide a window into resizable panels. Users can drag the handle between panels to adjust their sizes. But what if you want to let users double-click the handle to collapse a panel completely, and then double-click again to restore it? That's a common UX pattern, but QSplitter doesn't support it out of the box.

The QSplitter widget only provides a single signal — splitterMoved — and there's no built-in doubleClicked signal. To add this behavior, you need to customize the handle that sits between the panels. Let's walk through how to do that.

Here's a typical starting point — a simple horizontal splitter with two text editors:

python
from PySide6 import QtWidgets, QtCore

class Window(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)

        # We want something like this, but it doesn't exist:
        # splitter.doubleClicked.connect(self.handler)

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

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

This works fine for basic splitting, but there's no way to respond to a double-click on the splitter handle. Let's fix that.

A first attempt: override mouseDoubleClickEvent on QSplitter

Your first instinct might be to subclass QSplitter and override mouseDoubleClickEvent directly:

python
class ToggleSplitter(QtWidgets.QSplitter):

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

    def mouseDoubleClickEvent(self, e):
        sizes = self.sizes()
        if sizes[0] == 0 and self._previous_state:
            sizes = self._previous_state
        else:
            self._previous_state = sizes[:]
            sizes[0] = 0
        self.setSizes(sizes)

If you try this, you'll notice something frustrating — you can barely trigger the double-click. It only fires if you happen to click on the frame area right next to the handle, not on the handle itself. You have to move your mouse until the cursor turns from a resize arrow back to a normal pointer, and then double-click.

Why? Because QSplitter is a compound widget. The draggable handles between panels are separate objects — instances of QSplitterHandle. Mouse events on the handle go to the handle widget, not to the parent QSplitter.

The proper solution: custom QSplitterHandle

To intercept double-clicks on the handle itself, you need to create a custom QSplitterHandle subclass and override its mouseDoubleClickEvent. Then, you tell your custom QSplitter to use your custom handle by overriding the createHandle method.

QSplitter calls createHandle() internally whenever a new handle is needed (i.e., each time you add a widget). By overriding it, you can swap in your own handle class seamlessly.

Here's how the two classes work together:

python
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)
        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:
            self._previous_state = sizes[:]
            sizes[0] = 0
        self.setSizes(sizes)

The ToggleSplitterHandle class overrides mouseDoubleClickEvent and calls toggle_collapse() on its parent (the splitter). The handle can access the parent splitter through self.parent() since Qt automatically sets up the parent-child relationship.

Packaging Python Applications with PyInstaller by Martin Fitzpatrick — This step-by-step guide walks you through packaging your own Python applications from simple examples to complete installers and signed executables.

Get the book

The ToggleSplitter class does two things:

  1. createHandle() returns a ToggleSplitterHandle instead of the default QSplitterHandle. You pass self.orientation() and self (the parent) to the handle constructor, matching what Qt does internally.

  2. toggle_collapse() contains the collapse/restore logic. It checks whether the left panel is already collapsed (size is 0). If so, and if a previous state was saved, it restores that state. Otherwise, it saves the current sizes and collapses the left panel to zero.

Notice the sizes[:] slice — this creates a copy of the sizes list. Without it, modifying sizes[0] = 0 on the next line would also change self._previous_state, since both variables would point to the same list.

Full working example

Here's everything put together. You can copy, paste, and run this directly. Double-click the handle between the two text editors to collapse the left panel, then double-click again to restore it.

python
from PySide6 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 previous 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[:]  # copy so the change below doesn't affect stored.
            sizes[0] = 0

        self.setSizes(sizes)


class Window(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 = Window()
    w.show()
    app.exec()

When you run this, you'll see two text editors side by side. Double-click the thin handle between them — the left panel collapses to zero width. Double-click again and it pops back to its original size.

Going further

You could extend this approach in several ways:

  • Collapse the right panel instead of the left by changing sizes[0] = 0 to sizes[1] = 0.
  • Emit a custom signal from the handle or splitter when the double-click occurs, so other parts of your application can respond.
  • Animate the collapse using QPropertyAnimation for a smoother visual transition.
  • Support vertical splitters — the same code works without modification since createHandle passes self.orientation() through to the handle.

The createHandle pattern is useful beyond just double-click handling. Any time you need to customize the appearance or behavior of splitter handles — adding hover effects, custom painting, or context menus — subclassing QSplitterHandle and returning it from createHandle is the way to go.

Bring Your PyQt/PySide Application to Market — Specialized launch support for scientific and engineering software built using Python & Qt.

Find out More

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

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

Martin Fitzpatrick is the creator of Python GUIs, and has been developing Python/Qt applications for the past 12+ years. He has written a number of popular Python books and provides Python software development & consulting for teams and startups.