5

I have a class in Qt that inherits QDockWidget. And that class contains another widget. Is there any possibility to define a function in my QDockWidget inherited class that draws stuff on top of the contained widget? Like the painting to be independent of the contained widget but to be linked to the inherited class.

Thank you

3 Answers3

10

Sure it's possible. It is fairly simple to do, in fact. You need to place a child widget that sits on top of everything else in your QDockWidget. To do it so, it must be the last child widget you add to your dockwidget. That widget must not to draw its background, and it can then draw over any children of the dockwidget. The widget's size must track the size of the parent widget.

Below is a self-contained example.

Screenshot of the example

// https://github.com/KubaO/stackoverflown/tree/master/questions/overlay-line-11034838
#include <QtGui>
#if QT_VERSION > QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif

class Line : public QWidget {
protected:
   void paintEvent(QPaintEvent *) override {
        QPainter p(this);
        p.setRenderHint(QPainter::Antialiasing);
        p.drawLine(rect().topLeft(), rect().bottomRight());
    }
public:
    explicit Line(QWidget *parent = nullptr) : QWidget(parent) {
       setAttribute(Qt::WA_TransparentForMouseEvents);
    }
};

class Window : public QWidget {
    QHBoxLayout layout{this};
    QPushButton left{"Left"};
    QLabel right{"Right"};
    Line line{this};
protected:
    void resizeEvent(QResizeEvent *) override {
        line.resize(size());
    }
public:
    explicit Window(QWidget *parent = nullptr) : QWidget(parent) {
        layout.addWidget(&left);
        right.setFrameStyle(QFrame::Box | QFrame::Raised);
        layout.addWidget(&right);
        line.raise();
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    Window w;
    w.show();
    return app.exec();
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
0

AFAIK: No.

Widgets are drawn in depth order, so whatever your QDockWidget derived class paints, will be drawn over by the contained widgets when they are updated (immediately afterwards no doubt, because paint updates are propagated to child widgets).

cmannett85
  • 21,725
  • 8
  • 76
  • 119
  • That drawing in depth order is precisely what allows you to draw over underlying widgets using extra widgets placed on top of them. It's simple, even :) Props go to new widget rendering model introduced in IIRC Qt 4.1. – Kuba hasn't forgotten Monica Jun 15 '12 at 03:33
  • @KubaOber You're absolutely right, but that is not what the OP asked. The OP asked if a container widget can paint over child widgets - the answer is no. Your solution is a possible workaround, but as it doesn't involve a `QDockWidget` derived class painting over contained widgets, it doesn't answer the OP's question. – cmannett85 Jun 15 '12 at 06:52
  • 1
    It does offer a solution. The container widget is free to add a child widget to itself. Sure the painting code does not go into container's `paintEvent`, but who cares about that. – Kuba hasn't forgotten Monica Jun 15 '12 at 13:47
0

Python version for the accepted answer:

# Created by BaiJiFeiLong@gmail.com at 2022/1/15 10:22

from PySide2 import QtWidgets, QtGui, QtCore

app = QtWidgets.QApplication()
widget = QtWidgets.QWidget()

line = QtWidgets.QFrame(widget)
line.paintEvent = lambda _: QtGui.QPainter(line).drawLine(line.rect().topLeft(), line.rect().bottomRight())
line.setAttribute(QtCore.Qt.WidgetAttribute.WA_TransparentForMouseEvents)

widget.setLayout(QtWidgets.QGridLayout(widget))
widget.layout().addWidget(QtWidgets.QPushButton("CLICK ME", widget))
widget.resizeEvent = lambda event: line.resize(event.size())

line.raise_()
widget.show()
app.exec_()

Note that this will not work for QSplitter, in this condition, you should use QMainWindow as parent widget.

BaiJiFeiLong
  • 3,716
  • 1
  • 30
  • 28