图形视图框架提供了一个基于图形项的模型视图编程方法,主要由场景、视图和图形项三部分组成,这三部分分别由QGraphicsScene、QGraphicsView和QGraphicsItem这三个类来表示。
QGraphicsScene类提供绘图场景(Scene)。场景是不可见的,是一个抽象的管理图形项的容器,可以向场景添加图形项,获取场景中的某个图形项等。
场景具有以下功能:
一个场景分为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 |
获取与选定项目包含或相交的项 |
QGraphicsItemGroup *QGraphicsScene::createItemGroup(const QList |
将一些项添加到一个分组中,并返回该分组的指针 |
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 |
返回场景中的所有项 |
QList |
返回场景中的所有与该点相交的项 |
QList |
返回有Rect包含或与Rect相交的所有项的列表、这些项按堆叠顺序排列(第一项是最上面的项) |
QGraphicsItem *mouseGrabberItem() const | 返回场景中正在被鼠标抓取的项目(正在拖动的项目?) |
void removeItem(QGraphicsItem *item) | 移除所有项目 |
void setSceneRect(const QRectF &rect) | 设置场景范围 |
bool QGraphicsScene::sendEvent(QGraphicsItem *item, QEvent *event) | 事件过滤器 |
QGraphicsView提供了视图部件,它用来使场景中的内容可视化。
能够链接多个视图到同一个场景来为相同的数据集提供多个视口。视图部件是一个可滚动的区域,它提供了一个滚动条来浏览大的场景。
视图接收键盘和鼠标输入并转换为场景事件,并进行坐标转换后传送给可视场景。
QGraphicsScene scene;
myPopulateScene(&scene);
QGraphicsView view(&scene);
view.show();
函数 | 功能 |
---|---|
QGraphicsItem *itemAt(const QPoint &pos) const | 返回位置为 QPoint 的项,如果有多项,则返回最上面那个项 |
QList |
返回关联场景中的所有项 |
void setGraphicsEffect(QGraphicsEffect *effect) | 设置图形效果 |
QList |
返回有Rect包含或与Rect相交的所有项的列表、这些项按堆叠顺序排列(第一项是最上面的项) |
QList |
返回有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就是一些基本的图形元件,是所有图形项的基类。
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还支持以下功能:
碰撞检测有两种方式:
可以调用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事件。
示例:
main.cpp
#include
#include "myitem.h"
#include "myview.h"
#include
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
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
#include
#include
#include
#include
#include
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
class MyView : public QGraphicsView
{
Q_OBJECT
public:
explicit MyView(QWidget *parent = 0);
protected:
void keyPressEvent(QKeyEvent *event);
};
myview.cpp
#include "myview.h"
#include
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);//必定要加上这个,不然在场景和图形项中就没法再接收到该事件了
}