Qt——基础图形绘制

1.Qt图形系统中的关键角色

  • QPainter
    • Qt中的画家,能够绘制各种基础图形
    • 拥有绘图所需的画笔(QPen)、画刷(QBrush)、字体(QFont)
  • QPaintDevice
    • Qt中的画布,画家(QPainter)的绘图板
    • 所有的QWidget类都继承自QPaintDevice

注意:只能在QWidget::paintEvent中绘制图形

Widget.h

复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPushButton>
#include <QPoint>

class Widget : public QWidget
{
    Q_OBJECT

    enum
    {
        LINE, //直线
        RECT, //矩形
        ELLIPSE //椭圆
    };

    struct DrawParam
    {
        int type; //样式类型值
        Qt::PenStyle pen; //绘制样式
        QPoint begin;
        QPoint end;
    };

    QPushButton m_testBtn;
    QList<DrawParam> m_list;

protected slots:
    void onTestBtnClicked();
protected:
    void paintEvent(QPaintEvent*);

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget() override;
};
#endif // WIDGET_H

Widget.cpp

复制代码
#include "Widget.h"
#include <QPainter>
#include <QPoint>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    m_testBtn.setParent(this);
    m_testBtn.move(400, 300);
    m_testBtn.resize(70, 30);
    m_testBtn.setText("Test");

    resize(500, 350);

    connect(&m_testBtn, SIGNAL(clicked()), this, SLOT(onTestBtnClicked()));
}

void Widget::onTestBtnClicked()
{
    DrawParam dp =
    {
        qrand() % 3, //随机值
        static_cast<Qt::PenStyle>(qrand() % 5 + 1),
        QPoint(qrand()% 400, qrand() % 300),
        QPoint(qrand()% 400, qrand() % 300)
    };
    m_list.append(dp);
    update();
}
void Widget::paintEvent(QPaintEvent*)
{
    QPainter painter;

    painter.begin(this);
    for(int i=0; i<m_list.count(); i++)
    {
        int x = (m_list[i].begin.x() < m_list[i].end.x() ? m_list[i].begin.x() : m_list[i].end.x());
        int y = (m_list[i].begin.y() < m_list[i].end.y() ? m_list[i].begin.y() : m_list[i].end.y());
        int w = qAbs(m_list[i].begin.x() - m_list[i].end.x()) + 1;
        int h = qAbs(m_list[i].begin.y() - m_list[i].end.y()) + 1;
        painter.setPen(m_list[i].pen);
        switch (m_list[i].type) {
        case LINE:
            painter.drawLine(m_list[i].begin, m_list[i].end);
            break;
        case RECT:
            painter.drawRect(x, y, w, h);
            break;
        case ELLIPSE:
            painter.drawEllipse(x, y, w, h);
            break;
        }
    }
    painter.end();
}
Widget::~Widget() = default;

2.Qt图形系统中的坐标系

  • 物理坐标系(设备坐标系)
    • 原点(0,0)在左上角的位置,单位:像素
    • x坐标向右增长,y坐标向下增长
  • 逻辑坐标系
    • 数学模型中的抽象坐标系,单位由具体问题决定
    • 坐标轴的增长方向由具体问题决定
    • QPaint用的就是逻辑坐标系

3.视口和窗口

  • 视口:物理坐标系中一个任意指定的矩形
  • 窗口:逻辑坐标系下对应到物理坐标系中的相同矩形
  • 视口与窗口的变换方式
    • 定义视口:左上角坐标,右下角坐标,计算宽度和高度
    • 定义窗口:左上角坐标,右下角坐标,计算宽度和高度

Widget.h

复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT
protected:
    void paintEvent(QPaintEvent* );
public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget() override;
};
#endif // WIDGET_H

Widget.cpp

复制代码
#include "Widget.h"
#include <QPainter>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{

}
void Widget::paintEvent(QPaintEvent* )
{
    QPainter painter(this);

    painter.setViewport(0, 0, width(), height()); //设置物理坐标系统,即实际窗口的像素区域
    
    //参数含义:(左上角x, 左上角y, 宽度, 高度)
    painter.setWindow(-100, 100, 200, -200); //设置逻辑坐标系统
    painter.drawLine(QPoint(0, 0), QPoint(80, 80));
}
Widget::~Widget() = default;

运行结果:

示例:正弦波形绘图示例

Widget.cpp

复制代码
#include "Widget.h"
#include <QPainter>
#include <QPointF>
#include <qmath.h>
#include <QPen>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{

}
void Widget::paintEvent(QPaintEvent* )
{
    QPainter painter(this);
    QPen pen;

    pen.setColor(Qt::green);
    pen.setStyle(Qt::SolidLine);
    pen.setWidthF(0.01); //线条宽度为 0.01
    painter.setPen(pen);

    painter.setViewport(50, 50, width()-100, height()-100); //设置物理坐标系统,即实际窗口的像素区域

    //参数含义:(左上角x, 左上角y, 宽度, 高度)
    painter.setWindow(-10, 2, 20, -4); //设置逻辑坐标系统
    painter.fillRect(-10, 2, 20, -4, Qt::black); //是在逻辑坐标系中绘制一个填充矩形
    painter.drawLine(QPointF(-10, 0), QPointF(10, 0));
    painter.drawLine(QPointF(0, 2), QPointF(0, -2));

    for(float x=-10; x<10; x+=0.01)
    {
        float y = qSin(x);
        painter.drawPoint(QPointF(x, y));
    }
}
Widget::~Widget() = default;

运行结果: