Qt 重绘示例

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的槽函数是可以定义为虚函数的;

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值