QWindow的绘制

QWindow时钟

简介

在 Qt 中,绘画机制主要包括以下几个核心组件:

QPainter:画家,用于在 QWidget、QImage、QPixmap 等绘制设备上绘制图形。QPainter 提供了许多绘图函数,如 drawLine()、drawRect()、drawEllipse() 等,用于在绘制设备上绘制各种图形元素。
QPaintDevice:绘制设备,可以用画板做比喻,如QWidget、QImage、QPixmap,QWindow 等绘制设备
QPaintEngine:绘制引擎,可以用画笔做比喻。

简单示例

#include <QApplication>
#include <QWidget>
#include <QPainter>

class MyWidget : public QWidget {
public:
    MyWidget(QWidget *parent = nullptr) : QWidget(parent) {}

protected:
	//绘画事件,所有界面的绘制一般在该事件中
    void paintEvent(QPaintEvent *event) override {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        QPen pen(Qt::blue, 2);
        painter.setPen(pen);

        QBrush brush(Qt::green, Qt::SolidPattern);
        painter.setBrush(brush);

        painter.drawRect(10, 10, 100, 100);
        painter.drawEllipse(120, 10, 100, 100);
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);

    MyWidget widget;
    widget.show();

    return app.exec();
}

在上面的示例中可以看到 QPainter painter(this) 创建了一个画家,并以MyWidget为绘图设备,并没有看到绘制引擎,原因是绘图设备有默认的绘图引擎,所以不用额外设置。

QWindow的绘制

QWindow并没有paintEvent事件,但是Qt提供了QBackingStore允许使用QPainter在QWindow上进行绘制。

main.cpp

这里提供一个类AnalogClockWindow ,继承于RasterWindow,RasterWindow继承于QWindow。主要作用是开了定时器,每秒刷新时钟的绘制,render函数是主要绘制动作。

#include <QtGui>

#include "rasterwindow.h"

//! [5]
class AnalogClockWindow : public RasterWindow
{
public:
    AnalogClockWindow();

protected:
    void timerEvent(QTimerEvent *) override;
    void render(QPainter *p) override;

private:
    int m_timerId;
};
//! [5]


//! [6]
AnalogClockWindow::AnalogClockWindow()
{
    setTitle("Analog Clock");
    resize(200, 200);
	//开启一个每秒触发一次的定时器
    m_timerId = startTimer(1000);
}
//! [6]

//! [7]
//定时器事件,用来重新绘制
void AnalogClockWindow::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == m_timerId)
        renderLater();	//调用子类的函数,做一些预处理
}
//! [7]

//! [1] //! [14]
//绘制时钟的函数
void AnalogClockWindow::render(QPainter *p)
{
//! [14]
//! [8]
    static const QPoint hourHand[3] = {
        QPoint(7, 8),
        QPoint(-7, 8),
        QPoint(0, -40)
    };

    static const QPoint minuteHand[3] = {
        QPoint(7, 8),
        QPoint(-7, 8),
        QPoint(0, -70)
    };
	//设置绘制秒针和分针的颜色
    QColor hourColor(127, 0, 127);
    QColor minuteColor(0, 127, 127, 191);
//! [8]

//! [9]开启抗锯齿
    p->setRenderHint(QPainter::Antialiasing);
//! [9] //! [10]把绘制的坐标挪到窗口的中心,绘制坐标默认在左上角
    p->translate(width() / 2, height() / 2);
	//下面两句代码是缩放坐标用的,目的是为了让画出来的时钟适应窗口大小,至于为啥除以200,因为窗口初始大小就是200*200
    int side = qMin(width(), height());
    p->scale(side / 200.0, side / 200.0);
//! [1] //! [10]

//! [11]
	//设置画笔和画刷
    p->setPen(Qt::NoPen);
    p->setBrush(hourColor);
//! [11]
//!
//! [2]
	//获取当前时间
    QTime time = QTime::currentTime();
	//QPainter为了画完时针后回到原来的绘制坐标,用save保存当前位置状态,然后restore
    p->save();
    p->rotate(30.0 * ((time.hour() + time.minute() / 60.0)));
    //这里用3个点绘制不规则形状,一个小时需要旋转30度,两分钟旋转1度
    p->drawConvexPolygon(hourHand, 3);
    p->restore();
//! [2]

//! [12]
    p->setPen(hourColor);
	//这里把小时的刻度线画出来
    for (int i = 0; i < 12; ++i) {
        p->drawLine(0, 80, 0, 96);
        p->rotate(30.0);
    }
//! [12] //! [13]
    p->setPen(Qt::NoPen);
    p->setBrush(minuteColor);
//! [13]

//! [3]绘制分针
    p->save();
    p->rotate(6.0 * (time.minute() + time.second() / 60.0));
    p->drawConvexPolygon(minuteHand, 3);
    p->restore();
//! [3]

//! [4]
    p->setPen(minuteColor);
	//绘制分针的刻度线
    for (int j = 0; j < 60; ++j) {
        if ((j % 5) != 0)
            p->drawLine(92, 0, 96, 0);
        p->rotate(6.0);
    }
//! [4]
}

int main(int argc, char **argv)
{
    QGuiApplication app(argc, argv);

    AnalogClockWindow clock;
    clock.show();

    return app.exec();
}

rasterwindow.h

该类是AnalogClockWindow 的父类,继承于QWindow

#ifndef RASTERWINDOW_H
#define RASTERWINDOW_H

//! [1]
#include <QtGui>

class RasterWindow : public QWindow
{
    Q_OBJECT
public:
    explicit RasterWindow(QWindow *parent = 0);

    virtual void render(QPainter *painter);

public slots:
    void renderLater();
    void renderNow();

protected:
    bool event(QEvent *event) override;

    void resizeEvent(QResizeEvent *event) override;
    void exposeEvent(QExposeEvent *event) override;

private:
    QBackingStore *m_backingStore;
};
//! [1]
#endif // RASTERWINDOW_H

rasterwindow.cpp

#include "rasterwindow.h"

//! [1]
RasterWindow::RasterWindow(QWindow *parent)
    : QWindow(parent)
    , m_backingStore(new QBackingStore(this))
{
}
//! [1]


//! [7]
bool RasterWindow::event(QEvent *event)
{
	//捕获UpdateRequest事件,当调用requestUpdate()函数时
    if (event->type() == QEvent::UpdateRequest) {
        renderNow();
        return true;
    }
    return QWindow::event(event);
}
//! [7]

//! [6]
void RasterWindow::renderLater()
{
	//触发事件
    requestUpdate();
}
//! [6]


//! [5]
void RasterWindow::resizeEvent(QResizeEvent *resizeEvent)
{
	//让QWindow的绘制区域适应QWindow的大小
    m_backingStore->resize(resizeEvent->size());
}
//! [5]

//! [2]如果窗口移出屏幕,被另一个窗口、图标化或类似的窗口完全遮蔽,则可能调用此函数
void RasterWindow::exposeEvent(QExposeEvent *)
{
    if (isExposed())
        renderNow();
}
//! [2]


//! [3]
void RasterWindow::renderNow()
{
    if (!isExposed())
        return;
	//设置绘制区域,m_backingStore->resize()效果看到的和m_backingStore->beginPaint()是一样的,不过resize()必须设置
    QRect rect(0, 0, width(), height());
    m_backingStore->beginPaint(rect);
	//设置QPainter 的绘制设备
    QPaintDevice *device = m_backingStore->paintDevice();
    QPainter painter(device);
	//绘制矩形背景色
    painter.fillRect(0, 0, width(), height(), QGradient::NightFade);
    //绘制时钟
    render(&painter);
    painter.end();

    m_backingStore->endPaint();
    m_backingStore->flush(rect);
}
//! [3]

//! [4]
void RasterWindow::render(QPainter *painter)
{
    qDebug() << "RasterWindow::render";
    painter->drawText(QRectF(0, 0, width(), height()), Qt::AlignCenter, QStringLiteral("QWindow"));
}
//! [4]

总结

利用QBackingStore可以绘制QWindow,也可以用其他绘制QWindow的方法是使用OpenGL和QOpenGLContext。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值