QGraphicsView、QGraphicsScene、QGraphicsItem是QT图形框架三个重要元素,通过QGraphicsItem创建图元,但是实际应用中,通常使用继承QGraphicsItem而来的QGraphicsObject。图元添加到QGraphicsScene中可以显示出来或者进行用户交互操作。QGraphicsView的作用是,将Scene中的部分或者全部显示出来,可以拖动或放大缩小,Scene和View的关系如下图所示。

基本使用流程为:首先创建一个继承QWidget的类,用于盛放和显示View,包括成员:QGridLayout、QGraphicsScene、QGraphicsView。MyGraphicsView是继承自QGraphicsView的自定义View,用于替代QGraphicsView。
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include "mygraphicsview.h"
- #include "mygraphicsobject.h"
-
- class DxfWnd : public QWidget
- {
- Q_OBJECT
- public:
- explicit DxfWnd(QWidget *parent = nullptr);
-
- void Demo();
- void LoadXYZ(const QString &t_strFile);
-
- MyGraphicsView *m_view;
-
- private:
- QGridLayout *layout;
- QGraphicsScene *m_scene;
-
- QList
m_listPositionItem; -
- private slots:
- void RecvRatioFromGraphicsView(double, double, double, double);
-
- };
-
- #endif // DXFWND_H
构造函数
- DxfWnd::DxfWnd(QWidget *parent) : QWidget(parent)
- {
- layout = new QGridLayout();
- this->setLayout(layout);
- m_view = new MyGraphicsView(); // 定义一个视图
-
- connect(m_view, &MyGraphicsView::SendRatio2DxfWnd, this, &DxfWnd::RecvRatioFromGraphicsView);
-
- Demo();
- }
在类中定义成员函数,创建图元,创建Scene,创建View,讲图元添加到Scene,将Scene添加到View,将View添加到QLayout中,实现图形的显示。
- void DxfWnd::Demo(){
- m_scene = new QGraphicsScene(); // 定义一个场景,设置背景色为白色
- m_scene->setBackgroundBrush(Qt::white);
- QPen pen; // 定义一个画笔,设置画笔颜色和宽度
- pen.setColor(QColor(0, 160, 230));
- pen.setWidth(1);
-
- QGraphicsRectItem *rectItem = new QGraphicsRectItem(); // 定义一个矩形图元
- rectItem->setRect(476415,3888005, 80, 80);
- rectItem->setPen(pen);
- rectItem->setBrush(QBrush(QColor(255, 0, 255)));
- rectItem->setFlag(QGraphicsItem::ItemIsMovable);
-
- m_scene->addItem(rectItem);
- LoadXYZ("D:/QT_temp/layout_test/data/0.xyz");
-
- // MyGraphicsObject *gi = new MyGraphicsObject();
- // gi->setPos(476415,3888005);
- // gi->SetValue(12.34);
- // m_scene->addItem(gi);
-
-
- m_view->setFixedSize(400, 300);
- m_view->setScene(m_scene);
- m_view->setDragMode(QGraphicsView::RubberBandDrag); //设置view橡皮筋框选区域
-
- layout->addWidget(m_view);
- }
对于大量图元的管理,可以创建一个QVector
- void DxfWnd::LoadXYZ(const QString &t_strFile){
- // 读取数据
- QVector
vectorPoint; - QFile file(t_strFile);
- if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- QTextStream in(&file);
- while (!in.atEnd()) {
-
- // 读取一行
- QString line = in.readLine();
- QStringList strList = line.split(',');
-
- // to QVector3D
- if(strList.size() == 3) {
- QVector3D point;
- point.setX(strList.at(0).toDouble());
- point.setY(strList.at(1).toDouble());
- point.setZ(strList.at(2).toDouble());
- vectorPoint.append(point);
- }
- }
- }
- // Scene中加载
- for(auto const point : vectorPoint) {
- auto pPositionItem = new MyGraphicsObject();
- pPositionItem->setPos(point.x(), point.y());
- pPositionItem->SetValue(point.z());
- m_scene->addItem(pPositionItem);
- m_listPositionItem.append(pPositionItem);
- }
- }
-
- void DxfWnd::RecvRatioFromGraphicsView(double p0x, double p0y, double p2x, double p2y){
- if(p2x - p0x > 1000 || p2y - p0y > 1000){
- for(int i=0; i
size(); i++) { - if(i % 10 == 0) {
- m_listPositionItem.at(i)->show();
- }
- else{
- m_listPositionItem.at(i)->hide();
- }
- }
- }
- else{
- for(int i=0; i
size(); i++) { - m_listPositionItem.at(i)->show();
- }
- }
- }
在实际使用中,通常使用继承QGraphicsView的自定义View,可以进行鼠标操作等用户交互操作的管理。
- #ifndef MYGRAPHICSVIEW_H
- #define MYGRAPHICSVIEW_H
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- enum MODE{selectMode, drawMode, deleteMode};
-
- class MyGraphicsView : public QGraphicsView
- {
- Q_OBJECT
- public:
- MyGraphicsView();
-
- private:
- QPointF centerAnchor;
- QPointF posAnchor;
- bool viewMove = false;
- double m_scaleValue = 1;
- MODE mode = selectMode;
-
- QGraphicsItem *selectedItem = nullptr;
-
- void GetScale();
-
- QVector
point2LineCache; -
- public slots:
- void slot_rotateLeft();// { rotate(-30); }
- void slot_rotateRight();// { rotate(30); }
- void slot_reset();
- void ActiveDrawLine();
- void CancelDrawLine();
- void DeleteItem();
- void StopDeleteMode();
-
- protected:
- virtual void mousePressEvent(QMouseEvent *event);
- virtual void mouseReleaseEvent(QMouseEvent *event);
- virtual void wheelEvent(QWheelEvent *event);
- virtual void mouseMoveEvent(QMouseEvent *event);
- void view_zoomIn();// { scale(1.2, 1.2); }
- void veiw_zoomOut();// { scale(1/1.2, 1/1.2); }
-
- signals:
- void SendRatio2DxfWnd(double, double, double, double);
- };
-
- #endif // MYGRAPHICSVIEW_H
mousePressEvent定义了鼠标点击操作
- void MyGraphicsView::mousePressEvent(QMouseEvent *event)
- {
- if( event->button() == Qt::RightButton)
- {
- QMenu *mouseLeftMenu = new QMenu(this);
- QAction* rotateLeft = new QAction(tr("rotateLeft"), this);
- QAction* rotateRight = new QAction(tr("rotateRight"), this);
- QAction* draw = new QAction(tr("drawLine"), this);
- QAction* cancel = new QAction(tr("stopDrawLine"), this);
- QAction* zoomReset = new QAction(tr("zoomReset"), this);
- QAction* deleteItem = new QAction(tr("delete"), this);
- QAction* cancelDeleteMode = new QAction(tr("stopDelete"), this);
-
- mouseLeftMenu->addAction(zoomReset);
- // mouseLeftMenu->addAction(rotateLeft);
- // mouseLeftMenu->addAction(rotateRight);
- if (mode == selectMode){
- mouseLeftMenu->addAction(draw);
- mouseLeftMenu->addAction(deleteItem);
- }
- else if(mode == drawMode){
- mouseLeftMenu->addAction(cancel);
- }
- // mouseLeftMenu->addAction(zoomOut);
- else if (mode == deleteMode){
- mouseLeftMenu->addAction(cancelDeleteMode);
- }
-
- mouseLeftMenu->move(cursor().pos());
- mouseLeftMenu->show();
-
- connect(rotateLeft, SIGNAL(triggered()), this, SLOT(slot_rotateLeft()));
- connect(rotateRight, SIGNAL(triggered()), this, SLOT(slot_rotateRight()));
- connect(draw, SIGNAL(triggered()), this, SLOT(ActiveDrawLine()));
- connect(cancel, SIGNAL(triggered()), this, SLOT(CancelDrawLine()));
- connect(zoomReset, SIGNAL(triggered()), this, SLOT(slot_reset()));
- connect(deleteItem, SIGNAL(triggered()), this, SLOT(DeleteItem()));
- connect(cancelDeleteMode, SIGNAL(triggered()), this, SLOT(StopDeleteMode()));
- }
- else if(event->button() == Qt::MiddleButton)
- {
- mode = selectMode;
- viewMove = true;
- GetScale();
- // centerAnchor = mapToScene(event->pos()) - event->pos() + QPointF(width() / 2, height() / 2);
- centerAnchor = mapToScene(QPoint(width() / 2, height() / 2));
- posAnchor = event->pos();
- }
- else if(event->button() == Qt::LeftButton){
- if(mode == drawMode){
- point2LineCache.append(mapToScene(event->pos()));
- if (point2LineCache.size() == 2){
- QLineF *line = new QLineF(point2LineCache[0], point2LineCache[1]);
- scene()->addLine(*line);
- point2LineCache.pop_front();
-
- scene()->update();
- }
- }
- else{
- QGraphicsItem *selectedItem = this->itemAt(event->pos());
- if(selectedItem){
- scene()->removeItem(selectedItem);
- }
- }
- }
- }
点击右键,会弹出菜单

点击左键,进行画线、删除等操作。
点击滚轮,进行View显示范围的移动(配合mouseMoveEvent实现View拖动)。
centerAnchor = mapToScene(QPoint(width() / 2, height() / 2));
该语句功能是得到画面中心在Scene中的坐标。
posAnchor = event->pos();
是获取鼠标在View中的坐标(注意存在Scene坐标系和View坐标系两个坐标系,View坐标系是像素距离坐标系,Scene坐标系是图元所在的实际坐标系)。
- void MyGraphicsView::mouseMoveEvent(QMouseEvent *event){
- if(viewMove){
- QPointF offsetPos = event->pos() - posAnchor;
- // setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
- GetScale();
- centerOn(centerAnchor - offsetPos * m_scaleValue);
- }
- }
在mouseMoveEvent中,得到鼠标移动后的位置,计算鼠标移动了多少距离,乘画面比例(即1像素距离等于多少实际距离),得到View的中心应该移动多少实际距离。
centerOn(centerAnchor - offsetPos * m_scaleValue);
该函数是将View的中心放置在一个新的Scene点上,即实现了可视范围的拖动。
wheelEvent实现放大缩小
- void MyGraphicsView::wheelEvent(QWheelEvent *event){
- auto test = this->mapToScene( this->viewport()->geometry() );
- double p0x = test[0].x();
- double p0y = test[0].y();
- double p2x = test[2].x();
- double p2y = test[2].y();
-
- if(event->delta() > 0){ // 当滚轮远离使用者时
- view_zoomIn(); // 进行放大
- }else{ // 当滚轮向使用者方向旋转时
- veiw_zoomOut(); // 进行缩小
- }
-
- emit SendRatio2DxfWnd(p0x, p0y, p2x, p2y);
- }
-
- void MyGraphicsView::view_zoomIn()
- {
- this->scale(1.2, 1.2);
- }
- void MyGraphicsView::veiw_zoomOut()
- {
- this->scale(1/1.2, 1/1.2);
- }
计算1像素距离代表多少实际距离,通过如下函数实现。
- void MyGraphicsView::GetScale(){
- auto test = this->mapToScene( this->viewport()->geometry() );
- double p0x = test[0].x();
- double p2x = test[2].x();
-
- m_scaleValue = (p2x - p0x) / this->width();
- }
mapToScene函数得到View四个角点在Scene坐标系中的实际坐标,width()函数获取View的像素宽度。
自定义View全部成员函数如下
- #include "mygraphicsview.h"
-
- MyGraphicsView::MyGraphicsView()
- {
- setMouseTracking(true);
- }
-
- void MyGraphicsView::mousePressEvent(QMouseEvent *event)
- {
- if( event->button() == Qt::RightButton)
- {
- QMenu *mouseLeftMenu = new QMenu(this);
- QAction* rotateLeft = new QAction(tr("rotateLeft"), this);
- QAction* rotateRight = new QAction(tr("rotateRight"), this);
- QAction* draw = new QAction(tr("drawLine"), this);
- QAction* cancel = new QAction(tr("stopDrawLine"), this);
- QAction* zoomReset = new QAction(tr("zoomReset"), this);
- QAction* deleteItem = new QAction(tr("delete"), this);
- QAction* cancelDeleteMode = new QAction(tr("stopDelete"), this);
-
- mouseLeftMenu->addAction(zoomReset);
- // mouseLeftMenu->addAction(rotateLeft);
- // mouseLeftMenu->addAction(rotateRight);
- if (mode == selectMode){
- mouseLeftMenu->addAction(draw);
- mouseLeftMenu->addAction(deleteItem);
- }
- else if(mode == drawMode){
- mouseLeftMenu->addAction(cancel);
- }
- // mouseLeftMenu->addAction(zoomOut);
- else if (mode == deleteMode){
- mouseLeftMenu->addAction(cancelDeleteMode);
- }
-
- mouseLeftMenu->move(cursor().pos());
- mouseLeftMenu->show();
-
- connect(rotateLeft, SIGNAL(triggered()), this, SLOT(slot_rotateLeft()));
- connect(rotateRight, SIGNAL(triggered()), this, SLOT(slot_rotateRight()));
- connect(draw, SIGNAL(triggered()), this, SLOT(ActiveDrawLine()));
- connect(cancel, SIGNAL(triggered()), this, SLOT(CancelDrawLine()));
- connect(zoomReset, SIGNAL(triggered()), this, SLOT(slot_reset()));
- connect(deleteItem, SIGNAL(triggered()), this, SLOT(DeleteItem()));
- connect(cancelDeleteMode, SIGNAL(triggered()), this, SLOT(StopDeleteMode()));
- }
- else if(event->button() == Qt::MiddleButton)
- {
- mode = selectMode;
- viewMove = true;
- GetScale();
- // centerAnchor = mapToScene(event->pos()) - event->pos() + QPointF(width() / 2, height() / 2);
- centerAnchor = mapToScene(QPoint(width() / 2, height() / 2));
- posAnchor = event->pos();
- }
- else if(event->button() == Qt::LeftButton){
- if(mode == drawMode){
- point2LineCache.append(mapToScene(event->pos()));
- if (point2LineCache.size() == 2){
- QLineF *line = new QLineF(point2LineCache[0], point2LineCache[1]);
- scene()->addLine(*line);
- point2LineCache.pop_front();
-
- scene()->update();
- }
- }
- else{
- QGraphicsItem *selectedItem = this->itemAt(event->pos());
- if(selectedItem){
- scene()->removeItem(selectedItem);
- }
- }
- }
- }
-
- void MyGraphicsView::mouseMoveEvent(QMouseEvent *event){
- if(viewMove){
- QPointF offsetPos = event->pos() - posAnchor;
- // setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
- GetScale();
- centerOn(centerAnchor - offsetPos * m_scaleValue);
- }
- }
-
- void MyGraphicsView::mouseReleaseEvent(QMouseEvent *event){
- viewMove = false;
- }
-
- void MyGraphicsView::wheelEvent(QWheelEvent *event){
- auto test = this->mapToScene( this->viewport()->geometry() );
- double p0x = test[0].x();
- double p0y = test[0].y();
- double p2x = test[2].x();
- double p2y = test[2].y();
-
- if(event->delta() > 0){ // 当滚轮远离使用者时
- view_zoomIn(); // 进行放大
- }else{ // 当滚轮向使用者方向旋转时
- veiw_zoomOut(); // 进行缩小
- }
-
- emit SendRatio2DxfWnd(p0x, p0y, p2x, p2y);
- }
-
- void MyGraphicsView::GetScale(){
- auto test = this->mapToScene( this->viewport()->geometry() );
- double p0x = test[0].x();
- double p2x = test[2].x();
-
- m_scaleValue = (p2x - p0x) / this->width();
- }
-
- void MyGraphicsView::view_zoomIn()
- {
- this->scale(1.2, 1.2);
- }
- void MyGraphicsView::veiw_zoomOut()
- {
- this->scale(1/1.2, 1/1.2);
- }
- void MyGraphicsView::slot_rotateLeft()
- {
- this->rotate(-30);
- }
- void MyGraphicsView::slot_rotateRight()
- {
- this->rotate(30);
- }
- void MyGraphicsView::slot_reset()
- {
- QRectF rectItem = scene()->itemsBoundingRect();
- QRectF rectView = this->rect();
- qreal ratioView = rectView.height() / rectView.width();
- qreal ratioItem = rectItem.height() / rectItem.width();
- if (ratioView > ratioItem)
- {
- rectItem.moveTop(rectItem.width()*ratioView - rectItem.height());
- rectItem.setHeight(rectItem.width()*ratioView);
-
- rectItem.setWidth(rectItem.width() * 1.2);
- rectItem.setHeight(rectItem.height() * 1.2);
- }
- else
- {
- rectItem.moveLeft(rectItem.height()/ratioView - rectItem.width());
- rectItem.setWidth(rectItem.height()/ratioView);
-
- rectItem.setWidth(rectItem.width() * 1.2);
- rectItem.setHeight(rectItem.height() * 1.2);
- }
-
- this->fitInView(rectItem, Qt::KeepAspectRatio);
- }
- void MyGraphicsView::ActiveDrawLine(){
- mode = drawMode;
- }
- void MyGraphicsView::CancelDrawLine(){
- mode = selectMode;
- point2LineCache.clear();
- }
- void MyGraphicsView::DeleteItem(){
- mode = deleteMode;
- }
- void MyGraphicsView::StopDeleteMode(){
- mode = selectMode;
- }
利用一个结构体
enum MODE{selectMode, drawMode, deleteMode};
来表示当前鼠标是什么状态。