Qt绘图之QWidget

简介: Qt绘图之QWidget

一、概述

我们知道当一个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) {
  ...
  
  ...
}

目录
相关文章
|
编解码 图形学
29 QT - 绘图设备
29 QT - 绘图设备
41 0
|
3月前
|
API
【Qt 学习笔记】QWidget的toolTip属性 | focusPolicy属性
【Qt 学习笔记】QWidget的toolTip属性 | focusPolicy属性
161 5
|
3月前
|
容器
【qt】GraphicsView绘图架构
【qt】GraphicsView绘图架构
71 0
|
3月前
【qt】绘图
【qt】绘图
33 0
|
1月前
(14)Qt绘图(one)
本文介绍了在Qt中使用QPainter进行绘图的基础操作,包括如何指定绘图设备、使用QPen和QBrush设置线条和填充样式、绘制不同样式的线条和形状,以及如何实现纹理填充和渐变填充等效果。
57 6
(14)Qt绘图(one)
|
1月前
|
计算机视觉
(15)Qt绘图(two)
Qt框架中QPainter类的多种绘图功能,包括坐标变换、基本图形绘制、文本和图片绘制、图像保存以及碰撞检测等。
38 1
(15)Qt绘图(two)
|
3月前
|
API
Qt绘图之Paint系统
Qt绘图之Paint系统
59 2
|
3月前
|
前端开发 搜索推荐 API
【Qt 学习笔记】QWidget的styleSheet属性 | RGB | 在线调色板
【Qt 学习笔记】QWidget的styleSheet属性 | RGB | 在线调色板
203 5
|
3月前
|
算法 API 图形学
【Qt 学习笔记】QWidget的geometry属性及window frame的影响
【Qt 学习笔记】QWidget的geometry属性及window frame的影响
172 2
|
3月前
|
XML 开发框架 API
【Qt 学习笔记】QWidget的windowTitle属性 | windowIcon属性 | qrc文件机制
【Qt 学习笔记】QWidget的windowTitle属性 | windowIcon属性 | qrc文件机制
155 1

推荐镜像

更多