Qt中的图形绘制

一、基础图形绘制

对于Qt中的图形绘制,最基础的元素之一就是“Qt中画家”——QPainter类,通过这个类,我们可以在任何QWidget上进行绘画,这个类拥有绘图所需的画笔(QPen),画刷(QBrush),字体(QFont),以及一系列针对不同形状(点、线、圆形、矩形等)不同参数的绘制函数。

绘图的基础在于QPaintDevice类,这个类代表着Qt的绘图板,并且通过这个类,可以设置绘图板的高度、宽度等参数。

我们来看Qt图形系统的关键角色的继承关系:

重点:对于图形的绘制,必须(只能)在QWidget::paintEvent()中进行,即必须在实现文件中重载这个函数,它在Qt中承担图像绘制承上启下的作用。

问题1:如何动态的绘制所需的图形?

解决方案:1、根据需要确定参数对象(绘图类型,点坐标,角度等);

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

3、在paintEvent()函数中遍历数据集合;

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

具体实现如下:

定义一个按钮,当按钮按下的时候,触发槽函数,随机绘制直线,矩形或圆形。

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

class Widget : public QWidget
{
    Q_OBJECT

    enum //使用枚举来定义常量
    {
        LINE,
        RECT,
        ELLIPSE
    };

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

    QPushButton m_PtBtn;
    QList<DrawParameter> m_list; //使用链表表示绘图参数

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

在头文件中,定义一个枚举类型的常量作为绘制图形形状的选项,同时定义一个DrawParameter的结构体,用来存放绘制图形的具体参数。

在penStyle这个类中,有多种画笔类型可供选择。查看帮助文档可以看到:

我们通过随机值来获取不同类型的画笔。同理,对于绘图形状和绘图的具体坐标,也通过随机值来产生。

实现代码如下:

void Widget::onTestBtnClicked()
{
    DrawParameter dp =
    {
        qrand() % 3,
        static_cast<Qt::PenStyle>(qrand() % 5 + 1), //笔的样式  随机取值
        QPoint(qrand() % 400, qrand() % 300),       //得到随机的两个点
        QPoint(qrand() % 400, qrand() % 300)
    };

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

    m_list.append(dp);  //数据进入链表--》 模型
    
    update(); // 模型改变,视图应发生变化
}

注意:每次获取绘图信息之后,必须对窗口进行更新update();

上面我们定义了了一个DrawParameter类型的链表,在paintEvent()函数中,我们对这个链表的数据逐个取出,并且获取绘制不同类型图像的参数,并且通过switch语句,对随机出来的数据进行绘制。

实现代码如下:

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

    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;
        default:
            break;
        }
    }

    painter.end();
}

上述就是通过改变绘图参数来进行动态绘图的方法。


Qt中的坐标系统

首先必须了解两个概念:物理坐标系、逻辑坐标系。

物理坐标系:原点(0,0)在左上角的位置,单位为具体的像素(点),x坐标向右增长,y坐标向下增长;

逻辑坐标系:数学模型中的抽象坐标系,单位有具体的问题决定;坐标轴的增长方向由具体问题决定。

我们必须清楚,QPainter使用逻辑坐标系进行图形绘制,逻辑坐标系中的图形大小和位置经过转换后绘制于具体设备,默认情况下,逻辑坐标系于物理坐标系完全一致。

另两个概念:

视口(view port):物理坐标系中一个任意指定的矩形;

窗口(window):逻辑坐标系下对应到物理坐标系中的相同矩形。

一些概念:

转换关系:

通过一段代码来具体体会一下上面这些概念的区别。

我们来实现一个正弦函数的图像绘制:

void Widget::paintEvent(QPaintEvent *)
{
    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);
    qDebug() << width() << height() ;
    painter.setWindow(-10, 2, 20, -4); //(-10, 2)(10, -2)

    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));
    }

}

在paintEvent()函数中,我们注意,视口大小的定义是根据widget的大小定义的,我们通过qDebug()可以看到随着认为的拖动widget的同时,输出的宽度和高度也在不断变化。但是对于窗口(window),是采用直接输入具体参数的方式定义的。

具体效果如下:

 

当拖动其变小时:

通过上面这个实验,就可以清晰地看出,不同参数的具体应用和区别了。

视口和窗口其实指的是不同坐标系下的同一个矩形,视口用于实际物理设备上的图形显示,窗口用于逻辑坐标系下的图形绘制。

 

 

 

Qt是一个强大的跨平台应用程序开发框架,其包括对串口通讯的支持。在Qt,你可以使用QSerialPort模块来进行串口数据的接收和发送。图形绘制方面,Qt提供了QGraphicsView和QGraphicsScene等组件,用于创建直观的界面来显示串口数据。 以下是使用Qt进行串口通讯和图形绘制的基本步骤: 1. **设置串口连接**: - 创建QSerialPort对象并打开串口。 ```cpp QSerialPort serialPort; if (!serialPort.open(QIODevice::ReadWrite)) { qCritical() << "Failed to open serial port: " << serialPort.errorString(); } ``` 2. **数据接收与处理**: - 定义一个槽函数处理接收到的数据,并可能将其转换成图形可显示的形式(例如,电压、温度等数值)。 ```cpp void handleDataReceived(const QByteArray &data) { // 处理接收到的数据,并将其添加到图形模型 } serialPort.readyRead.connect(handleDataReceived); ``` 3. **图形绘制**: - 使用QGraphicsScene和QGraphicsView创建一个可视化的区域。可以使用QGraphicsLineItem、QGraphicsPixmapItem或其他图形元素来表示数据点或趋势线。 ```cpp QGraphicsScene scene; QGraphicsView view.setScene(&scene); QGraphicsRectItem plotItem = new QGraphicsRectItem(0, 0, width, height, &scene); ``` - 根据数据更新图形,如在plotItem的位置上线或点。 4. **定时刷新和数据更新**: - 可能需要设置一个定时器定期获取新数据并更新图形。 ```cpp QTimer timer; timer.singleShot(0, []() { handleNewData(); timer.start(interval); }); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值