一、概述
我们知道当一个QWidget以另外一个QWidget作为父对象时,显示父对象会自动显示子对象,今天就对其实现过程做个简单分析。
二、测试源码
subwidget.h
#ifndef SUBWIDGET_H #define SUBWIDGET_H #include <QWidget> #include <QPainter> #include <QResizeEvent> #include <QMoveEvent> #include <QDebug> class SubWidget : public QWidget { Q_OBJECT public: explicit SubWidget(QWidget *parent = nullptr); void paintEvent(QPaintEvent* event); QSize sizeHint() const { return QSize(400, 400); } void moveEvent(QMoveEvent *event) { qDebug() << "moveEvent, oldPos:" << event->oldPos() << ", pos:" << event->pos(); } void resizeEvent(QResizeEvent *event) { qDebug() << "resizeEvent, oldSize:" << event->oldSize() << ", size:" << event->size(); } signals: public slots: }; #endif // SUBWIDGET_H
subwidget.cpp
#include "subwidget.h" SubWidget::SubWidget(QWidget *parent) : QWidget(parent) { } void SubWidget::paintEvent(QPaintEvent* event) { QPainter painter(this); QPen pen; pen.setColor(Qt::blue); painter.setPen(pen); painter.drawText(10, 100, "This is subWidget"); }
widget.h
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QPainter> #include <QSize> class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = 0); ~Widget(); void paintEvent(QPaintEvent* event); QSize sizeHint() const { return QSize(400, 400); } }; #endif // WIDGET_H
widget.cpp
#include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { } Widget::~Widget() { } void Widget::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.drawText(50, 50, "This is widget"); }
main.cpp
#include "widget.h" #include "subwidget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; SubWidget subWidget(&w); w.show(); return a.exec(); }
三、分析父对象绘制同时其对象也会绘制情景
SubWidget 是Widget 的子对象,Widget 绘制的时候会同时绘制SubWidget ,以下是其调用堆栈:
从上图中可以看出,25 QWindowsContext::windowsProc会收到QtWindows::ExposeEvent事件,然后其启动对窗口进行绘制。绘制过程通过绘制事件进行驱动。QWidgetPrivate::drawWidget会绘制QWidget(最终调用paintEvent),如果QWidget有子对象,则其会通过QWidgetPrivate::drawWidget中的QWidgetPrivate::paintSiblingsRecursive嵌套调用子对象的QWidgetPrivate::drawWidget,这样就完成了各个子对象的绘制与显示。
QWidgetPrivate::drawWidget
void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags, QPainter *sharedPainter, QWidgetBackingStore *backingStore) { ... if (recursive && !children.isEmpty()) { paintSiblingsRecursive(pdev, children, children.size() - 1, rgn, offset, flag & ~DrawAsRoot, sharePainter, backingStore); } }
QWidgetPrivate::paintSiblingsRecursive
void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectList& siblings, int index, const QRegion &rgn, const QPoint &offset, int flags, QPainter *sharedPainter, QWidgetBackingStore *backingStore) { ... // 如果还有兄弟对象,则调用paintSiblingsRecursive处理兄弟对象,也就是说先加入父对象的子对象 // 会先进行绘制。 如果没有兄弟对象了或者调用返回了,则调用wd->drawWidget绘制子对象。 if (index > 0) { paintSiblingsRecursive(pdev, siblings, --index, wr, offset, flag, sharePainter, backingStore); } ... // 绘制子对象,,wd是QWidgetPrivate类型的对象。 wd->drawWidget(pdev, wRegion, offset + widgetPos, flags, sharePainter, backingStore); ... }
四、分析父对象设置layout影响子对象布局的情景
修改main.cpp
#include "widget.h" #include "subwidget.h" #include <QApplication> #include <QHBoxLayout> int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; QHBoxLayout layout(&w); SubWidget sw1(&w); SubWidget sw2(&w); layout.addWidget(&sw1); layout.addWidget(&sw2); w.show(); return a.exec(); }
调试
第一次layout
w.show() ==> QWidget::setVisible ==> QLayout::activate> ==>QLayoutPrivate::doResize ==> QBoxLayout::setGeometry ==> QWidgetItem::setGeometry ==> QWidget::setGeometry
第二次根据父对象空间调整子对象的layout
w.show() ==> QWidget::setVisible ==> QWidgetPrivate::show_helper> ==>
QWidgetPrivate::sendPendingMoveAndResizeEvents ==> QCoreApplication::sendEvent ==>
QApplicationPrivate::notify_helper ==> QLayout::widgetEvent ==> QLayoutPrivate::doResize ==> QBoxLayout::setGeometry ==> QWidgetItem::setGeometry ==> QWidget::setGeometry
void QWidget::setVisible(bool visible) { ... // QWidget上有layout则会调用此,进行第一次布局计算 if (d->layout) d->layout->activate(); ... if (isWindow() || parentWidget()->isVisible()) { d->show_helper(); ... } ... }
void QWidgetPrivate::show_helper() { ... sendPendingMoveAndResizeEvents(); ... }
void QWidgetPrivate::sendPendingMoveAndResizeEvents(bool recursive, bool disableUpdates) { ... // QWidget 有移动QWidget窗口位置事件,则发送事件 if (q->testAttribute(Qt::WA_PendingMoveEvent)) { QMoveEvent e(data.crect.topLeft(), data.crect.topLeft()); QApplication::sendEvent(q, &e); q->setAttribute(Qt::WA_PendingMoveEvent, false); } // QWidget 有调整QWidget窗口大小事件,则发送事件 if (q->testAttribute(Qt::WA_PendingResizeEvent)) { QResizeEvent e(data.crect.topLeft(), QSize()); QApplication::sendEvent(q, &e); q->setAttribute(Qt::WA_PendingResizeEvent, false); } ... // 调整子QWidget窗口的位置和大小 for (int i = 0; i < children.size(); ++i) { if (QWidget *child = qobject_cast<QWidget *>(children.at(i))) child->d_func()->sendPendingMoveAndResizeEvents(recursive, disableUpdates); } }
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent *e) { ... // QWidget 上有layout则把事件发送到layout中进行处理 if (QLayout *layout = widget->d_func()->layout) { layout->widgetEvent(e); } ... }
void QLayout::widgetEvent(QEvent *e) { ... switch (e->type()) { case QEvent::Resize: if (d->activated) { QResizeEvent *r = (QResizeEvent *)e; d->doResize(r->size()); } else { activate(); } break; case QEvent::ChildRemoved: ... break; case QEvent::LayoutRequest: if (static_cast<QWidget *>(parent())->isVisible()) activate(); break; default: break; } }
void QBoxLayout::setGeometry(const QRect &r) { ... ... }