文章目录
- [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函数;