QT中Graphics View程序基本结构和功能实现

功能介绍

程序参考《Qt5.9 C++开发指南》实现,有部分类名、变量名进行重命名。添加了场景坐标轴的绘制,为了更好体会视图坐标、场景坐标、绘图项坐标.
QPainter不能实现图件的选择、编辑、拖放、修改等功能

  • QGraphicScene(场景):可以管理多个图形项
  • QGraphicItems(图形项): 也就是图元,支持鼠标的事件响应
  • QGraphicView(视图):关联场景可以让场景中的所有图形项可视化

代码实现

继承QGraphicsView实现自定义MyGraphicsView

实现了mouseMoveEvent()事件和mousePressEvent()事件,并把鼠标事件转换为信号,这样可以在主程序设计槽响应这些鼠标事件

MyGraphicsView.h代码实现

#pragma once
#include <QGraphicsView>
#include <QMouseEvent>
class MyGraphicsView :public QGraphicsView
{
	Q_OBJECT
public:
	MyGraphicsView(QWidget* parent = 0);
protected:
	void mouseMoveEvent(QMouseEvent* event);
	void mousePressEvent(QMouseEvent* evment);
signals:
	void mouseMovePoint(QPoint point);//自定义MouseMove信号
	void mouseClicked(QPoint point);//自定义MouseClicked信号	
};

MyGraphicsView.cpp代码实现

#include "MyGraphicsView.h"
MyGraphicsView::MyGraphicsView(QWidget* parent) :QGraphicsView(parent)
{
}

/*
此事件相应代码里,通过事件的pos()函数获取鼠标光标在视图中的坐标point
然后作为参数发射mouseMovePoint(point)信号.这样若在其它地方编写槽函数与此信号关联,就可以对鼠标移动事件做出响应
*/
void MyGraphicsView::mouseMoveEvent(QMouseEvent* event)
{
	//鼠标移动事件
	QPoint point = event->pos();//QGraphicsView的坐标
	emit mouseMovePoint(point);//发射信号
	QGraphicsView::mouseMoveEvent(event);
}

/*
此事件响应代码里,首先判断是否是鼠标左键按下,然后通过事件的pos()函数获取鼠标光标在视图中的坐标point
然后作为参数发射mouseClicked(point)信号
*/
void MyGraphicsView::mousePressEvent(QMouseEvent* event)
{
	//鼠标左键按下事件
	if (event->button() == Qt::LeftButton)
	{
		QPoint point = event->pos();//QGraphicsView的坐标
		emit mouseClicked(point);//发射信号
	}
	QGraphicsView::mousePressEvent(event);
}



继承QGraphicsLineItem实现自定义ArrowLineItem带箭头直线用于后面的画坐标轴

ArrowLineItem.h代码实现

#ifndef ARROWLINEITEM_H
#define ARROWLINEITEM_H
#include <QGraphicsLineItem>
#include <QPen>
class ArrowLineItem : public QGraphicsLineItem
{
public:
    ArrowLineItem(const QLineF& line, QGraphicsItem* parent = nullptr);
    ArrowLineItem(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem* parent = nullptr);
    void setArrowSize(qreal size) { m_arrowSize = size; update(); }
    qreal arrowSize() const { return m_arrowSize; }
    void setFillArrow(bool fill) { m_fillArrow = fill; update(); }
    bool fillArrow() const { return m_fillArrow; }
protected:
    void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override;
private:
    qreal m_arrowSize;
    bool m_fillArrow;
};
#endif // ARROWLINEITEM_H

ArrowLineItem.cpp代码实现

#include "arrowlineitem.h"
#include <QPainter>
#include <QtMath>

ArrowLineItem::ArrowLineItem(const QLineF& line, QGraphicsItem* parent)
    : QGraphicsLineItem(line, parent), m_arrowSize(10), m_fillArrow(true)
{
}
ArrowLineItem::ArrowLineItem(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem* parent)
    : QGraphicsLineItem(x1, y1, x2, y2, parent), m_arrowSize(10), m_fillArrow(true)
{
}
void ArrowLineItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);

    // 绘制主线
    painter->setPen(pen());
    painter->drawLine(line());

    // 计算箭头方向角度
    QLineF line = this->line();
    double angle = qAtan2(line.dy(), line.dx());

    // 计算箭头的两个侧边点
    QPointF arrowP1 = line.p2() - QPointF(
        qCos(angle - M_PI / 6) * m_arrowSize,
        qSin(angle - M_PI / 6) * m_arrowSize
    );

    QPointF arrowP2 = line.p2() - QPointF(
        qCos(angle + M_PI / 6) * m_arrowSize,
        qSin(angle + M_PI / 6) * m_arrowSize
    );

    // 创建箭头三角形
    QPolygonF arrowHead;
    arrowHead << line.p2() << arrowP1 << arrowP2;

    // 绘制箭头
    if (m_fillArrow) {
        // 填充箭头
        QBrush originalBrush = painter->brush();
        painter->setBrush(painter->pen().color());
        painter->drawPolygon(arrowHead);
        painter->setBrush(originalBrush);
    }
    else {
        // 只绘制轮廓
        painter->drawPolygon(arrowHead);
    }
}

继承自QMainWindow的MainWindow

MainWindow.h代码实现

#pragma once
#include <QtWidgets/QMainWindow>
#include  <QGraphicsScene>
#include  <QLabel>
#include "MyGraphicsView.h"
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
protected:
    void resizeEvent(QResizeEvent* event);//窗口改变事件
private slots:
    void on_mouseMovePoint(QPoint point);
    void on_mouseClicked(QPoint point);
private:
    void iniGraphicsSystem();//初始化场景信息
    void drawSceneCooridate(QGraphicsScene* scene);//绘制Scene的坐标系
    MyGraphicsView* m_graphView;
    QGraphicsScene* m_scene;
    QLabel* labViewCord;//用于显示视图坐标
    QLabel* labSceneCord;//用于显示场景坐标
    QLabel* labItemCord;//用于显示图形项的局部坐标
};

MainWindow.cpp代码实现

#include "MainWindow.h"
#include <QGraphicsItem>
#include <QPen>
#include <QObject>
#include <qmath.h>
#include <QStatusBar>
#include "ArrowLineItem.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    
{   //指定MyGraphicsView对象为MainWindow的CentralWidget
    m_graphView = new MyGraphicsView();
    this->setCentralWidget(m_graphView);    

    labViewCord = new QLabel("View 坐标:");
    labViewCord->setMinimumWidth(150);   
    this->statusBar()->addWidget(labViewCord);

    labSceneCord = new QLabel("Scene 坐标:");
    labSceneCord->setMinimumWidth(150);
    this->statusBar()->addWidget(labSceneCord);

    labItemCord = new QLabel("Item 坐标:");
    labItemCord->setMinimumWidth(150);
    this->statusBar()->addWidget(labItemCord);

    m_graphView->setCursor(Qt::CrossCursor);
    m_graphView->setMouseTracking(true);
    m_graphView->setDragMode(QGraphicsView::RubberBandDrag);

   QObject::connect(m_graphView, SIGNAL(mouseMovePoint(QPoint)), this, SLOT(on_mouseMovePoint(QPoint)));
   QObject::connect(m_graphView, SIGNAL(mouseClicked(QPoint)), this, SLOT(on_mouseClicked(QPoint)));
   
   iniGraphicsSystem();//初始化
   this->showMaximized();//窗口最大化
}

MainWindow::~MainWindow()
{    
}
/*
窗口的resizeEvent()事件的响应函数,以便在窗口变化大小时,显示视图区域的大小和场景信息大小
*/
void MainWindow::resizeEvent(QResizeEvent* event)
{
    //窗口变化的大小的事件
    
    //显示视图区域坐标信息
    QString graphViewInfo = QString::asprintf("Graphics View坐标,左上角总是(0,0),宽度=%d,高度=%d",m_graphView->width(), m_graphView->height());
    this->setWindowTitle(graphViewInfo);
    //Scene场景坐标信息
    QRectF rectF = m_graphView->sceneRect();
    QString sceneInfo = QString::asprintf("QGraphicsView::sceneRect=(Left,Top,Width,Height)=%0.0f,%0.0f,%0.0f,%0.0f", rectF.left(),rectF.top(),rectF.width(),rectF.height());
    QString info = QString("%1.%2").arg(graphViewInfo).arg(sceneInfo);
    this->setWindowTitle(info);
   
}

void MainWindow::iniGraphicsSystem()
{    
    //构建Graphics View的各项
    QRectF rect(-200, -100, 400, 200);//左上角坐标,宽度和高度
    m_scene = new QGraphicsScene(rect);//scene逻辑坐标系定义
   m_graphView->setScene(m_scene);//画一个矩形框,大小等于scene;
    QGraphicsRectItem* item = new QGraphicsRectItem(rect);
    item->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
    QPen pen;
    pen.setWidth(2);
    item->setPen(pen);
    m_scene->addItem(item);

    //画一个位于scene中心的椭圆,测试局部坐标
    QGraphicsEllipseItem* item2 = new QGraphicsEllipseItem(-100, -50, 200, 100);
    item2->setPos(0, 0);
    item2->setBrush(QBrush(Qt::blue));
    //setFlags设置图形项的操作标记,包括可选择、可移动、可获取焦点
    item2->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
    m_scene->addItem(item2);

    //画一个圆,中心位于scene的边缘
    QGraphicsEllipseItem* item3 = new QGraphicsEllipseItem(-50, -50, 100, 100);
    item3->setPos(rect.right(), rect.bottom());
    item3->setBrush(QBrush(Qt::red));
    //
    item3->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
    m_scene->addItem(item3);

    //画Scene场景的坐标轴信息
    drawSceneCooridate(m_scene);

    m_scene->clearSelection();   

   
}
void MainWindow::drawSceneCooridate(QGraphicsScene* scene)
{
    QRectF rectScene=scene->sceneRect();
  
    //画横轴(X轴)
    QPointF xlineStart(rectScene.left(), (rectScene.topLeft().y() + rectScene.bottomLeft().y()) / 2);
    QPointF xlineEnd(rectScene.right(), (rectScene.topLeft().y() + rectScene.bottomLeft().y()) / 2);
    QLineF xline(xlineStart, xlineEnd);
    ArrowLineItem* xArrowLine = new ArrowLineItem(xline);
    xArrowLine->setPos(0, 0);
    xArrowLine->setPen(QPen(Qt::black, 4));
    scene->addItem(xArrowLine);

    //画竖轴(Y轴)
    QPointF ylineStart((rectScene.topLeft().x() + rectScene.topRight().x()) / 2, rectScene.top());
    QPointF ylineEnd((rectScene.topLeft().x() + rectScene.topRight().x()) / 2, rectScene.bottom());
    QLineF yline(ylineStart,ylineEnd );
    ArrowLineItem* yArrowLine = new ArrowLineItem(yline);
    yArrowLine->setPos(0, 0);
    yArrowLine->setPen(QPen(Qt::black, 4));
    scene->addItem(yArrowLine);

    //画X轴文字
    QGraphicsTextItem* xArrowlineText = new QGraphicsTextItem();
    xArrowlineText->setPlainText("X");
    xArrowlineText->setPos(xlineEnd);
    scene->addItem(xArrowlineText);

    //画Y轴文字
    QGraphicsTextItem* yArrowlineText = new QGraphicsTextItem();
    yArrowlineText->setPlainText("Y");
    yArrowlineText->setPos(ylineEnd);
    scene->addItem(yArrowlineText);

    //画中心点文字
    QGraphicsTextItem* centerText = new QGraphicsTextItem();
    centerText->setPlainText("(0,0)");
    centerText->setPos(0,0);
    scene->addItem(centerText);

    //标识是场景的坐标系文字
    QGraphicsTextItem* sceneText = new QGraphicsTextItem();
    sceneText->setPlainText("Scene");
    //sceneText->setFont(QFont())
    sceneText->setPos(rectScene.topLeft());
    scene->addItem(sceneText);

    
}
void MainWindow::on_mouseClicked(QPoint point)
{
    //鼠标单击事件
    QPointF pointScene = m_graphView->mapToScene(point);//转换到Scene坐标
    QGraphicsItem* item = nullptr;
    item = m_scene->itemAt(pointScene, m_graphView->transform());//获取光标下的图形项
    if (item != nullptr)
    {
        QPointF pointItem = item->mapFromScene(pointScene);//将场景坐标转换为图形项的局部坐标
        labItemCord->setText(QString::asprintf("Item 坐标:%.0f,%0.0f", pointItem.x(), pointItem.y()));
    }    
}

void  MainWindow::on_mouseMovePoint(QPoint point)
{
    //鼠标移动事件,point是GraphicView的坐标,物理坐标
    labViewCord->setText(QString::asprintf("View坐标:%d,%d", point.x(), point.y()));
    QPointF pointScene = m_graphView->mapToScene(point);//转到Scene坐标(物理坐标转场景坐标)
    labSceneCord->setText(QString::asprintf("Scene坐标:%0.0f,%0.0f", pointScene.x(), pointScene.y()));
}


结果展示

image

代码程序下载

GaphicsCooridateDemo

posted @ 2025-11-02 13:52  焦涛  阅读(39)  评论(0)    收藏  举报