Qt 重绘示例
1. 简单的重绘
- 绘制矩形、扇形、图片、文字
// .h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <memory>
using std::unique_ptr;
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
void paintEvent(QPaintEvent *event) override;
private:
void InitializeWidget();
private:
Ui::Widget *ui;
unique_ptr<QPen> m_upPen;
unique_ptr<QBrush> m_upBrush;
unique_ptr<QPainter> m_upPainter;
};
#endif // WIDGET_H
// .cpp
#include "Widget.h"
#include "ui_Widget.h"
#include <QPainter>
#include <QPen>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
InitializeWidget();
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
m_upPainter->begin(this);
m_upPainter->setRenderHint(QPainter::RenderHint::Antialiasing);
m_upPainter->setRenderHint(QPainter::RenderHint::TextAntialiasing);
m_upPen->setWidth(3);
m_upPen->setColor(Qt::GlobalColor::red);
m_upPen->setStyle(Qt::PenStyle::DotLine);
m_upPainter->setPen(*m_upPen);
m_upBrush->setColor(Qt::GlobalColor::yellow);
m_upBrush->setStyle(Qt::BrushStyle::SolidPattern);
m_upPainter->setBrush(*m_upBrush);
// 绘制矩形
QRect rectA(20, 30, 200, 100);
m_upPainter->drawRect(rectA); // 画刷绘制内部,画笔绘制边框
m_upPen->setWidth(1);
m_upPen->setColor(Qt::GlobalColor::red);
m_upPen->setStyle(Qt::PenStyle::SolidLine);
m_upPainter->setPen(*m_upPen);
m_upBrush->setColor(Qt::GlobalColor::blue);
m_upBrush->setStyle(Qt::BrushStyle::BDiagPattern);
m_upPainter->setBrush(*m_upBrush);
// 绘制扇形
QRect rectB(280, 30, 200, 100);
m_upPainter->drawPie(rectB, 30 * 16, 300 * 16); // 画刷绘制内部,画笔绘制边框
m_upPen->setColor(QColor(0, 255, 3));
m_upPainter->setPen(*m_upPen);
m_upPainter->setFont(QFont(QString::fromLocal8Bit("楷体"), 20));
// 绘制文本
QRect rectC(20, 150, 300, 100);
m_upPainter->drawText(rectC, Qt::AlignmentFlag::AlignCenter, QString::fromLocal8Bit("文本内容ABCD1234"));
QImage image(":/Image/images/Flower.jpg");
// 绘制图片
QRect rectD(350, 150, image.width() * 0.9, image.height());
m_upPainter->drawImage(rectD, image);
m_upPainter->end();
}
void Widget::InitializeWidget()
{
m_upPen = std::make_unique<QPen>();
m_upBrush = std::make_unique<QBrush>();
m_upPainter = std::make_unique<QPainter>();
}
2. 使用多线程将要绘制的内容绘制在Pixmap上作为缓冲,再由主线程统一绘制到界面
2.1 程序构成
- 两个绘图的Woker和对应的线程(QThread)
- 两个QPixmap缓冲区
// worker部分的.h和.cpp
// BasePaintWorker 为基类,左右分别继承它负责界面左右的绘制
//
#ifndef BASEPAINTWORKER_H
#define BASEPAINTWORKER_H
#include <Windows.h>
#include <QObject>
#include <memory>
#include <QPainter>
using std::unique_ptr;
using std::shared_ptr;
class BasePaintWorker : public QObject
{
Q_OBJECT
public:
explicit BasePaintWorker(const shared_ptr<QPixmap>& spPaintPixDevice, QObject *parent = nullptr);
virtual ~BasePaintWorker();
public:
HANDLE& GetEvent();
protected:
virtual void DrawData() = 0;
public slots:
void SlotDrawData();
protected:
shared_ptr<QPixmap> m_spPaintPixDevice;
unique_ptr<QPainter> m_upPainter;
HANDLE m_hEvent; // 用于做线程的同步控制,当执行完成之后置位
};
#endif // BASEPAINTWORKER_H
//
#ifndef LEFTPAINTWORKER_H
#define LEFTPAINTWORKER_H
#include "BasePaintWorker.h"
class LeftPaintWorker : public BasePaintWorker
{
Q_OBJECT
public:
explicit LeftPaintWorker(const shared_ptr<QPixmap>& spPaintPixDevice, QObject *parent = nullptr);
~LeftPaintWorker();
protected:
void DrawData() override;
};
#endif // LEFTPAINTWORKER_H
//
#ifndef RIGHTPAINTWORKER_H
#define RIGHTPAINTWORKER_H
#include "BasePaintWorker.h"
class RightPaintWorker : public BasePaintWorker
{
Q_OBJECT
public:
explicit RightPaintWorker(const shared_ptr<QPixmap>& spPaintPixDevice, QObject *parent = nullptr);
~RightPaintWorker();
protected:
void DrawData() override;
};
#endif // RIGHTPAINTWORKER_H
//
#include "BasePaintWorker.h"
#include <QDebug>
BasePaintWorker::BasePaintWorker(const shared_ptr<QPixmap>& spPaintPixDevice, QObject *parent) : QObject(parent)
{
m_spPaintPixDevice = spPaintPixDevice;
m_upPainter = std::make_unique<QPainter>();
m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
}
BasePaintWorker::~BasePaintWorker()
{
if (NULL != m_hEvent)
{
CloseHandle(m_hEvent);
m_hEvent = NULL;
}
}
HANDLE &BasePaintWorker::GetEvent()
{
return m_hEvent;
}
void BasePaintWorker::SlotDrawData()
{
DrawData();
SetEvent(m_hEvent);
qDebug() << "Do Over!";
}
//
#include "LeftPaintWorker.h"
#include <QDebug>
LeftPaintWorker::LeftPaintWorker(const shared_ptr<QPixmap>& spPaintPixDevice, QObject *parent) : BasePaintWorker(spPaintPixDevice, parent)
{
// ...
}
LeftPaintWorker::~LeftPaintWorker()
{
// ...
}
void LeftPaintWorker::DrawData()
{
m_upPainter->begin(m_spPaintPixDevice.get());
QPen pen(Qt::black);
pen.setWidth(1);
QBrush brush(Qt::red);
m_upPainter->setPen(pen);
m_upPainter->setBrush(brush);
for (int nRow = 0; nRow < 50; ++nRow)
{
for (int nCol = 0; nCol < 25; ++nCol)
{
QRect rect(nCol * 10, nRow * 10, 5, 5);
m_upPainter->drawRect(rect);
}
}
m_upPainter->end();
}
//
#include "RightPaintWorker.h"
RightPaintWorker::RightPaintWorker(const shared_ptr<QPixmap> &spPaintPixDevice, QObject *parent) : BasePaintWorker(spPaintPixDevice, parent)
{
}
RightPaintWorker::~RightPaintWorker()
{
}
void RightPaintWorker::DrawData()
{
m_upPainter->begin(m_spPaintPixDevice.get());
QPen pen(Qt::black);
pen.setWidth(1);
QBrush brush(Qt::green);
m_upPainter->setPen(pen);
m_upPainter->setBrush(brush);
for (int nRow = 0; nRow < 50; ++nRow)
{
for (int nCol = 0; nCol < 25; ++nCol)
{
QRect rect(nCol * 10, nRow * 10, 5, 5);
m_upPainter->drawRect(rect);
}
}
m_upPainter->end();
}
// 主界面代码
// .h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
#include <QPixmap>
#include <memory>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
using std::unique_ptr;
using std::shared_ptr;
class BasePaintWorker;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void paintEvent(QPaintEvent* event) override;
private slots:
void on_actionStart_triggered();
signals:
void SignalDrawData();
private:
Ui::MainWindow *ui;
shared_ptr<QPixmap> m_spLeftPaintPixDevice;
shared_ptr<QPixmap> m_spRightPaintPixDevice;
unique_ptr<QThread> m_upLeftPaintThread;
unique_ptr<QThread> m_upRightPaintThread;
unique_ptr<BasePaintWorker> m_upLeftPaintWorker;
unique_ptr<BasePaintWorker> m_upRightPaintWorker;
bool m_bPaint = false;
qint64 m_nTime = 0;
};
#endif // MAINWINDOW_H
// .cpp
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "LeftPaintWorker.h"
#include "RightPaintWorker.h"
#include <QDebug>
#include <QDateTime>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
resize(500, 500);
m_spLeftPaintPixDevice = std::make_shared<QPixmap>(250, 500);
m_spRightPaintPixDevice = std::make_shared<QPixmap>(250, 500);
m_upLeftPaintThread = std::make_unique<QThread>();
m_upRightPaintThread = std::make_unique<QThread>();
m_upLeftPaintWorker = std::make_unique<LeftPaintWorker>(m_spLeftPaintPixDevice);
m_upRightPaintWorker = std::make_unique<RightPaintWorker>(m_spRightPaintPixDevice);
m_upLeftPaintWorker->moveToThread(m_upLeftPaintThread.get());
m_upRightPaintWorker->moveToThread(m_upRightPaintThread.get());
connect(this, &MainWindow::SignalDrawData, m_upLeftPaintWorker.get(), &BasePaintWorker::SlotDrawData);
connect(this, &MainWindow::SignalDrawData, m_upRightPaintWorker.get(), &BasePaintWorker::SlotDrawData);
m_upLeftPaintThread->start();
m_upRightPaintThread->start();
}
MainWindow::~MainWindow()
{
m_upLeftPaintThread->quit();
m_upLeftPaintThread->wait();
m_upRightPaintThread->quit();
m_upRightPaintThread->wait();
delete ui;
}
void MainWindow::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
QPainter painter;
painter.begin(this);
painter.drawPixmap(QPoint(0, 0), *m_spLeftPaintPixDevice); // 绘制 Pixmap
painter.drawPixmap(QPoint(250, 0), *m_spRightPaintPixDevice); // 绘制 Pixmap
painter.end();
qint64 nUseTime = QDateTime::currentMSecsSinceEpoch() - m_nTime;
qDebug() << "Use time = " << nUseTime;
return QWidget::paintEvent(event);
}
void MainWindow::on_actionStart_triggered()
{
m_nTime = QDateTime::currentMSecsSinceEpoch();
ResetEvent(m_upLeftPaintWorker->GetEvent());
ResetEvent(m_upRightPaintWorker->GetEvent());
emit SignalDrawData();
DWORD nLeft = WaitForSingleObject(m_upLeftPaintWorker->GetEvent(), 5000);
DWORD nRight = WaitForSingleObject(m_upRightPaintWorker->GetEvent(), 5000);
qDebug() << "Left = " << nLeft;
qDebug() << "Right = " << nRight;
update();
}
上述涉及到的技术栈:
- Qt 多线程
- Windows 句柄同步的等待
- 重绘
- 继承、多态
我们发现Qt的槽函数内部是可以调用虚函数的,起始Qt的槽函数是可以定义为虚函数的;