图形视图框架提供了一个基于图形项的模型视图编程方法,主要由场景、视图和图形项三部分组成,这三部分分别由QGraphicsScene、QGraphicsView和QGraphicsItem这三个类来表示。
场景QGraphicsScene
QGraphicsScene类提供绘图场景(Scene)。场景是不可见的,是一个抽象的管理图形项的容器,可以向场景添加图形项,获取场景中的某个图形项等。
场景具有以下功能:
- 提供用于管理大量图形项的高速接口函数
- 传播事件到每个图形项spa
- 管理图形项的状态,好比选择和处理焦点设计
- 提供无变换的渲染功能,主要用于打印3d
一个场景分为3层:图形项层(ItemLayer)、前景层(ForegroundLayer)、背景层(BackgroundLayer)。场景的绘制老是从背景层开始,而后是图形项层,最后是前景层。前景层和背景层均可以使用QBrush进行填充,好比使用渐变和贴图等,也可以通过重新实现drawBackground()和drawForeground()函数来实现自定义的背景和前景。
可以通过**QGraphicsScene::addItem()可以添加一个图元到场景中。图元可以通过多个函数进行检索。通过QGraphicsScene::items()**和一些重载函数可以返回和点、矩形、多边形或向量路径相交的所有图元。**QGraphicsScene::itemAt()**返回指定点的最顶层图元。
QGraphicsScene scene;
QGraphicsRectItem *rect = scene.addRect(QRectF(0, 0, 100, 100))
QTransform transform;;
QGraphicsItem *item = scene.itemAt(50, 50, transform);
// item == rect
QGraphicsScene的事件传播体系结构安排将场景事件传递到项目,并管理项目之间的传播。如果场景在某个位置接收到鼠标按下事件,则场景将事件传递到该位置的任何项目。
类方法
函数 | 功能 |
---|---|
void QGraphicsScene::addItem(QGraphicsItem *item) | 添加子项目 |
void QGraphicsScene::removeItem(QGraphicsItem *item) | 移除子项目 |
QGraphicsLineItem *QGraphicsScene::addLine() | 添加直线项 |
QGraphicsEllipseItem *addEllipse() | 添加椭圆项 |
QGraphicsPathItem *QGraphicsScene::addPath(const QPainterPath &path, const QPen &pen = QPen(), const QBrush &brush = QBrush()) | 添加路径项 |
QGraphicsPixmapItem *QGraphicsScene::addPixmap(const QPixmap &pixmap) | 添加图片项 |
QGraphicsRectItem *addRect() | 添加矩形项 |
QGraphicsTextItem *QGraphicsScene::addText(const QString &text, const QFont &font = QFont()) | 添加文本项 |
QGraphicsPolygonItem *addPolygon(const QPolygonF &polygon, const QPen &pen = QPen(), const QBrush &brush = QBrush()) | 添加多边形项 |
QGraphicsProxyWidget *QGraphicsScene::addWidget(QWidget *widget, Qt::WindowFlags wFlags = Qt::WindowFlags()) | 添加装置项 |
QBrush backgroundBrush() const | 设置场景背景笔刷 |
void clearFocus() | 清除焦点 |
QList<QGraphicsItem *> collidingItems(const QGraphicsItem *item, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const | 获取与选定项目包含或相交的项 |
QGraphicsItemGroup *QGraphicsScene::createItemGroup(const QList<QGraphicsItem *> &items) | 将一些项添加到一个分组中,并返回该分组的指针 |
void QGraphicsScene::destroyItemGroup(QGraphicsItemGroup *group) | 拆除分组 |
void setFont(const QFont &font) | 设置字体 会触发 FontChange 信号 |
QBrush foregroundBrush() const | 获取前景笔刷 |
qreal height() const | 获取场景的高度 |
qreal width() const | 获取场景的宽度 |
bool isActive() const | 查看场景是否是活动的 |
QGraphicsItem *itemAt() | 返回位置为 QPoint 的项,如果有多项,则返回最上面那个项 |
QList<QGraphicsItem *> items(Qt::SortOrder order = Qt::DescendingOrder) const | 返回场景中的所有项 |
QList<QGraphicsItem *> items(const QPointF &pos, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape, Qt::SortOrder order = Qt::DescendingOrder, const QTransform &deviceTransform = QTransform()) const | 返回场景中的所有与该点相交的项 |
QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape, Qt::SortOrder order = Qt::DescendingOrder, const QTransform &deviceTransform = QTransform()) const | 返回有Rect包含或与Rect相交的所有项的列表、这些项按堆叠顺序排列(第一项是最上面的项) |
QGraphicsItem *mouseGrabberItem() const | 返回场景中正在被鼠标抓取的项目(正在拖动的项目?) |
void removeItem(QGraphicsItem *item) | 移除所有项目 |
void setSceneRect(const QRectF &rect) | 设置场景范围 |
bool QGraphicsScene::sendEvent(QGraphicsItem *item, QEvent *event) | 事件过滤器 |
视图QGraphicsView
QGraphicsView提供了视图部件,它用来使场景中的内容可视化。
能够链接多个视图到同一个场景来为相同的数据集提供多个视口。视图部件是一个可滚动的区域,它提供了一个滚动条来浏览大的场景。
视图接收键盘和鼠标输入并转换为场景事件,并进行坐标转换后传送给可视场景。
QGraphicsScene scene;
myPopulateScene(&scene);
QGraphicsView view(&scene);
view.show();
类方法
函数 | 功能 |
---|---|
QGraphicsItem *itemAt(const QPoint &pos) const | 返回位置为 QPoint 的项,如果有多项,则返回最上面那个项 |
QList <QGraphicsItem *> items() const | 返回关联场景中的所有项 |
void setGraphicsEffect(QGraphicsEffect *effect) | 设置图形效果 |
QList<QGraphicsItem *> items(const QRect &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const | 返回有Rect包含或与Rect相交的所有项的列表、这些项按堆叠顺序排列(第一项是最上面的项) |
QList<QGraphicsItem *> items(int x, int y, int w, int h, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const | 返回有Rect包含或与Rect相交的所有项的列表、这些项按堆叠顺序排列(第一项是最上面的项) |
QGraphicsScene *scene() const | 返回当前视图中的场景,如过没有,返回nullptr |
void setSceneRect(const QRectF &rect) | 设置场景的可视化区域 (超出的部分,会出现滚动条) |
void setSceneRect(qreal x, qreal y, qreal w, qreal h) | 设置场景的可视化区域 (超出的部分,会出现滚动条) |
void setAlignment(Qt::Alignment alignment) | 设置场景在视图中的对齐方式,默认为居中对齐 |
QBrush backgroundBrush() const | 获取视图中的场景背景笔刷 |
void setBackgroundBrush(const QBrush &brush) | 设置视图中的场景背景笔刷 |
void setDragMode(QGraphicsView::DragMode mode) | 设置场景可否被鼠标左键拖动,默认无法被拖动 |
void setForegroundBrush(const QBrush &brush) | 设置前景笔刷 |
bool isInteractive() const | 是否运行场景交互(响应鼠标事件) |
void setScene(QGraphicsScene *scene) | 设置场景 |
void setInteractive(bool allowed) | 设置运行场景交互(响应鼠标事件) |
QBrush foregroundBrush() const | 获取前景笔刷 |
图形项QGraphicsItem
QGraphicsItem就是一些基本的图形元件,是所有图形项的基类。
- QGraphicsEllipseItem 提供椭圆项
- QGraphicsLineItem 提供直线项
- QGraphicsPathItem 提供任意路径项
- QGraphicsPixmapItem 提供pixmap项
- QGraphicsPolygonItem 提供多边形项
- QGraphicsRectItem 提供矩形项
- QGraphicsSimpleTextItem 提供简单的文本标签项
- QGraphicsTextItem 提供高级文本浏览器项
可以通过调用setVisible()来设置项是否应可见(即,绘制和接受事件)。隐藏项目也会隐藏其子项。同样,可以通过调用setEnabled()来启用或禁用项。如果禁用某个项目,它的所有子项也将被禁用。默认情况下,项目既可见又启用。若要切换是否选择项,请首先通过设置itemIsSelectable标志启用选择,然后调用setSelected()。通常,选择由场景切换,这是用户交互的结果。
class SimpleItem : public QGraphicsItem
{
public:
QRectF boundingRect() const override
{
qreal penWidth = 1;
return QRectF(-10 - penWidth / 2, -10 - penWidth / 2,
20 + penWidth, 20 + penWidth);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget) override
{
painter->drawRoundedRect(-10, -10, 20, 20, 5, 5);
}
};
QGraphicsItem还支持以下功能:
- 支持鼠标事件响应,包括鼠标按下、移动、释放和双击事件,以及鼠标悬停事件、滚轮事件和上下文菜单事件;
- 键盘输入焦点和按键事件;
- 支持拖放操作;
- 分组,通过父子关系,通过QGraphicsItemGroup;
- 碰撞检测
碰撞检测有两种方式:
- 重载shape()返回项的准确形状,并依赖collapsWithItem()的默认实现来进行形状交集。如果形状复杂的话,这可能会很贵(指性能消耗)。
- 重载collistWithItem()提供自己的自定义项和形状冲突算法。
可以调用contains()函数来确定项是否包含点。此函数也可以由项重新实现。contains()的默认行为基于调用shape()。
主要函数功能说明
分组 | 函数 | 功能 |
---|---|---|
属性设置 | void setFlags(QGraphicsItem::GraphicsItemFlags flags) | 设置图形项的操作属性,例如,可选择,可移动等 |
void setOpacity(qreal opacity) | 设置透明度 | |
void setGraphicsEffect(QGraphicsEffect *effect) | 设置图形效果 | |
void setSelected(bool selected) | 图形项是否被选中 | |
void setData(int key, const QVariant &value) | 用户自定义数据 | |
坐标 | void setX(qreal x) | 图形项的X坐标 |
void setY(qreal y) | 图形项的Y坐标 | |
void setZValue(qreal z) | 图形项的Z值,Z值控制图形项的叠放次序 | |
void setPos(const QPointF &pos) | 图形项在父项中的位置 | |
void setPos(qreal x, qreal y) | 图形项在父项中的位置 | |
QPointF scenePos() const | 返回图形项在场景中的坐标,相当于调用mapToScene(0, 0) | |
坐标变化 | void resetTransform() | 复位坐标系,取消所有坐标变换 |
void setRotation(qreal angle) | 旋转一定角度,参数为正数时表示顺时针旋转 | |
void setScale(qreal factor) | 按比例缩放,缺省值为1 | |
坐标映射 | QPointF mapFromItem() | 将另一个图形项的一个点映射到本图形项的坐标系 |
QPointF mapFromParent() | 将父项的一个点映射到本图形项的坐标系 | |
QPointF mapFromScene() | 将场景中的一个点映射到本图形项的坐标系 | |
QPointF mapToItem() | 将本图形项内的一个点映射到另一个图形项的坐标系 | |
QPointF mapToParent() | 将本图形项内的一个点映射到父项坐标系 | |
QPointF mapToScene() | 将本图形项内的一个点映射到场景坐标系 |
参考:https://blog.csdn.net/kenfan1647/article/details/116991074
坐标系统
QGraphicsView有三个有效的坐标系,图形坐标,场景坐标,视图坐标。场景坐标等价于QPainter的逻辑坐标,一般以场景的中心为原点;视图坐标与设备坐标相同,是物理坐标,缺省以左上角为原点;图形项坐标是局部逻辑坐标,一般以图件的中心为原点。
图形项坐标
图形项使用自己的局部坐标(Item Coordinates),通常以其中心为(0,0),也是各种坐标变换的中心。图形项的鼠标事件的坐标是用局部坐标表示的,创建自定义图形项,绘制图形项时只需要考虑其局部坐标,QGaphicsScene和QGraphicsView会自动进行坐标转换。
一个图形项的位置是其中心点在父坐标系统中的坐标,对于没有父图形项的图形项,其父对象就是场景,图形项的位置就是在场景中的坐标。如果一个图形项的位置还是其他图形项的父项,父项进行坐标变化时,子项也做同样的坐标变换。
视图坐标
视图坐标(view Coordinates)就是窗口界面的物理坐标,单位是像素。视图坐标只与widget或视口有关,而与观察的场景无关。QGraphicsView视口的左上角坐标总是(0,0)。
所有的鼠标事件,拖放事件的坐标是由视图坐标定义的,然后用户需要讲这些坐标映射为场景坐标,以便和图形交互。
场景坐标
场景是所有图形项的基础坐标,场景坐标描述了每个顶层图形项的位置创建场景时可以定义场景矩形区的坐标范围,例如:
scene=new QGraphicsScene(-400,-300,800,600);
这样定义的scene是左上角坐标为(-400,-300),宽度为800,高度为600的矩形区域,单位是像素。
每个图形项在场景里都有一个位置坐标,由函数QGraphicsItem::scenePos()给出;还有一个图形项边界矩形,由QGraphicsItem::sceneBoundingRect()函数给出。边界矩形可以使QGraphicsScene::changed()信号,参数是一个场景的矩形列表,表示发生变化的矩形区。
坐标映射
在场景中操作图形项时,进行场景到图形项,图形项到图形项,或视图到场景之间的坐标变换是比较有用的,即坐标映射(Coordinate Mapping).例如,在QGraphicsView的视口上单击鼠标时,通过函数QGraphicsView::mapToScene()可以将视图坐标映射为场景坐标,然后用QGraphicsScene::itemAt()函数可以获取场景中鼠标光标处的图形项。
事件处理与传播
图形视图框架中的事件都是先由视图进行接收,而后传递给场景,再由场景传递给相应的图形项。而对于键盘事件,它会传递给得到焦点的图形项,可使用QGraphicsScene类的setFocusItem()函数或者图形项自身调用setFocus()函数来设置焦点图形项。默认的,若是场景没有得到焦点,那么全部的键盘事件都会被丢弃。场景中的图形项得到了焦点,场景也会自动得到焦点。
对于鼠标悬停效果,QGraphicsScene会发送悬停事件。如果一个项接受悬停事件(请参见QGraphicsItem::acceptHoverEvents()),则当鼠标进入其区域时,它将收到一个QGaphicsSceneHoverCenter事件。当鼠标继续在项目区域内移动时,QGraphicsScene将向其发送GraphicsSceneHoverMove事件。当鼠标离开项目区域时,项目将收到一个GraphicsSceneHoverLeave事件。
- contextMenuEvent()处理上下文菜单事件。
- focusInEvent()和focusOutEvent()处理焦点进入和退出事件。
- hoverEnterEvent()、hoverMoveEvent()和hoverLeaveEvent()处理hoverEnter、move和leave事件。
- inputMethodEvent()处理输入事件,以获得辅助功能支持,如中文输入法获取输入内容。
- keyPressEvent()和keyReleaseEvent()处理按键和释放事件。
- mousePressEvent()、mouseMoveEvent()、mouseReleaseEvent()和 mouseDoubleClickEvent()处理鼠标按下、移动、释放、单击和双击事件。
示例:
main.cpp
#include <QApplication>
#include "myitem.h"
#include "myview.h"
#include <QTime>
int main(int argc, char* argv[ ])
{
QApplication app(argc, argv);
qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
QGraphicsScene scene;
scene.setSceneRect(-200, -150, 400, 300);
for (int i = 0; i < 5; ++i) {
MyItem *item = new MyItem;
item->setColor(QColor(qrand() % 256, qrand() % 256, qrand() % 256));
item->setPos(i * 50 - 90, -50);
scene.addItem(item);
}
MyView view;
view.setScene(&scene);
view.setBackgroundBrush(QPixmap("../myView/background.png"));
view.show();
return app.exec();
}
myitem.h
#include <QGraphicsItem>
class MyItem : public QGraphicsItem
{
public:
MyItem();
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
void setColor(const QColor &color) { brushColor = color; }
private:
QColor brushColor;
protected:
void keyPressEvent(QKeyEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
};
myitem.cpp
#include "myitem.h"
#include <QPainter>
#include <QCursor>
#include <QKeyEvent>
#include <QGraphicsSceneHoverEvent>
#include <QGraphicsSceneContextMenuEvent>
#include <QMenu>
MyItem::MyItem()
{
brushColor = Qt::red;
setFlag(QGraphicsItem::ItemIsFocusable);
setFlag(QGraphicsItem::ItemIsMovable);
setAcceptHoverEvents(true);
}
QRectF MyItem::boundingRect() const
{
qreal adjust = 0.5;
return QRectF(-10 - adjust, -10 - adjust,
20 + adjust, 20 + adjust);
}
void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
QWidget *)
{
if (hasFocus()) {
painter->setPen(QPen(QColor(255, 255, 255, 200)));
} else {
painter->setPen(QPen(QColor(100, 100, 100, 100)));
}
painter->setBrush(brushColor);
painter->drawRect(-10, -10, 20, 20);
}
// 鼠标按下事件处理函数,设置被点击的图形项得到焦点,并改变光标外观
void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *)
{
setFocus();
setCursor(Qt::ClosedHandCursor); //设置光标为手握下的形状
}
// 键盘按下事件处理函数,判断是不是向下方向键,若是是,则向下移动图形项
void MyItem::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Down)
moveBy(0, 10);
}
// 悬停事件处理函数,设置光标外观和提示
void MyItem::hoverEnterEvent(QGraphicsSceneHoverEvent *)
{
setCursor(Qt::OpenHandCursor); //设置光标为手张开的形状
setToolTip("I am item");
}
// 右键菜单事件处理函数,为图形项添加一个右键菜单
void MyItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
{
QMenu menu;
QAction *moveAction = menu.addAction("move back");
QAction *selectedAction = menu.exec(event->screenPos());
if (selectedAction == moveAction) {
setPos(0, 0);
}
}
myview.h
#include <QGraphicsView>
class MyView : public QGraphicsView
{
Q_OBJECT
public:
explicit MyView(QWidget *parent = 0);
protected:
void keyPressEvent(QKeyEvent *event);
};
myview.cpp
#include "myview.h"
#include <QKeyEvent>
MyView::MyView(QWidget *parent) :
QGraphicsView(parent)
{
}
void MyView::keyPressEvent(QKeyEvent *event)
{
switch (event->key())
{
case Qt::Key_Plus :
scale(1.2, 1.2);
break;
case Qt::Key_Minus :
scale(1 / 1.2, 1 / 1.2);
break;
case Qt::Key_Right :
rotate(30);
break;
}
QGraphicsView::keyPressEvent(event);//必定要加上这个,不然在场景和图形项中就没法再接收到该事件了
}