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

相关推荐
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner2 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz7 天前
QML Hello World 入门示例
qt
xcyxiner10 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner11 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner11 天前
DicomViewer (添加模型类)3
qt
xcyxiner12 天前
DicomViewer (目录调整) 2
qt
xcyxiner12 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00614 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术14 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript