基础图形绘制

Qt 图形系统中的关键角色

QPainter

  • Qt 中的画家,能够绘制各种基础图形
  • 拥有绘图所需的画笔 (QPen),画刷 (QBrush),字体 (QFont)

QPainterDevice

  • Qt 中的画布,画家 (QPainter) 的绘画板
  • 所有的 QWidget 类都继承自 QPainterDevice

小贴士:

1. QPainter 中的所有绘制参数都可以自定义

2. 任意的 QWidget 对象都能够作为画布绘制图形

画家 (QPainter) 所使用的工具角色

QPen

  • 用于绘制几何图形的边缘,由颜色,宽度,线风格等参数组成

QBrush

  • 用于填充几何图形的调色板,由颜色和填充风格组成

QFont

  • 用于文本绘制,由字体属性组成

QPainter 的基本绘图能力

重要规则 

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

问题

如何动态绘制需要的图形?

工程中的解决方案

1. 根据需要确定参数对象 (绘图类型,点坐标,角度,等)

2. 将参数对象存入数据集合中 (如:链表)

3. 在 paintEvent 函数中遍历数据集合

4. 根据参数对象绘制图形 (update())

动态随机绘图

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPushButton>
#include <QList>

enum
{
    RECT,
    LINE,
    ELLIPSE
};

struct PaintParam
{
    int type;
    Qt::PenStyle pen;
    QPoint begin;
    QPoint end;
};

class Widget : public QWidget
{
    Q_OBJECT
private:
    QPushButton m_testBtn;

protected slots:
    void onButtonClicked();

protected:
    QList<PaintParam> m_paint;
    void paintEvent(QPaintEvent* event);

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

Widget.cpp

#include "Widget.h"
#include <QPainter>
#include <QPoint>

Widget::Widget(QWidget* parent)
    : QWidget(parent)
{
    resize(600, 400);

    m_testBtn.setParent(this);
    m_testBtn.move(500, 330);
    m_testBtn.resize(80, 30);
    m_testBtn.setText("button");

    connect(&m_testBtn, &QPushButton::clicked, this, &Widget::onButtonClicked);
}

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

    painter.begin(this);

    for(int i = 0; i < m_paint.count(); i++)
    {
        int x = (m_paint[i].begin.x() < m_paint[i].end.x() ? m_paint[i].begin.x() : m_paint[i].end.x());
        int y = (m_paint[i].begin.y() < m_paint[i].end.y() ? m_paint[i].begin.y() : m_paint[i].end.y());
        int w = qAbs(m_paint[i].begin.x() - m_paint[i].end.x());
        int h = qAbs(m_paint[i].begin.y() - m_paint[i].end.y());

        painter.setPen(m_paint[i].pen);

        switch(m_paint[i].type)
        {
        case LINE:
            painter.drawLine(m_paint[i].begin, m_paint[i].end);
            break;

        case RECT:
            painter.drawRect(x, y, w, h);
            break;

        case ELLIPSE:
            painter.drawEllipse(x, y, w, h);
            break;

        default:
            break;
        }
    }

    painter.end();
}

void Widget::onButtonClicked()
{
    PaintParam temp;
    temp.type = qrand() % 3;
    temp.pen = static_cast<Qt::PenStyle>((qrand() % 6) + 1);
    temp.begin = QPoint(qrand() % 400, qrand() % 300);
    temp.end = QPoint(qrand() % 400, qrand() % 300);

    if(m_paint.count() == 5)
    {
        m_paint.clear();
    }

    m_paint.append(temp);

    update();
}

Widget::~Widget()
{
}

程序运行结果如下所示:

Qt 图形系统中的坐标系

物理坐标系 (设备坐标系)

  • 原点 (0, 0) 在左上角的位置,单位:像素 (点)
  • x 坐标向右增长,y 坐标向下增长

逻辑坐标系

  • 数学模型中的抽象坐标系,单位由具体问题决定
  • 坐标轴的增长方向由具体问题决定

一些事实

QPainter 使用逻辑坐标系绘制图形

逻辑坐标系中图形的大小和位置经由转换后绘制于具体设备

默认情况下的逻辑坐标系和物理坐标系完全一致

视口与窗口 

视口 (view port)

  • 物理坐标系中一个任意指定的矩形

窗口

  • 逻辑坐标系下对应到物理坐标系中的相同矩形

深入理解视口和窗口 

视口和窗口是不同坐标系中的同一矩形

视口和窗口中的坐标点存在一一映射的关系

视口和窗口能够通过坐标变换而相互转换

视口和窗口的变换方法 

视口和窗口的变换方法 

定义视口 (setViewport):

左上角坐标,右下角坐标,计算宽度和高度

定义窗口 (setWindow):

左上角坐标,右下角坐标,计算宽度和高度

正弦波形绘图实例

解决方案

1. 定义视口矩形和逻辑坐标系

2. 定义画笔并填充窗口底色

3. 根据实际问题中的波形函数绘图 (drawPoint())

正弦波绘制

Widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QList>

struct Point
{
    float x;
    float y;
};

class Widget : public QWidget
{
    Q_OBJECT
protected:
    void paintEvent(QPaintEvent *event);

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

Widget.cpp

#include "Widget.h"
#include <QPainter>
#include <QPen>
#include <QtMath>

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

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

    pen.setColor(Qt::green);
    pen.setStyle(Qt::SolidLine);
    pen.setWidthF(0.01);
    painter.setPen(pen);

    painter.setViewport(50, 50, width() - 100, height() - 100);
    painter.setWindow(-10, 2, 20, -4);    // (-10, 2)  (10, -2)

    painter.fillRect(-10, 2, 20, -4, Qt::black);

    painter.drawLine(QPoint(-10, 0), QPoint(10, 0));
    painter.drawLine(QPoint(0, -2), QPoint(0, 2));

    for(float x=-10; x<10; x+=0.01)
    {
        float y = qSin(x);

        painter.drawPoint(QPointF(x, y));
    }
}

Widget::~Widget()
{
}

窗口用于逻辑坐标系下的图形绘制,视口用于实际物理设备上的图形显示

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值