Qt使用QGraphicsView绘制线路图————附带详细实现代码
文章目录
- 0 效果
- 1 核心
- 1.1 简单示例
- 1.1.1 解读
- 1.2 创建用户交互
- 1.2.1 完整示例
- 1.3 创建图形元
- 1.3.1 绘制直线
- 1.3.2 绘制贝塞尔曲线
- 1.3.3 绘制图片
- 1.4 移动的小车
- 2 使用自定义视图类
- 参考
0 效果
视图中包含线路、道岔、信号灯、火车。
下图为站点信号灯:
下图为区间信号灯:
下视频为火车过站和区间时,信号灯的改变:
下图为信号灯的修改:
下图为道岔的修改:
- 道岔开:
- 道岔关:
1 核心
Qt的Graphics View 框架中,核心就是场景(QGraphicsScene)、视图(QGraphicsView)、图形项(QGraphicsItem)。
引入的头文件为:
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsRectItem>
1.1 简单示例
//创建场景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, 创建场景
场景是不可见的,是一个抽象的管理图形项的容器,可向场景中添加图形项,获取场景中的某个图形项等;
QGraphicsScene* m_qgraphicsScene = new QGraphicsScene;
- 2 创建视图类
该类提供绘图的视图(View)组件,用于显示场景中的内容,处理用户的交互操作(如鼠标、键盘事件);
QGraphicsView* m_graphicsView(m_qgraphicsScene);
- 3 创建图形项目
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
重写鼠标和键盘事件,代码中使用图标元素的坐标位置来唯一确定图形项:
示例如下:
声明:
protected:/*** @brief mousePressEvent:鼠标点击事件* @param event*/void mousePressEvent(QMouseEvent *event) override;/*** @brief mouseMoveEvent:鼠标移动事件* @param event*/void mouseMoveEvent(QMouseEvent *event) override;
实现:
//鼠标点击事件
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";}
}
//鼠标移动事件
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 完整示例
头文件:
class GraphicsView : public QGraphicsView
{Q_OBJECTpublic: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文件:
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 绘制直线
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 绘制贝塞尔曲线
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 绘制图片
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 移动的小车
小车使用的是之前博文写的类,这里仅显示对应的头文件,详细内容请查看对应的博文。
#include <QToolButton>
#include <QTimer>class VehicleToolButton : public QToolButton
{Q_OBJECTpublic:/*** @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/sqreal m_speed = 0.0; //当前车速 m/s//设置刷新时间(毫秒)int m_refreshTime = 10;//计时器QTimer m_timer;signals:/*** @brief 停止移动*/void stopVehicleMove();
};
2 使用自定义视图类
1,创建视图类指针:
GraphicsView* m_graphicsView;
2,调用方法绘图:
drawGraphicsViewRailwayStation(m_graphicsView);
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