QT 中的 Graphics View 系统

这个框架是一个相对成熟的渲染引擎的上层框架,通常也可以会叫做 Scene - View。

在QT中,GraphicsView是一个与QWdiget系列一点点不一样的系统。这个系统主要由下面几个框架类构成:QGraphicsView,  QGraphicsScene, QGraphicsItem

Image(149) Image(150)

Image(151)

QGrahpicsView是从原生的QWidget继承过来,QGraphics-View系统中,他承担的也是视口的指责,Viewport,Viewport相当于显示设备的一个矩形区域。

QGraphicsScene 是一个管理器,用来管理所有的QGraphicsItem,包括根据坐标查询Item,排序Item,绘制Item等。

QGraphicsItem 是所有可见的元件。一个完整的UI界面,由各种QGraphicsItem组合起来。这些QGraphicsItem之间由一棵多叉树组织。

要架构一个基于GraphicsView的UI库,需要做如下的三件事情:

  • 我们需要先由一个 QGraphicsView, 这个是UI显示的地方,也就是装满可见原色的Scene,
  • 然后需要一个QGraphicsScene 用来管理所有可见的界面元素,
  • 要实现UI功能,我们需要用各种从QGraphicsItem拼装成UI控件,并赋予他控件的逻辑。

对应到Duifw,我们的DuiFrameWindow 就是从 QGraphicsView 继承过来的,也就是说它是一个独立的视口,相当于操作系统的一个窗口.

在DuiFrameWindow 中 void DuiFrameWindow::_initScene(), 函数中,我们为每一个 DuiFrameWindow 创建了一个标准的 QGraphicsScene,  并且在 scene 上我们 add 了一个 DuiRootItem,然后我们就在 rootitem上构建我们的所有UI控件,  从  DuiRootItem: m_rootItem 上 我们add 三个子Item ,分别是 DuiBackgroundItem:m_backgroundItem, DuiTitlebar:m_titlebar, DuiSpacerItem:m_contentItem, 然后我们定义了一个rootWidget() 函数 用来返回  DuiSpacerItem:m_contentItem, 后面通过xml文件配置的一个窗口,就通过访问rootWidget(), 把所有DuiFrameWindow的孩子节点都以 这个rootWidget为 父节点,如下代码:

createChild(child->child(index), frame->rootWidget());

这样就构建了一个QGraphicsItem的多叉树。前面有一个细节,没有列出,就是我们的DuiWidget, 是一个什么, 我们的DuiWidget是从QGraphicsWidget,继承而来的,也就是说它本身是一个符合 QGraphicsScene - QGraphicsItem体系的可视元件。

了解了的GraphicsView的构成框架后,对于QGraphicsItem的消息来源,以及这个框架中的消息走向能够大概有了猜测了。

QGraphicsView 会把  原来的QWidget中的各种QHoverEvent, QInputEvent,......等等一些列命令,转换成各种 QGraphicsSceneEvent ,QGraphicsSceneMouseEvent,QGraphicsSceneWheelEvent......,,,然后传递给当前View绑定的QGraphicsScene。然后通过Scene传递给 QGraphicsitem,QGraphicsScene 有如下的一堆消息处理接口:

Image(152)

通过上述的分发,最后消息会通过下面的接口传递到具体的item,所有的scene消息都是走接口:

Image(153)

这样就完成了从windows消息到 QGraphicsItem 的消息处理的流程。

到此。QGraphics - View 框架就简述完成了。

 

//***************************************************
Qt图形简单绘制  -贪吃蛇小游戏

在初步了解Qt绘图相关的基础知识后,我们将开始学习用Qt做一个贪吃蛇小游戏。

不过在开始游戏之前,我们首先要了解Qt中的一个重要框架,叫Graphics View。

Graphics View是一个很常用的框架(贪吃蛇,俄罗斯方块等用Graphics View都是最好的方法...),甚至Linux的KDE桌面视图都是用它编写的。

Graphics View 分为三个部分:元素(item),场景(scene)以及视图(view)。

我们举一个很简单的例子:我们看到森林里有很多树,那么这个森林就是场景(scene),树是元素(item),你的眼睛就是视图(view)。我们可以在场景里种树,也就是把item通过add的方法置于场景,遮阳,在我们的视图(view)里面就会出现一棵树了。而实际上,我们也未必会一直站在同一个地方看树;当我们移动的时候,整个森林(scene)的场景在我们的视图(view)里边变化。而这在程序中对应着二维图的旋转,缩放等功能。

我们来看下面一这个代码:

 

 
  1. int main(int argc, char *argv[])

  2. {

  3. QApplication a(argc, argv);

  4. MainWindow w;

  5. w.show();

  6.  
  7. QGraphicsScene scene;

  8. scene.addRect(0,0,150,150);

  9. scene.setBackgroundBrush(QBrush(Qt::gray));

  10.  
  11. QGraphicsView view(&scene);

  12. view.rotate(30);

  13. view.setWindowTitle("Graphics View");

  14. view.resize(200, 200);

  15. view.show();

  16. return a.exec();

  17. }

在这里,我们构建了一个场景(scene)和一个视图(view),同时,我们在视图上做了旋转,那么它输出的结果就是一个旋转的正方形,如下图:

 

这种方式在OpenGL以及其他三维软件API中也很常用,想想大家完第一视角的游戏(比如‘我的世界’),无非就是建立一个场景(世界),修改场景中的元素(比如盖房子),然后移动视角,遮阳在场景中就能呈现我们的世界。

大家也可以通过以上代码,简单做几个小程序,或者采用简单的形状,通过代码画一幅简单的画。

 

GraphicsView编程

一、QGraphicsScene

1、QGraphicsScene

QGraphicsScene继承自QObject,是一个管理图元的容器,与QGraphicsView合用可以在2D屏幕上显示如线、三角形、文本、自定义图元等图元。

QGraphicsScene是不可见的,只用于管理图元。为了查看场景,需要创建一个视图组件。

一个场景分为三个层:图元层、前景层和背景层。场景的绘制总是从背景层开始,然后是图形项层,最后是前景层。

2、事件处理与传播

    QGraphicsScene的责任之一是传播来自视图的事件。要发送一个事件到场景,需要构造一个继承自QEvent的事件,使用QApplication::sendEvent()函数发送事件。event()函数负责派发事件到各个图元。常用的事件会被便利事件处理函数处理,如鼠标按下事件会被mousePressEvent()函数处理。

    按键事件会被派发到焦点图元。为了设置焦点图元,可以调用setFocusItem()函数,或是图元自身调用QGraphicsItem::setFocus()函数。调用focusItem()函数可以获取当前的焦点图元。为了兼容图形组件,场景维护着自己的焦点信息。默认场景并没有焦点,并且所有的按键事件会别丢弃。如果setFocus()函数被调用,或是场景中一个图元获得了焦点,场景会自动获得焦点。如果场景有焦点,hasFocus()函数会返回true,按键事件会被发送到焦点图元。如果场景失去了焦点,而图元有焦点(如调用clearFocus()函数),场景会维护图元的焦点信息,一旦场景重新获得焦点,会确保最后一个有焦点的图元获得焦点。

    对于悬停效果,QGraphicsScene会派发悬停事件,如果某个图元接受了悬停事件(调用QGraphicsItem::acceptHoverEvents()),当鼠标进入图元的区域时,图元会接收到一个GraphicsSceneHoverEnter事件。当鼠标继续在图元内部移动时,QGraphicsScene会发送GraphicsSceneHoverMove事件。当鼠标离开图元的区域时,图元会收到一个GraphicsSceneHoverLeave事件。

    所有鼠标事件会被传播到当前鼠标获取的图元。如果一个图元接收了鼠标事件,并收到鼠标按下,图元就是场景的鼠标获取图元。这个图元会一直被鼠标获取,直到图元收到一个鼠标释放事件。调用mouseGrabberItem()函数可以知道当前鼠标获取的图元。

场景可以传递来自视图的事件,将事件传递给该点最顶层的图元。如果一个图元要接收键盘事件,那么它必须获得焦点。而且,如果在场景中重写了事件处理函数,那么在该函数的最后必须调用场景默认的事件处理函数,只有这样,图元才能接收到该事件。

A、拖拽事件

复制代码

[virtual protected] void dragEnterEvent(QGraphicsSceneDragDropEvent *event)//拖入事件处理函数

[virtual protected] void dragLeaveEvent(QGraphicsSceneDragDropEvent *event)//拖离事件梳理函数

[virtual protected] void dragMoveEvent(QGraphicsSceneDragDropEvent *event)//拖动事件处理函数

[virtual protected] void dropEvent(QGraphicsSceneDragDropEvent *event)//Drop事件处理函数

//在以上拖拽事件处理函数中的末尾需要调用QGraphicsScene类相应的事件处理函数。

QGraphicsScene::dragEnterEvent(event);

QGraphicsScene::dragLeaveEvent(event);

QGraphicsScene::dragMoveEvent(event);

QGraphicsScene::dropEvent(event);

复制代码

 

B、鼠标事件

复制代码

[virtual protected] void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)//鼠标移动处理函数

[virtual protected] void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)//鼠标按下处理函数

[virtual protected] void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)//鼠标释放处理函数

//在以上鼠标事件处理函数中的末尾需要调用QGraphicsScene类相应的事件处理函数。

QGraphicsScene::mouseMoveEvent(event);

QGraphicsScene::mousePressEvent(event);

QGraphicsScene::mouseReleaseEvent(event);

复制代码

 

3、索引算法

    索引算法,是指在场景中进行图元查找的算法。QGraphicsScene中提供了两种选择,在一个枚举变量QGraphicsScene::ItemIndexMethod中定义,分别是:

    QGraphicsSecne::BspTreeIndex :应用Binary Space Partition tree,适合于大量的静态图元,是默认值。

    QGraphicsScene::NoIndex :不用索引,搜索场景中所有的图元,适合于经常进行图元的添加、移动和删除等操作的情况。

    使用setItemIndexMethod()函数进行索引算法的更改。

4、边界矩形

    图元可以放到场景的任何位置,场景的大小默认是没有限制的。而场景的边界矩形仅用于场景内部进行索引的维护。因为如果没有边界矩形,场景就要搜索所有的图元,然后确定出其边界,这是十分费时的。所以如果要操作一个较大的场景,应该给出它的边界矩形。

    设置边界矩形,可以使用setSceneRect()函数。

5、图元的查找

    场景最大的优势之一就是可以快速的锁定图元的位置,即使有上百万个图元,items()函数也能在数毫秒的时间内锁定一个图元的位置。items()函数有几个重载函数来方便的进行图元的查找。如果在场景的一个点可能重叠着几个图元,可以使用itemAt()函数返回最上面的一个图元。

二、QGraphicsItem

1、自定义QGraphicsItem

    QGraphicsItem是图元的基类。

    自定义图元,首先应该继承QGraphicsItem,然后重写他的两个纯虚公共函数boundingRect()和paint(),boundingRect()函数返回绘制图元大概的区域,paint()函数用来绘制图元内容。

    boundingRect()函数有很多用处,场景在boundingRect()来建立它的图元的index,视图使用boundingRect来剪切可见的图元,在重新绘制图元时候,来决定相互重叠的部分,此外,图元的碰撞检测机制也使用的boundingRect()来提供一个高效的定点,在collidesWithItem()更好的碰撞算法建立在调用函数shape(),shape()函数以QpainterPath类型返回图元的精准的轮廓。

    场景不希望图元的boundingRect()和shape()变化,除非该图元被通告,如果想通过一些方法改变图元的形状,首先应该调用QgraphicsScene()来允许场景QgraphicsScene来刷新它的图元记录。

    图元没有获得焦点时,事件只能从视图传递到场景,不能传递到图元。清除图元的焦点函数为clearFocus()。

2、绘制

    paint()函数被QgrapicsView类调用来绘制图元的内容,图元默认是没有背景或者填充颜色的。在函数中没有被绘制的所有区域都将会发亮,可以调用update()来重绘图元,可以选择传递需要重绘的矩形区域(不是必须的)。取决于图元在视图中是否可见,图元可能会也可能不会重绘,QgraphicsItem里面没有和 Qwidget::repaint()函数等价的图元通过视图来绘制,从父类图元开始,然后是图元自身,以上升的栈的顺序,可以通过调用setZValue()设置图元的栈顺序,通过zValue()来测试,具有低z-values的图元比具有高z-value 的图元先绘制,栈顺序应用于兄弟图元,父类图元总是比子类图元更早绘制。

3、排序

    所有的图元都按照一个已经声明的稳定的顺序来绘制,声明的顺序决定了当在场景中点击鼠标时候,哪个图元最先接受鼠标的输入。通常情况下,不需要担心图元排序的问题,因为所有的图元都按照一个在场景中声明的自然的顺序。
    在一个栈中,子类图元在父类图元的上面,兄弟图元按照插入场景的顺序来入栈,如果你先添加图元A ,然后是图元B,然后是图元C ,栈中的顺序从下往上就是A、B、C。可以调用setZvalue()来设置一个图元的相对于另一个图元向上、向下或者兄弟栈顺序。默认的Z值是0,具有同样的Z值的图元会按照插入的顺序来入栈。可以调用stackBefore()来备份子类图元的列表,直接更正图元的顺序。
    如果想让子类图元在父类图元的后面,也就是先绘制子类图元,然后再绘制父类图元。可以利用函数setFlag()设置ItemStacksBehindParent属性给图元。

4、事件处理

    QgraphicsItem从场景中通过sceneEvent()函数来接受事件,sceneEvent()函数通过一些方便的操作分散大部分事件。

    ContextMenuEvent()函数接受上下文菜单事件, FocusInEvent()和focusOutEvent()函数接受焦点进出事件, hoverEnterEvent()、hoverMoveEvent()、hoverLeaveEvent() 接受鼠标悬浮移动和离开事件。 

    inputMethodEvent()函数处理输入法事件,keyPressEvent()和keyReleaseEvent()事件处理键盘按下和释放事件。

    mousePressEvent()、mouseMoveEvent()、mouseReleaseEvent()、   mouseDoubleClickEvent()处理鼠标按下、移动、释放、双击事件。

    通过安装过滤器,可以为图元过滤一些事件,与QT一般的事件过滤器不一样,一般的过滤器只工作在Qobject及其子类。通过调用 installSceneEventFilter()为图元安装事件过滤器后,被过滤的事件将会被虚函数sceneEventFilter()捕捉 到,可以通过调用函数removeSceneEventFilter()来去除掉事件过滤器。

A、拖拽事件

    GraphicsView框架为视图、场景、图元提供拖拽支持。当视图接收到拖拽事件,GraphicsView框架会将拖拽事件翻译为QGraphicsSceneDragDropEvent事件,再发送到场景,场景接管事件,把事件发送到光标下接受拖拽的第一个图元。

    从图元开始拖拽时,创建一个QDrag对象,传递开始拖拽的QWidget的指针。图元可以同时被多个视图观察,但只有一个视图可以开始拖拽。拖拽在多数情况下是从按下鼠标或是移动鼠标开始的,在mousePressEvent()或mouseMoveEvent()中,可以从事件中得到原始的QWidget指针。

    要在场景中取拖拽事件,需要重新实现QGraphicsScene::dragEnterEvent()和QGraphicsItem子类里任何与特定场景需要的事件处理器。图元也可以通过调用QGraphicsItem::setAcceptDrops()获得拖拽支持,为了处理将要进行的拖拽,需要重新实现QGraphicsItem的dragEnterEvent()、dragMoveEvent()、dropEvent()、dragLeaveEvent() 。

复制代码

[virtual protected] void dragEnterEvent(QGraphicsSceneDragDropEvent *event)

[virtual protected] void dragLeaveEvent(QGraphicsSceneDragDropEvent *event)

[virtual protected] void dragMoveEvent(QGraphicsSceneDragDropEvent *event)

[virtual protected] void dropEvent(QGraphicsSceneDragDropEvent *event)

复制代码

B、鼠标事件

    要在自定义图元类中处理鼠标事件,需要重写QGraphicsItem类中鼠标按下、鼠标移动、鼠标释放的事件。

[virtual protected] void mouseMoveEvent(QGraphicsSceneMouseEvent *event)

[virtual protected] void mousePressEvent(QGraphicsSceneMouseEvent *event)

[virtual protected] void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)

 

5、动画效果

    实现图元的动画效果,也可以在不同的层面进行。如果只想控制一两个图元的动画,一般在场景或视图中实现。但是要是想让一个图元类的多个对象都进行同样的动画,那么我们就可以在图元类的构造函数中进行实现。

复制代码

    //图元可获得焦点
    setFlag(QGraphicsItem::ItemIsFocusable);

    //图元可移动
    setFlag(QGraphicsItem::ItemIsMovable);

    QGraphicsItemAnimation *anim = new QGraphicsItemAnimation;

    //将图元加入动画对象中
    anim->setItem(this);

    //创建长为1秒的时间线
    QTimeLine *timeLine = new QTimeLine(1000);

    //动画循环次数为0,表示无限循环
    timeLine->setLoopCount(0);

    //将时间线加入动画类对象中
    anim->setTimeLine(timeLine);

    //在动画时间的一半时图形项旋转180度
    anim->setRotationAt(0.5,180);

    //在动画执行完时图形项旋转360度
    anim->setRotationAt(1,360);

    //开始动画
    timeLine->start();

复制代码

 

6、移动

    图元的移动,有多种方法实现,可以在视图或场景上控制,但对于不同类型的大量图元,怎样能一起控制呢?在图形视图框架中提供了advance()槽函数,advance()函数在QGraphicsScene和QGraphicsItem中都有定义,在图元类中的原型是advance(int phase)。实现流程是,利用QGraphicsScene类的对象调用QGraphicsScene的advance()函数,会执行两次场景中所有图元的advance(int phase)函数,第一次phase为0,告诉所有图形项即将要移动;第二次phase的值为1,执行移动。

    QTimer timer;

    QObject::connect(&timer, SIGNAL(timeout()),scene, SLOT(advance()));

    timer.start(1000);

    至于图元如何移动,需要重写图元类的advance()函数。

    如果在自定义图元类的构造函数中设置为可移动,则图元可以直接使用鼠标拖拽。

setFlag(QGraphicsItem::ItemIsMovable);

7、图元的坐标转换

    QgraphicsItem支持坐标转换,对于简单的转换,可以调用函数setRotation()或者setScale(),可以传递一个转换矩阵给函数setTransform(),对于一些更复杂的转换,可以通过调用函数setTransformations()来设置一系列组合的转换。
    图元转换从父类到子类进行聚集,因此如果一个父类图元和子类图元都旋转90度,那么子类图元就旋转了180度;如果父类图元和子类图元都放大了2X倍,那么子类图元就被放大4X倍,图元的转换不影响图元的外观,所有和外观有关的函数(例如contains(),update()和所有的映射mapping函数)将会在本地坐标中操作,QgraphicsItem提供函数sceneTransform(),将会返回图元所有的转换矩阵,scenePos()将会返回图元在场景坐标中的位置,重新设置图元的矩阵,调用函数resetTransform()。
    一般的转换回产生一个不同的结果,取决于转换应用的顺序,转换顺序不同得到结果将不同。

8、主要成员函数

    QVariant itemChange(GraphicsItemChange change, const QVariant & value)

    itemChange函数被QGraphicsItem调用用来标识图元的状态改变了,通过重载itemChange函数,可以对自己定义事件响应。参数change是改变的图元的改变状态参数,value是一个新的数据,类型取决于change,change是QGraphicsItem::GraphicsItemChange枚举变量。

    在itemChange函数内部调用函数时候要谨慎,不能在itemChange函数里面调用setPos(),参数change是ItemPositionChange时,setPos()函数将会再次调用itemChange(ItemPositionChange),形成死循环。

    void setFlag(GraphicsItemFlag flag, bool enabled = true)
 
     void setFlags(GraphicsItemFlags flags)
 
    flags设置为图元的属性,如果图元获得了光标,但flags没有使能ItemsFocusable,图元将会丢失光标,当图元被选择,但没有使能ItemsSelectable,图元会自动的失去选择。
     QPainterPath shape () const 

    以QPainterPath返回图元在本地坐标中的形状,形状可以用来做很多事情,包括碰撞侦测,打击测试,还有用来 QGraphicsScene::items() 函数
    默认的函数调用boundingRect()返回一个简单的矩形形状,子类可以重载boundingRect函数,为非矩形的图元返回一个更加精准的形状,例如一个圆形的图元可以选择返回一个椭圆形,用来获得更好的碰撞侦测效果。

三、QGraphicsView

1、QGraphicsView简介

    QGraphicsView继承自QAbstractScrollArea,继承了QWidget的特性。

    QGraphicsView提供了视图窗口部件,使场景的内容可视化。可以给一个场景关联多个视图,从而给一个数据集提供多个视口。视图部件是一个滚动区域,可以提供一个滚动条来显示大型的场景。

2、事件处理

    在图形视图框架中,鼠标键盘等事件是从视图进入的,视图将事件传递给场景,场景再将事件传递给该点的图元,如果该点有多个图元,那么就传给最上面的图元。为了使事件能进一步传播到场景,需要在重新实现事件处理函数时,在其最后将event参数传给默认的事件处理函数。比如重写了视图的鼠标按下事件处理函数,那么就在该函数的最后写上QGraphicsView::mousePressEvent(event);

A、拖拽事件

    在QGraphicView中提供了三种拖拽模式,分别是:

    QGraphicsView::NoDrag //忽略鼠标事件,不可以拖动。

    QGraphicsView::ScrollHandDrag //光标变为手型,可以拖动场景进行移动。

    QGraphicsView::RubberBandDrag // 使用橡皮筋效果,进行区域选择,可以选中一个区域内的所有图元。

 

    可以利用setDragMode()函数进行相应设置。

复制代码

[virtual protected] void dragEnterEvent(QDragEnterEvent *event)

[virtual protected] void dragLeaveEvent(QDragLeaveEvent *event)

[virtual protected] void dragMoveEvent(QDragMoveEvent *event)

[virtual protected] void dropEvent(QDropEvent *event)

//在以上拖拽事件处理函数中的末尾需要调用QGraphicsView类相应的事件处理函数。

QGraphicsView::dragEnterEvent(event);

QGraphicsView::dragLeaveEvent(event);

QGraphicsView::dragMoveEvent(event);

QGraphicsView::dropEvent(event);

复制代码

 

B、鼠标事件

复制代码

[virtual protected] void mouseMoveEvent(QMouseEvent *event)

[virtual protected] void mousePressEvent(QMouseEvent *event)

[virtual protected] void mouseReleaseEvent(QMouseEvent *event)

void setMouseTracking(bool enable)

//在以上鼠标事件处理函数中的末尾需要调用QGraphicsView类相应的事件处理函数。

QGraphicsView::mouseMoveEvent(event);

QGraphicsView::mousePressEvent(event);

QGraphicsView::mouseReleaseEvent(event);

复制代码

 

四、程序实例

1、自定义视图

CustomView.h文件:

复制代码

#ifndef CUSTOMVIEW_H
#define CUSTOMVIEW_H
 
#include <QGraphicsView>
 
class CustomView : public QGraphicsView
{
    Q_OBJECT
public:
    CustomView(QWidget *parent = 0);
protected:
    void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
    void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
    void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE;
    void dragLeaveEvent(QDragLeaveEvent *event) Q_DECL_OVERRIDE;
    void dragMoveEvent(QDragMoveEvent *event) Q_DECL_OVERRIDE;
    void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE;
};
 
#endif // CUSTOMVIEW_H

复制代码

CustomView.cpp文件:

复制代码

#include "CustomView.h"
#include <QDebug>
 
CustomView::CustomView(QWidget *parent):QGraphicsView(parent)
{
}
 
void CustomView::mousePressEvent(QMouseEvent *event)
{
    qDebug() << "CustomView::mousePressEvent";
    QGraphicsView::mousePressEvent(event);
}
 
void CustomView::mouseMoveEvent(QMouseEvent *event)
{
    qDebug() << "CustomView::mouseMoveEvent";
    QGraphicsView::mouseMoveEvent(event);
}
 
void CustomView::mouseReleaseEvent(QMouseEvent *event)
{
    qDebug() << "CustomView::mouseReleaseEvent";
    QGraphicsView::mouseReleaseEvent(event);
}
 
void CustomView::paintEvent(QPaintEvent *event)
{
    qDebug() << "CustomView::paintEvent";
    QGraphicsView::paintEvent(event);
}
 
void CustomView::dragEnterEvent(QDragEnterEvent *event)
{
    qDebug() << "CustomView::dragEnterEvent";
    QGraphicsView::dragEnterEvent(event);
}
 
void CustomView::dragLeaveEvent(QDragLeaveEvent *event)
{
    qDebug() << "CustomView::dragLeaveEvent";
    QGraphicsView::dragLeaveEvent(event);
}
 
void CustomView::dragMoveEvent(QDragMoveEvent *event)
{
    setCursor(Qt::CrossCursor);
    qDebug() << "CustomView::dragMoveEvent";
    QGraphicsView::dragMoveEvent(event);
}
 
void CustomView::dropEvent(QDropEvent *event)
{
    qDebug() << "CustomView::dropEvent";
    QGraphicsView::dropEvent(event);
}

复制代码

2、自定义场景

CustomScene.h文件:

复制代码

#ifndef CUSTOMSCENE_H
#define CUSTOMSCENE_H
 
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPaintEvent>
 
class CustomScene : public QGraphicsScene
{
    Q_OBJECT
public:
    CustomScene(QObject *parent = 0);
protected:
    void mousePressEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE;
    void dragEnterEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;
    void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;
    void dragMoveEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;
    void dropEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;
};
 
#endif // CUSTOMSCENE_H

复制代码

CustomScene.cpp文件:

复制代码

#include "CustomScene.h"
#include <QDebug>
 
CustomScene::CustomScene(QObject *parent):QGraphicsScene(parent)
{
}
 
void CustomScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "CustomScene::mousePressEvent";
    QGraphicsScene::mousePressEvent(event);
}
 
void CustomScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "CustomScene::mouseReleaseEvent";
    QGraphicsScene::mouseReleaseEvent(event);
}
 
void CustomScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "CustomScene::mouseMoveEvent";
    QGraphicsScene::mouseMoveEvent(event);
}
 
void CustomScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    qDebug() << "CustomScene::dragEnterEvent";
    QGraphicsScene::dragEnterEvent(event);
}
 
void CustomScene::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
    qDebug() << "CustomScene::dragLeaveEvent";
    QGraphicsScene::dragLeaveEvent(event);
}
 
void CustomScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
    qDebug() << "CustomScene::dragMoveEvent";
    QGraphicsScene::dragMoveEvent(event);
}
 
void CustomScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    qDebug() << "CustomScene::dropEvent";
    QGraphicsScene::dropEvent(event);
}

复制代码

3、自定义图元

CustomItem.h文件:

复制代码

#ifndef CUSTOMITEM_H
#define CUSTOMITEM_H
 
#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
 
class CustomItem : public QGraphicsItem
{
public:
    CustomItem();
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE;
    QRectF boundingRect() const Q_DECL_OVERRIDE;
protected:
    //鼠标事件
    void mousePressEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE;
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE;
    //拖拽事件
    void dragEnterEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;
    void dragLeaveEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;
    void dragMoveEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;
    void dropEvent(QGraphicsSceneDragDropEvent *event) Q_DECL_OVERRIDE;
private:
    QColor color;
};
 
#endif // CUSTOMITEM_H

复制代码

CustomItem.cpp文件:

复制代码

#include "CustomItem.h"
#include <QDebug>
#include <QPainter>
#include <QCursor>
#include <QPen>
 
CustomItem::CustomItem()
{
    color = Qt::red;
    setFlag(QGraphicsItem::ItemIsFocusable);
    //设置图元为可移动的
    setFlag(QGraphicsItem::ItemIsMovable);
    setAcceptDrops(true);
}
 
void CustomItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    qDebug() << "CustomItem::paint";
    if(hasFocus()) {
        painter->setPen(QPen(QColor(255,255,255,200)));
    } else {
        painter->setPen(QPen(QColor(100,100,100,100)));
    }
    painter->setBrush(color);
    painter->drawRect(-10, -10, 20, 20);
}
 
QRectF CustomItem::boundingRect() const
{
    qreal adjust = 0.5;
    return QRectF(-10 - adjust, -10 - adjust, 20 + adjust, 20 + adjust);
}
 
void CustomItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);
    qDebug() << "CustomItem::mousePressEvent";
    setCursor(Qt::OpenHandCursor);
}
 
void CustomItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    setCursor(Qt::DragMoveCursor);
    qDebug() << "CustomItem::mouseMoveEvent";
}
 
void CustomItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "CustomItem::mouseReleaseEvent";
    setCursor(Qt::ArrowCursor);
}
 
void CustomItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    setCursor(Qt::CrossCursor);
    qDebug() << "CustomItem::dragEnterEvent";
}
 
void CustomItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
{
    setCursor(Qt::ForbiddenCursor);
    qDebug() << "CustomItem::dragLeaveEvent";
}
 
void CustomItem::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
    setCursor(Qt::CrossCursor);
    qDebug() << "CustomItem::dragMoveEvent";
}
 
void CustomItem::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    setCursor(Qt::WaitCursor);
    qDebug() << "CustomItem::dropEvent";
}

复制代码

4、程序使用

复制代码

#include "CustomScene.h"
#include "CustomView.h"
#include "CustomItem.h"
#include <QApplication>
#include <QTime>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
 
    CustomScene scene;
    scene.setSceneRect(-200, -150, 400, 300);
    for(int i = 0; i < 5; ++i) {
        CustomItem *item = new CustomItem;
        item->setPos(i * 50 - 90, -50);
        scene.addItem(item);
    }
    CustomView view;
    view.setScene(&scene);
    view.show();
 
    return a.exec();
}

复制代码

 

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值