此篇文章来源于自己在完成一个图片编辑软件而遇到的三个类:QGraphicsScene、QGraphicsPixmapItem、QGraphicsView。此篇文章先介绍QGraphicsView,另外两个类在其他文章,大家可查看博主其他文章。本人能力有限,大家有任何问题可评论区评论,共同学习,共同进步。
一、QGraphicsView介绍
QGraphicsView
是 Qt GUI 库中的一个核心类,它是 Qt 图形视图框架的一部分,主要用于可视化地显示 QGraphicsScene
中的内容。QGraphicsView 提供了一个灵活且可定制的二维图形界面,支持丰富的交互功能,如平移、缩放、旋转,并能够管理和布局复杂的图形场景。
基本特性:
视图与场景分离:
QGraphicsView
类本身负责渲染和显示图形场景 (QGraphicsScene
),而场景则负责存储和管理所有的图形项 (QGraphicsItem
),这些图形项可以是文本、形状、图片等任何可视化元素。视口与坐标系统:
- QGraphicsView 内部有一个视口,视口映射的是场景的部分或全部内容。它可以有自己的坐标系统,并且可以通过调整视口边界或缩放级别来改变显示效果。
- 视口坐标与场景坐标之间存在转换关系,QGraphicsView 提供了一系列方法来处理这种坐标变换。
交互功能:
- 支持鼠标滚轮和键盘事件,用于缩放和平移场景内容。
- 使用
scroll()
和centerOn()
方法可以方便地移动视图焦点。scale()
和rotate()
方法可用于缩放和旋转视图中的场景内容。- 可以通过
dragMode
属性控制用户是否可以拖动场景或视图。缓存机制:
- QGraphicsView 支持多种缓存模式 (
CacheModeFlag
),通过预渲染并缓存部分内容在QPixmap
中,提高复杂场景的重绘效率。适应场景大小:
fitInView()
函数可以自动调整视图大小以适应指定的场景区域,并可以选择保持纵横比或忽略它。事件处理与信号槽:
- QGraphicsView 处理各种图形视图相关的事件,包括但不限于鼠标点击、鼠标移动、鼠标滚轮事件等。
- 它还发出一系列信号,以便与其他组件协同工作,比如当视图区域发生更改时,可以连接相应的槽函数进行响应。
可扩展性:
- 用户可以通过继承
QGraphicsView
并重写其虚函数来自定义视图的行为,例如实现自定义的绘图、鼠标跟踪以及触摸屏交互等高级功能。
总结来说,QGraphicsView
是构建复杂图形用户界面的强大工具,尤其适用于那些需要动态更新、可交互的2D图形化应用程序,如图表绘制、流程图编辑器、游戏地图显示等等。通过结合使用 QGraphicsScene
和 QGraphicsItem
,开发者可以创建出高度定制化、性能优良的图形用户界面组件。
下面是一个简单的 QGraphicsView
使用示例,展示如何创建一个包含一个矩形图形项的基本场景,并将其显示在一个 QGraphicsView
控件中:
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// 创建一个图形场景
QGraphicsScene* scene = new QGraphicsScene();
// 在场景中添加一个矩形图形项
QGraphicsRectItem* rectItem = scene->addRect(50, 50, 100, 100);
rectItem->setBrush(Qt::blue); // 设置填充颜色
rectItem->setPen(QPen(Qt::black)); // 设置边框颜色
// 创建一个图形视图并设置场景
QGraphicsView* view = new QGraphicsView();
view->setScene(scene);
// 设置视图的一些基本属性
view->setRenderHint(QPainter::Antialiasing); // 启用抗锯齿
view->setDragMode(QGraphicsView::ScrollHandDrag); // 允许拖拽视图进行平移
// 显示视图到窗口
QMainWindow window;
window.setCentralWidget(view);
window.show();
return app.exec();
}
- 首先,我们创建了一个
QGraphicsScene
对象,它是所有图形项的容器。 - 然后,在场景中添加了一个
QGraphicsRectItem
(矩形图形项),并设置了填充色和边框色。 - 接着,我们创建了
QGraphicsView
对象,并将其关联到已创建的场景上,这样视图就能显示场景内的内容。 - 设置视图的渲染提示和拖拽模式,增强了视觉效果并允许用户通过鼠标拖拽来平移视图内容。
- 最后,我们将
QGraphicsView
设置为主窗口的中央部件并显示窗口。
运行这段代码,你会看到一个蓝色矩形出现在一个可滚动和平移的视图窗口中。这只是 QGraphicsView
功能的冰山一角,实际上还可以进一步添加更多图形项,实现动画、交互等功能。
二、成员函数
1、QGraphicsView::QGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr)
QGraphicsView::QGraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr)
是 QGraphicsView
类的一个构造函数。在 Qt 框架中,QGraphicsView
是一个用于显示 QGraphicsScene
中图形内容的视图组件。这个构造函数的用途是初始化一个新的 QGraphicsView
实例,并且可以指定一个与之关联的 QGraphicsScene
和父级 QWidget
。
参数说明:
QGraphicsScene *scene
: 这是一个指向QGraphicsScene
对象的指针。如果传入非空指针,则新创建的QGraphicsView
将自动关联到该场景,用于显示场景中的图形元素。如果没有提供场景,或者传入nullptr
,则视图可以稍后通过调用setScene()
方法来关联场景。QWidget *parent
: 这是指向父级QWidget
对象的指针,默认为nullptr
。如果提供了父级窗口部件,那么新建的QGraphicsView
将成为其子部件,并遵循父级的布局管理和生命周期管理规则。
例如,基于上面的构造函数,你可以这样创建一个关联了特定场景的 QGraphicsView
:
// 创建一个图形场景
QGraphicsScene *myScene = new QGraphicsScene();
// 创建一个图形视图并将场景关联到视图上
QGraphicsView *myView = new QGraphicsView(myScene, this);
这里假设 this
是当前的 QWidget
或者 QObject
子类实例,即将 myView
添加为其子部件。
2、QGraphicsView::QGraphicsView(QWidget *parent = nullptr)
QGraphicsView::QGraphicsView(QWidget *parent = nullptr)
是 QGraphicsView
类的一个构造函数,它的主要作用是创建一个新的 QGraphicsView
对象实例。在这个版本的构造函数中,没有直接关联一个 QGraphicsScene
对象。
参数说明:
QWidget *parent
: 这是一个指向父级QWidget
对象的指针,默认值为nullptr
。如果提供了父级窗口部件(即传递非空指针),那么新创建的QGraphicsView
将成为该父窗口部件的子部件,会受到父窗口部件的布局管理,并且会在父窗口部件销毁时自动被删除。如果没有提供父窗口部件,那么QGraphicsView
将成为一个顶级窗口或者可以在之后通过调用setParent()
方法来设置父窗口部件。
使用这个构造函数创建 QGraphicsView
的示例代码如下:
// 创建一个图形视图,暂时不关联任何图形场景
QGraphicsView *myView = new QGraphicsView(this);
// 创建一个图形场景
QGraphicsScene *myScene = new QGraphicsScene();
// 将创建的图形场景关联到视图上
myView->setScene(myScene);
在这个例子中,首先创建了一个没有关联场景的 QGraphicsView
,然后创建了一个 QGraphicsScene
,最后通过调用 setScene()
方法将场景设置给视图。这样,即使在创建视图时不直接关联场景,也能在后续操作中完成关联。
3、void QGraphicsView::invalidateScene(const QRectF &rect = QRectF(), QGraphicsScene::SceneLayers layers = QGraphicsScene::AllLayers)
QGraphicsView::invalidateScene(const QRectF &rect = QRectF(), QGraphicsScene::SceneLayers layers = QGraphicsScene::AllLayers)
是 QGraphicsView
类的一个成员函数,用于通知视图某个区域的场景内容已经改变,需要重新绘制。
参数说明:
const QRectF &rect
: 表示需要无效化的场景区域,默认为空QRectF
,表示整个场景都需要无效化并重新绘制。如果你仅修改了场景中的某个特定区域,可以传递这个区域的矩形坐标,这样只会重新绘制这一部分区域,从而提升性能。QGraphicsScene::SceneLayers layers
: 表示需要无效化的场景层,默认为QGraphicsScene::AllLayers
,表示所有层都需要无效化。也可以指定只无效化某一层或多层,如背景层、前景层等。
当你调用此函数时,视图将会安排在下次事件循环迭代时更新指定区域的显示,确保视图所显示的内容与场景中的实际内容一致。这对于在程序中动态修改了场景内容,尤其是图形项的位置、大小、形状或外观等属性后,是非常有用的。
4、void QGraphicsView::rubberBandChanged(QRect rubberBandRect, QPointF fromScenePoint, QPointF toScenePoint)
void QGraphicsView::rubberBandChanged(QRect rubberBandRect, QPointF fromScenePoint, QPointF toScenePoint)
这个函数签名看起来并不是标准的 Qt QGraphicsView
类的成员函数。通常,在 QGraphicsView
中处理橡皮筋选择(rubber band selection)时,不会有直接名为 rubberBandChanged
的信号或槽函数,并且不接受这种特定形式的参数。
不过,基于函数名和参数,我们可以推测这可能是一个自定义实现或者派生类中的一个函数,用来响应橡皮筋选择框的变化。在这个假设下:
QRect rubberBandRect
: 橡皮筋选择框当前的矩形区域,它代表用户通过鼠标拖拽在视图上画出的选择范围。QPointF fromScenePoint
: 橡皮筋选择开始时在场景坐标系下的点。QPointF toScenePoint
: 橡皮筋选择结束时(或者当前状态)在场景坐标系下的点。
当用户在 QGraphicsView
上拖动鼠标来创建一个选择区域时,这个函数可能是用来实时更新并显示橡皮筋效果,或者是用来记录选择过程中的相关数据。在标准Qt库中,要实现橡皮筋选择的效果,一般会通过重载鼠标事件处理函数并在其中创建一个临时的 QRubberBand
对象,并随着鼠标移动动态调整其位置和大小。
#include <QGraphicsView>
#include <QRubberBand>
#include <QMouseEvent>
class CustomGraphicsView : public QGraphicsView {
Q_OBJECT
public:
CustomGraphicsView(QWidget *parent = nullptr) : QGraphicsView(parent), rubberBand(new QRubberBand(QRubberBand::Rectangle, this)) {}
protected:
void mousePressEvent(QMouseEvent *event) override {
if (event->button() == Qt::LeftButton) {
rubberBandOrigin = event->pos();
rubberBand->setGeometry(QRect(rubberBandOrigin, QSize()));
rubberBand->show();
}
QGraphicsView::mousePressEvent(event);
}
void mouseMoveEvent(QMouseEvent *event) override {
if (rubberBand->isVisible()) {
QRect rect = rubberBand->geometry();
rect.setBottomRight(event->pos());
rubberBand->setGeometry(rect);
// 在这里可以模拟调用 rubberBandChanged 函数,传递当前的矩形区域
rubberBandChanged(rect, rubberBandOrigin.toPoint(), event->pos().toPoint());
}
QGraphicsView::mouseMoveEvent(event);
}
void mouseReleaseEvent(QMouseEvent *event) override {
if (rubberBand->isVisible()) {
rubberBand->hide();
// 处理选择完成逻辑...
}
QGraphicsView::mouseReleaseEvent(event);
}
signals:
// 假设我们添加了一个自定义信号用于通知橡皮筋框变化
void rubberBandChanged(const QRect &rect, const QPointF &from, const QPointF &to);
private:
QPoint rubberBandOrigin;
QRubberBand *rubberBand;
};
// 主要程序中连接信号与槽
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
CustomGraphicsView view;
QObject::connect(&view, &CustomGraphicsView::rubberBandChanged,
[](const QRect &r, const QPointF &f, const QPointF &t) {
// 在这里处理橡胶带矩形变化的实际逻辑
qDebug() << "Rubber band changed:"
<< "Rect:" << r << ", From:" << f << ", To:" << t;
});
// ...
return app.exec();
}
请注意,在上述示例中,我假设了一个自定义信号 rubberBandChanged
,并给出了一个简单的槽函数来处理这个信号。在实际应用中,你可以根据需求在mouseMoveEvent
内部实现自己的 rubberBandChanged
函数逻辑。
5、void QGraphicsView::setupViewport(QWidget *widget)
void QGraphicsView::setupViewport(QWidget *widget)
是 QGraphicsView
类的一个保护成员函数,用于设置视口(viewport)。视口是 QGraphicsView
内部用于显示 QGraphicsScene
内容的区域,它是一个 QWidget
。
当你调用这个函数并传入一个 QWidget
指针时,这个 QWidget
将作为 QGraphicsView
的视口,所有的绘画和事件处理都会在这个视口 widget 上进行。默认情况下,QGraphicsView
会自动创建并管理其视口,但如果你需要自定义视口的行为,例如使用自定义的 QWidget
子类,就可以通过覆盖 setupViewport()
函数来实现。
示例用法:
class MyCustomWidget : public QWidget {
// 自定义视口类的实现...
};
MyCustomWidget customViewport;
void MyGraphicsView::setupViewport()
{
QGraphicsView::setupViewport(&customViewport);
// 可以在这里对 customViewport 进行额外的配置
}
MyGraphicsView::MyGraphicsView(QWidget *parent)
: QGraphicsView(parent)
{
setupViewport(); // 调用自定义的 setupViewport 函数
// ...
}
在上面的例子中,MyGraphicsView
类覆盖了 setupViewport()
函数,将自定义的 MyCustomWidget
设置为了视口。这样,QGraphicsView
所有的渲染和事件处理都将发生在 MyCustomWidget
上。
6、void QGraphicsView::updateScene(const QList<QRectF> &rects)
void QGraphicsView::updateScene(const QList<QRectF> &rects)
是 QGraphicsView
类的一个成员函数,用于通知视图中指定的一组矩形区域需要更新场景内容。当场景中的某些图形元素发生变化时,调用此函数可以确保视图正确地重绘这些变化区域。
该函数接受一个 QList<QRectF>
类型的参数,其中包含了需要更新的矩形区域列表。每个 QRectF
都代表了场景坐标系下的一个区域。
通常情况下,在 QGraphicsScene
中修改或移动项目后,若只关心部分区域的变化,而不是整个场景,调用此方法比调用 QGraphicsView::update()
更高效,因为它允许更精确地控制重绘的范围。
示例用法:
// 假设我们有一些在sceneRect内的矩形区域发生了改变
QList<QRectF> updatedAreas;
updatedAreas << QRectF(0, 0, 100, 100) << QRectF(50, 50, 200, 200); // 这些是需要更新的矩形区域
// 获取视图对象引用
QGraphicsView* view = ...; // 假设已经初始化并连接到了对应的QGraphicsScene
// 更新视图中的指定场景区域
view->updateScene(updatedAreas);
上述代码将指示 QGraphicsView
重新绘制与 updatedAreas
列表中所给定的矩形区域相对应的视图部分。这样,视图只会刷新必要的部分,从而提高性能。
7、void QGraphicsView::updateSceneRect(const QRectF &rect)
void QGraphicsView::updateSceneRect(const QRectF &rect)
是 QGraphicsView
类中的另一个成员函数,不同于 updateScene(const QList<QRectF> &rects)
函数,它主要用于更新视图所显示的整个场景区域(即场景边界)而非仅仅更新特定的矩形区域。
这个函数接收一个 QRectF
类型的参数,该参数定义了一个新的场景矩形区域。当调用此函数时,它会更新视图内部对场景矩形边界的认知,并可能触发视图进行相应的调整和重绘,以适应新场景矩形的大小。
使用场景矩形的主要目的是为了设置视图可视范围的边界以及视图对场景内容的适应策略。例如,当你动态添加或删除场景中的项,或者场景本身的大小发生改变时,调用 updateSceneRect()
方法可以确保视图及时响应这些变化。
// 假设我们改变了场景的大小或位置
QRectF newSceneRect(0, 0, 800, 600); // 新的场景矩形区域
// 获取视图对象引用
QGraphicsView* view = ...; // 假设已经初始化并连接到了对应的QGraphicsScene
// 更新视图的场景矩形
view->setSceneRect(newSceneRect);
// 或者如果只是扩展/收缩而不改变原点,也可以选择:
view->updateSceneRect(view->sceneRect().united(newSceneRect));
// 视图会自动调整以适应新的场景矩形并可能重绘视图内容
请注意,虽然函数名中有“update”,但它并不一定直接导致视图立即重绘,而是更改视图中用来参考的场景区域。实际的重绘操作通常是间接的,由Qt的事件循环根据场景矩形和其他因素来决定何时及如何进行。
8、QGraphicsView::~QGraphicsView()
QGraphicsView::~QGraphicsView()
是C++中 QGraphicsView
类的一个析构函数。在面向对象编程中,析构函数是一种特殊的成员函数,它在对象生命周期结束时自动调用,用于释放对象占用的资源和执行必要的清理工作。
对于 QGraphicsView
类来说,其析构函数的作用主要包括但不限于:
-
删除关联的
QGraphicsScene
对象,如果视图拥有该场景的所有权(即通过setScene(QGraphicsScene*)
设置了场景且采用的是父对象管理子对象的内存策略)。 -
清理与视图相关的任何事件处理器、定时器或者其他系统资源。
-
释放视图本身占用的内存空间。
在编写自定义的 QGraphicsView
子类时,通常不需要显式地覆盖析构函数,除非需要在销毁视图前进行额外的清理工作。在大多数情况下,Qt框架提供的默认析构函数就已经能够妥善地处理资源回收。
9、void QGraphicsView::centerOn(const QPointF &pos)
void QGraphicsView::centerOn(const QPointF &pos)
是 QGraphicsView
类的一个成员函数,它用于将视图中心定位到指定的场景坐标点 pos
上。这意味着当调用此函数后,视图的中心点将对准场景中的 pos
位置,使得该点位于视图的可见区域中央。
示例用法:
// 假设有以下变量:
QGraphicsView* view = ...; // 已经初始化的 QGraphicsView 对象
QGraphicsItem* item = ...; // 场景中的某个 QGraphicsItem 对象
// 获取item在场景坐标系下的中心点位置
QPointF pos = item->scenePos();
// 让视图中心对准item的中心位置
view->centerOn(pos);
在这段代码中,首先获取了场景中的某个图形项的场景坐标位置,然后调用 centerOn()
函数使视图的中心点与该图形项的中心点对齐。这样,无论图形项在场景中的具体位置如何,都能快速地让其居于视图的中央显示。
10、void QGraphicsView::centerOn(qreal x, qreal y)
void QGraphicsView::centerOn(qreal x, qreal y)
是 QGraphicsView
类的一个成员函数,它同样用于将视图的中心点定位到指定的场景坐标 (x, y)
上。这个函数提供了一种更为直接的方式来指定坐标,无需创建一个 QPointF
对象。
示例用法:
// 假设有以下变量:
QGraphicsView* view = ...; // 已经初始化的 QGraphicsView 对象
// 让视图中心对准场景坐标 (100, 200)
view->centerOn(100, 200);
在这个示例中,调用 centerOn(100, 200)
后,视图将滚动到适当的位置,使得场景坐标 (100, 200) 处于视图的中心位置。
11、void QGraphicsView::centerOn(const QGraphicsItem *item)
void QGraphicsView::centerOn(const QGraphicsItem *item)
是QGraphicsView
类的一个成员函数。该函数用于将视图中心对准指定的QGraphicsItem
项目。
具体来说,当你调用这个函数并传入一个QGraphicsItem
对象指针时,图形视图会自动滚动并调整其内容,使得该图形项位于视图的中心位置,便于用户查看或聚焦于该特定的图形项。
示例代码:
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// 创建图形场景和视图
QGraphicsScene scene;
QGraphicsView view(&scene);
// 在场景中添加一个矩形图形项
QGraphicsRectItem* rect = new QGraphicsRectItem(0, 0, 100, 100);
scene.addItem(rect);
// 将视图中心对准刚添加的矩形图形项
view.centerOn(rect);
// 显示视图
view.show();
return app.exec();
}
在这个例子中,我们首先创建了一个QGraphicsScene
(图形场景)和QGraphicsView
(图形视图)。然后我们在场景中添加了一个QGraphicsRectItem
(矩形图形项),最后调用view.centerOn(rect)
将视图的中心点移动到这个矩形图形项的位置上,确保它在视图窗口中居中显示。
12、void QGraphicsView::contextMenuEvent(QContextMenuEvent *event)
void QGraphicsView::contextMenuEvent(QContextMenuEvent *event)
是Qt GUI框架中的一个成员函数,它属于QGraphicsView
类。当用户在QGraphicsView
组件上执行右键点击(或者在某些操作系统上的等效操作,触发上下文菜单事件)时,该函数会被调用。
当你重写此函数时,你可以自定义在该视图上右键点击时的行为,比如弹出自定义的上下文菜单(Context Menu)或其他相应操作。
例如:
void MyGraphicsView::contextMenuEvent(QContextMenuEvent *event)
{
QMenu menu(this);
QAction *actionZoomIn = menu.addAction(tr("Zoom In"));
QAction *actionZoomOut = menu.addAction(tr("Zoom Out"));
connect(actionZoomIn, &QAction::triggered, this, [=]{ zoomIn(); });
connect(actionZoomOut, &QAction::triggered, this, [=]{ zoomOut(); });
menu.exec(event->globalPos());
}
在这个例子中,当用户在MyGraphicsView
上右键点击时,会弹出一个包含“放大”和“缩小”选项的上下文菜单。当用户选择这些动作时,对应的zoomIn()
和zoomOut()
函数会被调用以实现相应的功能。
13、void QGraphicsView::dragEnterEvent(QDragEnterEvent *event)
void QGraphicsView::dragEnterEvent(QDragEnterEvent *event)
是Qt GUI框架中 QGraphicsView
类的一个重载事件处理函数。当拖拽操作中的数据进入 QGraphicsView
视图的有效范围时,这个函数会被调用。
在自定义 dragEnterEvent
函数时,您可以决定是否接受这次拖拽操作,以及设定拖拽的数据格式和操作类型。例如:
void MyGraphicsView::dragEnterEvent(QDragEnterEvent *event)
{
// 检查拖拽的数据是否有我们感兴趣的内容类型
if (event->mimeData()->hasFormat("application/x-qgraphicsitem")) {
// 设置拖拽动作的接受策略
event->acceptProposedAction();
} else {
// 拒绝此次拖拽操作
event->ignore();
}
}
在这个例子中,我们检查了拖拽的数据是否携带 "application/x-qgraphicsitem"
类型,如果是,则接受拖拽操作,否则忽略。通过这种方式,您可以定制您的 QGraphicsView
对拖拽事件的响应行为。
14、void QGraphicsView::dragLeaveEvent(QDragLeaveEvent *event)
void QGraphicsView::dragLeaveEvent(QDragLeaveEvent *event)
是Qt框架中的一个事件处理函数,它是 QGraphicsView
类的一个重载方法。当拖拽的对象离开 QGraphicsView
视图的有效区域时,这个函数会被调用。
在自定义这个函数时,通常用来清理在 dragEnterEvent
或 dragMoveEvent
中设置的一些临时状态,例如恢复视图或场景的原始样式,或者取消任何正在等待的拖拽操作指示。
例如:
void MyGraphicsView::dragLeaveEvent(QDragLeaveEvent *event)
{
// 清理操作,如隐藏拖拽过程中的临时指示器
myDragIndicatorItem->setVisible(false);
event->accept();
}
在上面的示例中,当拖拽事件离开视图时,隐藏了一个可能用于指示当前拖拽位置的临时图形项(myDragIndicatorItem
)。调用 event->accept()
表示已处理此事件,不再需要进一步传递给其它事件处理器。