'Qt allow dock widget to overlay

How could I allow a dock widget to be over other widgets? I would like to get a result like the tool bar of a IDE such as VSC or PyCharm. The bar can be resized as much as you want, even if that implies shadowing other widgets.



Solution 1:[1]

I have handcrafted my custom widget for that:

It has some pitfalls:

Initially, it is a mess to set up the overlay widget properly. I have just chosen to hide it. The initial width of a window is not properly set until you show it, so I have had to show the window in the init method.

The resize event of the window is also hard to do so again I have chosen collapsing the widget.

from PySide6.QtCore import Qt
from PySide6.QtWidgets import *


class OverlayWidget(QWidget):
    def __init__(self, window):
        QWidget.__init__(self, window)
        self.window = window

        self.collapse_threshold = 0.8
        self.down = 1

        self.label = QLabel("This is an overlay widget")
        self.label.setStyleSheet("background-color: rgba(100, 150, 100, 0.5)")

        self.size_grip = QLabel("...")
        self.size_grip.setStyleSheet("background-color: rgba(255, 0, 0, 0.5);")
        self.size_grip.setAlignment(Qt.AlignHCenter)
        self.size_grip.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)

        self.central_layout = QVBoxLayout()
        self.central_layout.setContentsMargins(0, 0, 0, 0)
        self.central_layout.addWidget(self.size_grip)
        self.central_layout.addWidget(self.label)

        self.setLayout(self.central_layout)


class Window(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle("Overlay proof of concept")
        self.central_widget = QLabel(
            "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n "
            "Sed euismod, urna eu tempor congue, nisi nisl aliquet "
            "nunc, euismod egestas nisl nisl euismod nunc.\n "
            "Sed euismod, urna eu tempor congue, nisi nisl aliquet "
            "nunc, euismod egestas nisl nisl euismod nunc. \n"
            "Sed euismod, urna eu tempor congue, nisi nisl aliquet "
            "nunc, euismod egestas nisl nisl euismod nunc. "
        )
        self.setCentralWidget(self.central_widget)

        self.error_widget = OverlayWidget(self)

        self.resize(800, 600)
        self.show()

        # Initially hide the widget
        self.error_widget.label.setVisible(False)
        self.error_widget.setGeometry(
            0,
            600 - self.error_widget.size_grip.height(),
            800,
            self.error_widget.size_grip.height(),
        )

    def mouseMoveEvent(self, event):
        if self.error_widget.size_grip.underMouse():
            self.update_error_widget(event.position().y())

    def update_error_widget(self, y):
        self.error_widget.down = y / self.height()
        self.error_widget.size_grip.setMaximumWidth(self.width())
        self.error_widget.size_grip.setMinimumWidth(self.width())

        if y < self.error_widget.size_grip.height():
            # Cursor over the window
            pass

        elif (1 - self.error_widget.down) < (1 - self.error_widget.collapse_threshold) / 2:
            # Hide the widget
            self.error_widget.label.setVisible(False)
            self.error_widget.setGeometry(
                0,
                self.height() - self.error_widget.size_grip.height(),
                self.width(),
                self.error_widget.size_grip.height(),
            )

        elif (1 - self.error_widget.down) > (1 - self.error_widget.collapse_threshold):
            # Default behavior
            self.error_widget.label.setVisible(True)
            self.error_widget.setGeometry(
                0,
                self.error_widget.down * self.height() - self.error_widget.size_grip.height(),
                self.width(),
                self.height() - self.error_widget.down * self.height(),
            )

        else:
            # Block the widget
            self.error_widget.label.setVisible(True)
            self.error_widget.setGeometry(
                0,
                self.error_widget.collapse_threshold * self.height() - self.error_widget.size_grip.height(),
                self.width(),
                self.height() - self.error_widget.collapse_threshold * self.height(),
            )

    def resizeEvent(self, event):
        # Hacky easy solution: hide the widget
        self.error_widget.label.setVisible(False)
        self.error_widget.setGeometry(
            0,
            self.height() - self.error_widget.size_grip.height(),
            self.width(),
            self.error_widget.size_grip.height(),
        )
        self.error_widget.size_grip.setMaximumWidth(self.width())
        self.error_widget.size_grip.setMinimumWidth(self.width())


app = QApplication()
window = Window()
app.exec()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jaime02