Skip to content

正点原子QT之绘图与图表 ​

12. 绘图与图表 ​

12.1 QPainter 绘图 ​

Qt 里的所有绘图,比如一个按钮和一个 Label 的显示, 都有绘图系统来执行。绘图系统基于 QPainter、和 QPaintDevice 和 QPainEngine 类。 QPainter 是可以直接用来操作绘图的类,而QPaintDevice 和 QPainEngine 都比 QPainter 更底层,我们只需要了解一下 QPaintDevice 和QPainEngine 就行了。
paintEvent()事件可以被重写。(解释:什么是绘图事件?可以这么理解,当界面初始化或者需要刷新时才会执行的事件,也就是说绘图事件在构造对象实例化时会执行,需要刷新界面我们可以使用 update()方法执行 paintEvent()事件)。
paintEvent()事件是父类 QWidget 提供给子类的接口,在父类里定义为空, 所以可以说paintEvent()事件就是专门给子类画图用的。

案例:旋转的 CD ​

例 03_qpainter, 旋转的 CD(难度:一般)。项目路径为 Qt/2/03_qpainter。 本例使用一张CD 图片,用 QPainter 在 paintEvent()将 CD 画在窗口的中心,并且每 100ms 旋转 1 度角度。

cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPainter>
#include <QPaintEvent>
#include <QTimer>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    /* 重写父类下的protected方法*/
protected:
    void paintEvent(QPaintEvent *);

private:
    /* 定时器,用于定时更新界面 */
    QTimer *timer;
    /* 角度 */
    int angle;

private slots:
    /* 槽函数 */
    void timerTimeOut();

};
#endif // MAINWINDOW_H
cpp
#include "mainwindow.h"
#include "QDebug"
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 设置主窗口位置及颜色 */
    this->setGeometry(0, 0, 800, 480);
    setPalette(QPalette(Qt::gray));
    setAutoFillBackground(true);

    /* 定时器实例化 */
    timer = new QTimer(this);

    /* 默认角度为0 */
    angle = 0;

    /* 定时100ms */
    timer->start(100);

    /* 信号槽连接 */
    connect(timer, SIGNAL(timeout()), this, SLOT(timerTimeOut()));
}

MainWindow::~MainWindow()
{
}

void MainWindow::timerTimeOut()
{
    /* 需要更新界面,不设置不更新 */
    this->update();
}

void MainWindow::paintEvent(QPaintEvent *)
{
    /* 指定父对象,this指本窗口 */
    QPainter painter(this);

    /* 设置抗锯齿,流畅转换 */
    painter.setRenderHints(QPainter::Antialiasing
                           | QPainter::SmoothPixmapTransform);
    /* 计算旋转角度 */
    if (angle++ == 360)
        angle = 0;

    /* QPixmap类型对象 */
    QPixmap image;

    /* 加载 */
    image.load(":/image/cd.png");

    /* QRectF即,继承QRect(Qt的矩形类),F代表精确到浮点类型 */
    QRectF rect((this->width() - image.width()) / 2,
                (this->height() - image.height()) / 2,
                image.width(),
                image.height());

    /* 默认参考点为左上角原点(0,0),因为旋转需要以图形的中心为参考点,
     * 我们使用translate把参考点设置为CD图形的中心点坐标 */
    painter.translate(0 + rect.x() + rect.width() / 2,
                      0 + rect.y() + rect.height() / 2);

    /* 旋转角度 */
    painter.rotate(angle);

    /* 现在参考点为CD图形的中心,我们需要把它设置回原点的位置,
     * 所以需要减去上面加上的数 */
    painter.translate(0 - (rect.x() + rect.width() / 2),
                      0 - (rect.y() + rect.height() / 2));

    /* 画图,QPainter提供了许多drawX的方法 */
    painter.drawImage(rect, image.toImage(), image.rect());

    /* 再画一个矩形 */
    painter.drawRect(rect.toRect());
}

12.2 QChart 图表 ​

Qt5.7 之前, Qt 在开源社区版本里没有 Qt Charts(自带的绘图组件库)。这使得像 QWT、 QCustomPlot 等第三方库有了巨大的生存空间,作者也在 Qt 5.7 以下版本使用过第三方的 QCustomPlot。

QT += charts
using namespace QtCharts;

案例:实时动态曲线 ​

cpp
#include "mainwindow.h"
#include <QDateTime>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 设置最显示位置与大小 */
    this->setGeometry(0, 0, 800, 480);
    /* 最大储存maxSize - 1个数据 */
    maxSize = 51;
    /* x轴上的最大值 */
    maxX = 5000;
    /* y轴最大值 */
    maxY = 40;

    /* splineSeries曲线实例化(折线用QLineSeries) */
    splineSeries = new QSplineSeries();
    /* 图表实例化 */
    chart = new QChart();
    /* 图表视图实例化 */
    chartView = new QChartView();

    /* 坐标轴 */
    axisY = new QValueAxis();
    axisX = new QValueAxis();
    /* 定时器 */
    timer = new QTimer(this);

    /* legend译图例类型,以绘图的颜色区分,本例设置为隐藏 */
    chart->legend()->hide();
    /* chart设置标题 */
    chart->setTitle("实时动态曲线示例");
    /* 添加一条曲线splineSeries */
    chart->addSeries(splineSeries);

    /* 设置显示格式 */
    axisY->setLabelFormat("%i");
    /* y轴标题 */
    axisY->setTitleText("温度/℃");
    /* y轴标题位置(设置坐标轴的方向) */
    chart->addAxis(axisY, Qt::AlignLeft);
    /* 设置y轴范围 */
    axisY->setRange(0, maxY);
    /* 将splineSeries附加于y轴上 */
    splineSeries->attachAxis(axisY);

    /* 设置显示格式 */
    axisX->setLabelFormat("%i");
    /* x轴标题 */
    axisX->setTitleText("时间/ms");
    /* x轴标题位置(设置坐标轴的方向) */
    chart->addAxis(axisX, Qt::AlignBottom);
    /* 设置x轴范围 */
    axisX->setRange(0, maxX);
    /* 将splineSeries附加于x轴上 */
    splineSeries->attachAxis(axisX);

    /* 将图表的内容设置在图表视图上 */
    chartView->setChart(chart);
    /* 设置抗锯齿 */
    chartView->setRenderHint(QPainter::Antialiasing);

    /* 设置为图表视图为中心部件 */
    setCentralWidget(chartView);

    /* 定时200ms */
    timer->start(200);
    /* 信号槽连接 */
    connect(timer, SIGNAL(timeout()), this, SLOT(timerTimeOut()));

    /* 设置随机种子,随机数初始化 */
    qsrand(time(NULL));
}

MainWindow::~MainWindow()
{
}

void MainWindow::timerTimeOut()
{
    /* 产生随机0~maxY之间的数据 */
    receivedData(qrand() % maxY );
}

void MainWindow::receivedData(int value)
{
    /* 将数据添加到data中 */
    data.append(value);

    /* 当储存数据的个数大于最大值时,把第一个数据删除 */
    while (data.size() > maxSize) {
        /* 移除data中第一个数据 */
        data.removeFirst();
    }

    /* 先清空 */
    splineSeries->clear();

    /* 计算x轴上的点与点之间显示的间距 */
    int xSpace = maxX / (maxSize - 1);

    /* 添加点,xSpace * i 表示第i个点的x轴的位置 */
    for (int i = 0; i < data.size(); ++i) {
        splineSeries->append(xSpace * i, data.at(i));
    }
}