简介
在 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。