Qt使用QGraphicsView绘制线路图————附带详细实现代码

文章目录

  • [0 效果](#0 效果)
  • [1 核心](#1 核心)
    • [1.1 简单示例](#1.1 简单示例)
      • [1.1.1 解读](#1.1.1 解读)
    • [1.2 创建用户交互](#1.2 创建用户交互)
      • [1.2.1 完整示例](#1.2.1 完整示例)
    • [1.3 创建图形元](#1.3 创建图形元)
      • [1.3.1 绘制直线](#1.3.1 绘制直线)
      • [1.3.2 绘制贝塞尔曲线](#1.3.2 绘制贝塞尔曲线)
      • [1.3.3 绘制图片](#1.3.3 绘制图片)
    • [1.4 移动的小车](#1.4 移动的小车)
  • [2 使用自定义视图类](#2 使用自定义视图类)
  • 参考

0 效果

视图中包含线路、道岔、信号灯、火车。

下图为站点信号灯:

下图为区间信号灯:

下视频为火车过站和区间时,信号灯的改变:

下图为信号灯的修改:

下图为道岔的修改:

  • 道岔开:
  • 道岔关:

1 核心

Qt的Graphics View 框架中,核心就是场景(QGraphicsScene)、视图(QGraphicsView)、图形项(QGraphicsItem)。

引入的头文件为:

cpp 复制代码
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>

1.1 简单示例

cpp 复制代码
	//创建场景
    QGraphicsScene* m_qgraphicsScene  = new QGraphicsScene;
    //创建视图
    QGraphicsView m_graphicsView(m_qgraphicsScene);
    
    QGraphicsLineItem* routeLine1 = new QGraphicsLineItem;
    routeLine1->setPos(QPointF(182, 295));
    routeLine1->setLine(0,0, 1896-183, 0);
    routeLine1->setPen(QPen(QColor(125, 200, 235), 4));
    //添加到场景中
    m_qgraphicsScene->addItem(routeLine1);
    
    m_graphicsView->setGeometry(0, 0, 1910, 580);//实现显示窗口大小
    m_graphicsView->setGraphicsSceneSceneRect(0, 0, 1920, 580);//设置场景大小
    
    m_graphicsView.show();

注意:

1,如果场景的实际大小(宽、高)大于显示窗口大小,就会出现滑动条;否则,就不会出现。

2,可以使用m_graphicsView->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);来进行线段的抗锯齿化;

1.1.1 解读

  • 1, 创建场景
    场景是不可见的,是一个抽象的管理图形项的容器,可向场景中添加图形项,获取场景中的某个图形项等;
cpp 复制代码
QGraphicsScene* m_qgraphicsScene  = new QGraphicsScene;
  • 2 创建视图类
    该类提供绘图的视图(View)组件,用于显示场景中的内容,处理用户的交互操作(如鼠标、键盘事件);
cpp 复制代码
QGraphicsView* m_graphicsView(m_qgraphicsScene);
  • 3 创建图形项目
cpp 复制代码
    QGraphicsLineItem* routeLine1 = new QGraphicsLineItem;
    routeLine1->setPos(QPointF(182, 295));
    routeLine1->setLine(0,0, 1896-183, 0);
    routeLine1->setPen(QPen(QColor(125, 200, 235), 4));
    //添加到场景中
    m_qgraphicsScene->addItem(routeLine1);
	
	//显示视图	
	m_graphicsView.show();

1.2 创建用户交互

继承QGraphicsView重写鼠标和键盘事件,代码中使用图标元素的坐标位置来唯一确定图形项:

示例如下:

声明:

cpp 复制代码
protected:
    /**
     * @brief mousePressEvent:鼠标点击事件
     * @param event
     */
    void mousePressEvent(QMouseEvent *event) override;
    /**
     * @brief mouseMoveEvent:鼠标移动事件
     * @param event
     */
    void mouseMoveEvent(QMouseEvent *event) override;    

实现:

cpp 复制代码
//鼠标点击事件
void GraphicsView::mousePressEvent(QMouseEvent *event)
{
    // 处理鼠标点击事件
    QPointF pos = mapToScene(event->pos());//转换为场景中的位置
    QGraphicsItem *item = scene()->itemAt(pos, QTransform());

    if (item)
    {
        qDebug()<<"获得图元坐标:"<<item->pos();

        if(item->type() == 6){//直线

        }else if(item->type() == 7){//图片
            for(QMap<LampInformation, int>::Iterator iter = g_lampTypeHash.begin();iter != g_lampTypeHash.end();iter++){
                //qDebug()<<"判断坐标是否相同";
                if(iter.key().m_pos == item->pos()){
                    //qDebug()<<"相同";

                    emit changeLampColor2MainInterface(LampInformation(iter.key().m_lampType, iter.key().m_pos, TrainStationInformationNow::ChangdeStationLocated));
                }
            }
        }
    }
    else
    {
        qDebug() << "Clicked on background";
    }
}
cpp 复制代码
//鼠标移动事件
void GraphicsView::mouseMoveEvent(QMouseEvent *event)
{
    // setMouseTracking(true);

    //QGraphicsView坐标
    QPoint viewPoint = event->pos();

    //QGraphicsScene坐标
    QPointF  scenePoint = mapToScene(viewPoint);

    QGraphicsItem *item = scene()->itemAt(scenePoint, QTransform());
    if (item)
    {
        //qDebug()<<"移动获得图标项目:";
        if(item->type() == 6){
            
        }else if(item->type() == 7){
            viewport()->setCursor(Qt::PointingHandCursor);
        }
    }
    else
    {
        //qDebug() << "没有移动到图标位置";
        viewport()->setCursor(Qt::ArrowCursor);
    }
}

1.2.1 完整示例

头文件:

cpp 复制代码
class GraphicsView : public QGraphicsView
{
    Q_OBJECT

public:
    GraphicsView(QWidget *parent = nullptr);

    //场景
    QGraphicsScene *m_scene;

protected:
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;    //鼠标移动事件

public:
    QGraphicsScene* getGraphicsScene();
    void setGraphicsSceneSceneRect(qreal x, qreal y, qreal w, qreal h);
}

cpp文件:

cpp 复制代码
GraphicsView::GraphicsView(QWidget *parent)
    : QGraphicsView(parent)
{
    // 创建场景
    m_scene = new QGraphicsScene(this);
    //m_scene->setSceneRect(0,0, 1630 , 390 );
    
    setScene(m_scene);

    setMouseTracking(true);

    // 添加图形项
    // QGraphicsRectItem *rectItem = new QGraphicsRectItem(0,0, 100, 100);
    // scene->addItem(rectItem);
}

//鼠标点击事件
void GraphicsView::mousePressEvent(QMouseEvent *event)
{

    // 处理鼠标点击事件
    QPointF pos = mapToScene(event->pos());//转换为场景中的位置
    QGraphicsItem *item = scene()->itemAt(pos, QTransform());

    if (item)
    {
        qDebug()<<"获得图元坐标:"<<item->pos();

        if(item->type() == 6){
 
        }else if(item->type() == 7){
            
        }
    }
    else
    {
        qDebug() << "Clicked on background";
    }
}
//鼠标
void GraphicsView::mouseMoveEvent(QMouseEvent *event)
{
    // setMouseTracking(true);

    //QGraphicsView坐标
    QPoint viewPoint = event->pos();

    //QGraphicsScene坐标
    QPointF  scenePoint = mapToScene(viewPoint);

    QGraphicsItem *item = scene()->itemAt(scenePoint, QTransform());
    if (item)
    {
        qDebug()<<"移动获得图标项目:"<<item->pos();
       // viewport()->setCursor(Qt::PointingHandCursor);

        if(item->type() == 6){

        }else if(item->type() == 7){
            viewport()->setCursor(Qt::PointingHandCursor);
        }
    }
    else
    {
        //qDebug() << "没有移动到图标位置";
        viewport()->setCursor(Qt::ArrowCursor);
    }
}

QGraphicsScene *GraphicsView::getGraphicsScene()
{
    return m_scene;
}

void GraphicsView::setGraphicsSceneSceneRect(qreal x, qreal y, qreal w, qreal h)
{
    m_scene->setSceneRect(x, y, w , h);
}

1.3 创建图形元

1.3.1 绘制直线

cpp 复制代码
    QGraphicsLineItem* routeLine1 = new QGraphicsLineItem;
    routeLine1->setPos(QPointF(182, 295));
    routeLine1->setLine(0,0, 1896-183, 0);
    routeLine1->setPen(QPen(QColor(125, 200, 235), 4));
    qgraphicsScene->addItem(routeLine1);

1.3.2 绘制贝塞尔曲线

cpp 复制代码
    QPainterPath path;
    path.moveTo(0, 300);
    path.cubicTo(0, 300, 16, 266, 32, 266);

    QGraphicsPathItem* routeLine = new QGraphicsPathItem(path);
    routeLine->setPen(routeLineItemPassPen);
    qgraphicsScene->addItem(routeLine);

曲线的计算方法:

1.3.3 绘制图片

cpp 复制代码
    QGraphicsPixmapItem* lamp1 = new QGraphicsPixmapItem;
    lamp1->setPos(QPointF(1054, 46));
    icoPix.load(":/Image/lampMode4Red.png");
    lamp1->setPixmap(icoPix.scaled(50, 15, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
    qgraphicsScene->addItem(lamp1);

1.4 移动的小车

小车使用的是之前博文写的类,这里仅显示对应的头文件,详细内容请查看对应的博文。

cpp 复制代码
#include <QToolButton>
#include <QTimer>

class VehicleToolButton : public QToolButton
{
    Q_OBJECT

public:
    /**
     * @brief 使用默认图片
     * @param parent
     */
    VehicleToolButton(QWidget *parent = nullptr);
    /**
     * @brief VehicleToolButton
     * @param image:按钮的图标的文件路径
     * @param size:按钮大小
     * @param parent
     */
    VehicleToolButton(QString imagePath, QSize size,QWidget *parent = nullptr);
    ~VehicleToolButton();

public:
    /**
     * @brief 得到速度
     * @return
     */
    qreal getSpeed();

    /**
     * @brief 强制设置显示位置
     * @param x
     * @param y
     */
    void setCurCoordinate(int x,int y);
    /**
     * @brief 设置现在所处位置
     * @param x
     * @param y
     */
    void setCurrentPosition(int x, int y);
    /**
     * @brief 改变速度
     * @param _speed
     */
    void setSpeed(qreal _speed);
    /**
     * @brief 设置行进参数
     * @param _v_point
     * @param _distance
     * @param _speed
     */
    void setData(QVector<QPoint> _v_point, qreal _distance, qreal _speed);
    /**
     * @brief 设置刷新时间
     * @param refreshTime
     */
    void setRefreshTime(int refreshTime);
    /**
     * @brief 设置图标图片
     * @param image
     */
    void setImage(QImage image);
    /**
     * @brief 设置图标大小
     * @param size
     */
    void setSize(QSize size);

private:
    /**
     * @brief 计算从起点到终点方向距离distance的坐标点
     * @param distance
     * @param start
     * @param end
     * @return
     */
    QPoint getPoswithLinedistance(qreal distance,QPoint start,QPoint end);
    /**
     * @brief 按水平轴或者垂直线作镜像翻转,bIsHorizon为true按水平轴,false按垂直方向
     * @param image
     * @param bIsHorizon
     * @return
     */
    QImage filp(const QImage& image,bool bIsHorizon);
    /**
     * @brief 根据弧度值(角度值)起点(x1,y1)和终点(x2,y2)确定图片旋转的角度
     * @param x1
     * @param y1
     * @param x2
     * @param y2
     */
    void setImageRote(int x1,int y1,int x2,int y2);
    /**
     * @brief 将图片按顺时针方向旋转一定的角度,fAngle为角度值
     * @param image
     * @param fAngle
     * @return
     */
    QImage rotateImage(const QImage& image,qreal fAngle);
    /**
     * @brief 根据弧度值(角度值)r_x,r_y确定图片旋转的角度
     * @param r_x
     * @param r_y
     */
    void setImageRote(qreal r_x,qreal r_y);
    /**
     * @brief //根据车速和运动轨迹计算time时间之后位置,timer事件调用move()函数移动到该位置,
     * @param time
     * @param x
     * @param y
     */
    void getCurrentPos(qreal time,int& x,int& y);

public slots:
    /**
     * @brief 刷新图片
     */
    void updatedisplay();
    /**
     * @brief 开始定时器
     * @param _msec
     */
    void startTimer(int _msec);

private:
    QImage m_image;//按钮图标
    QSize m_pixSize;//按钮大小

    //车辆行进数据结构
    QVector<QPoint> m_pointVector;												//行驶路径点集合(图上位置)
    QVector<qreal> m_linedistanceVector;											//行驶路径段在图上的线段长度
    qreal m_distance;															//行驶路径总长度(单位m)
    qreal m_linedistance;														//行驶路径在图上的总长度
    int	   m_curposindex;														//当前所在点的下标
    qreal  m_curlinedistance;													//当前所在线段上距离
    qreal  m_curlinetotledistance;											//当前行驶完成的路径长度总和
    int	   m_curposx;															//当前在图上的点X坐标
    int    m_curposy;															//当前在图上的点Y坐标
    //1km/h ----->0.27777777778m/s
    qreal m_speed = 0.0;															//当前车速 m/s


    //设置刷新时间(毫秒)
    int m_refreshTime = 10;
    //计时器
    QTimer m_timer;

signals:
    /**
     * @brief 停止移动
     */
    void stopVehicleMove();
};

2 使用自定义视图类

1,创建视图类指针:

cpp 复制代码
    GraphicsView* m_graphicsView;

2,调用方法绘图:

cpp 复制代码
    drawGraphicsViewRailwayStation(m_graphicsView);
cpp 复制代码
void MainInterface::drawGraphicsViewRailwayStation(GraphicsView* graphicsView)
{

	//清除之前的场景元素
    graphicsView->getGraphicsScene()->clear();

    //1,创建场景: 场景是不可见的,是一个抽象的管理图形项的容器,可向场景中添加图形项,获取场景中的某个图形项等
    QGraphicsScene* qgraphicsScene = graphicsView->getGraphicsScene();
    //3,图形项类(QGraphicsItem):
    //该类提供了一些基本的图形元件,也可在此基础上自定义图形项,它支持各种事件的响应,如鼠标事件、键盘事件、拖放事件等,以实现图形的交互功能

    /*------画线路------*/
   // QPen routeLineItemPassPen(QColor(125, 200, 235), 4);

    QPen turnoutLinePassPen(QColor(125, 200, 235), 2);
    QPen routeLineItemPassPen(QColor(125, 200, 235), 4);

    QPen turnoutLineBlockedPen(Qt::red, 2);
    QPen routeLineItemBlockedPen(Qt::red, 4);


//主干线
    QGraphicsLineItem* routeLine1 = new QGraphicsLineItem;
    routeLine1->setPos(QPointF(182, 295));
    routeLine1->setLine(0,0, 1896-183, 0);
    routeLine1->setPen(QPen(QColor(125, 200, 235), 4));
    qgraphicsScene->addItem(routeLine1);

    QGraphicsLineItem* routeLine2 = new QGraphicsLineItem;
    routeLine2->setPos(QPointF(4, 358));
    routeLine2->setLine(0,0, 1896-4, 0);
    routeLine2->setPen(routeLineItemPassPen);
    qgraphicsScene->addItem(routeLine2);

    QGraphicsLineItem* routeLine3 = new QGraphicsLineItem;
    routeLine3->setPos(QPointF(850, 166));
    routeLine3->setLine(0,0, 1280 - 850, 0);
    routeLine3->setPen(routeLineItemPassPen);
    qgraphicsScene->addItem(routeLine3);


//道岔
    QGraphicsLineItem* turnoutLine1 = new QGraphicsLineItem;
    turnoutLine1->setPos(QPointF(965, 100));
    turnoutLine1->setLine(0,0, 1054 - 963, 36 - 103 + 5);
    turnoutLine1->setPen(turnoutLineBlockedPen);
    qgraphicsScene->addItem(turnoutLine1);


    QGraphicsLineItem* turnoutLine2 = new QGraphicsLineItem;
    turnoutLine2->setPos(QPointF(1274, 36));
    turnoutLine2->setLine(0,0, 1323 - 1274, 103 - 36 - 3);
    turnoutLine2->setPen(turnoutLineBlockedPen);
    qgraphicsScene->addItem(turnoutLine2);

    QGraphicsLineItem* routeLine5_2 = new QGraphicsLineItem;
    routeLine5_2->setPos(QPointF(1275, 103));
    routeLine5_2->setLine(0,0, 1323 - 1275, 0);
    routeLine5_2->setPen(routeLineItemPassPen);
    qgraphicsScene->addItem(routeLine5_2);
    g_turnoutTypeStatusHash[QPair(1274, 36)] = false;
    g_turnoutRelevanceInformationHash.insert(QPair(1274, 36), TurnoutRelevanceInformation({turnoutLine2}, {routeLine5_2}));


    QGraphicsLineItem* turnoutLine3 = new QGraphicsLineItem;
    turnoutLine3->setPos(QPointF(882, 163));
    turnoutLine3->setLine(0,0, 963 - 882, 103 - 163 + 3);
    turnoutLine3->setPen(turnoutLineBlockedPen);
    qgraphicsScene->addItem(turnoutLine3);

    QGraphicsLineItem* turnoutLine4 = new QGraphicsLineItem;
    turnoutLine4->setPos(QPointF(1380, 103));
    turnoutLine4->setLine(0,0, 1460 - 1380, 231 - 103 - 3);
    turnoutLine4->setPen(turnoutLineBlockedPen);
    qgraphicsScene->addItem(turnoutLine4);

    QGraphicsLineItem* routeLine9_4 = new QGraphicsLineItem;
    routeLine9_4->setPos(QPointF(1380, 231));
    routeLine9_4->setLine(0,0, 1456 - 1380, 0);
    routeLine9_4->setPen(routeLineItemPassPen);
    qgraphicsScene->addItem(routeLine9_4);
    g_turnoutTypeStatusHash[QPair(1380, 103)] = false;
    g_turnoutRelevanceInformationHash.insert(QPair(1380, 103), TurnoutRelevanceInformation({turnoutLine4}, {routeLine9_4}));


//画灯
    //QPointF使用qreal(通常是double)来表示坐标,这意味着它可以表示浮点数坐标。而QPoint使用int来表示坐标,只能表示整数坐标
    QPixmap icoPix;

    //类型:3、4灯
    QGraphicsPixmapItem* lamp1 = new QGraphicsPixmapItem;
    lamp1->setPos(QPointF(1054, 46));
    icoPix.load(":/Image/lampMode4Red.png");
    lamp1->setPixmap(icoPix.scaled(50, 15, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
    qgraphicsScene->addItem(lamp1);
    g_lampTypeHash[LampInformation(LampTypeEnum::LampFourType, QPointF(1054, 46), TrainStationInformationNow::StationLocated)] = LampColorTypeEnum::RedLampColorType;
    m_lampGraphicsPixmapHash[QPair(1054, 46)] = lamp1;


    QGraphicsPixmapItem* lamp2 = new QGraphicsPixmapItem;
    lamp2->setPos(QPointF(1210, 12));
    icoPix.load(":/Image/lampMode3Red.png");
    lamp2->setPixmap(icoPix.scaled(50, 15, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
    qgraphicsScene->addItem(lamp2);
    g_lampTypeHash[LampInformation(LampTypeEnum::LampThreeType, QPointF(1210, 12), TrainStationInformationNow::StationLocated)] = LampColorTypeEnum::RedLampColorType;
    m_lampGraphicsPixmapHash[QPair(1210, 12)] = lamp2;
    
}

注意:

1,如果要进行场景切换,则使用graphicsView->getGraphicsScene()->clear();进行场景的清除,而不是removeItem函数;

参考

QGraphicsView架构学习总结

Qt之QGraphicsView入门篇

QT之QGraphicsScene详细介绍

Qt工作笔记-QGraphics框架场景中图元的移除与析构

QPainter、QPen、QBrush,绘图、填充、渐变等使用方法

QGraphicsScene设置SceneRect

相关推荐
一个public的class几秒前
什么是 Java 泛型
java·开发语言·后端
士别三日&&当刮目相看3 分钟前
JAVA学习*Object类
java·开发语言·学习
invincible_Tang4 分钟前
R格式 (15届B) 高精度
开发语言·算法·r语言
一只小松许️15 分钟前
Rust闭包详解
开发语言·rust
独好紫罗兰1 小时前
洛谷题单2-P5715 【深基3.例8】三位数排序-python-流程图重构
开发语言·python·算法
阳光_你好1 小时前
详细说明Qt 中共享内存方法: QSharedMemory 对象
开发语言·数据库·qt
鹿屿二向箔1 小时前
阀门流量控制系统MATLAB仿真PID
开发语言·matlab
jiet_h1 小时前
深入解析Kapt —— Kotlin Annotation Processing Tool 技术博客
android·开发语言·kotlin
anda01091 小时前
11-leveldb compact原理和性能优化
java·开发语言·性能优化
tRNA做科研1 小时前
通过Bioconductor/BiocManager安装生物r包详解(问题汇总)
开发语言·r语言·生物信息学·bioconductor·biocmanager