没有重载view类鼠标事件的拖动
拖动效果:
自定义一个Node类,该类继承自QGraphicsItem,构造函数如所示。
Node::Node(GraphWidget *graphWidget)
: graph(graphWidget)
{
setFlag(ItemIsMovable);
setCacheMode(DeviceCoordinateCache);
setZValue(-1);
}
在构造函数中,我们设置了 ItemIsMovable 标志以允许项目移动以响应鼠标拖动。我们还启用了 DeviceCoordinateCache 来加快渲染性能。为了确保节点始终堆叠在边缘之上,我们最终将项目的 Z 值设置为 -1。
setFlag函数: 设置item类的某些标志位,相当于启动了某些功能。
ItemIsMovable标志位:
该item支持使用鼠标进行交互式移动。通过单击该item,然后拖动,该项目将与鼠标光标一起移动。如果item具有子项,则所有子项也将被移动。如果item是所选内容的一部分,则所有选定项目也将被移动。此功能是通过 QGraphicsItem 的鼠标事件处理程序的基本实现提供的。
所以只要用setFlag函数,设置标志位ItemIsMovable即可实现item类的交互式移动。
介绍一下其他标志位。
常量 | 说明 |
---|---|
QGraphicsItem::ItemIsSelectable | 该项目支持选择。启用此功能将启用 setSelected() 以切换项目的选择。它还将允许通过调用 QGraphicsScene::setSelectionArea()、单击项目或在 QGraphicsView 中使用橡皮筋选择来自动选择该项目。 |
QGraphicsItem::ItemIsFocusable | 该项目支持键盘输入焦点(即,它是输入项目)。启用此标志将允许项目接受焦点,这再次允许将关键事件传递到 QGraphicsItem::keyPressEvent() 和 QGraphicsItem::keyReleaseEvent()。 |
QGraphicsItem::ItemClipsToShape | 项目将剪辑成自己的形状。项目无法在其形状之外绘制或接收鼠标、平板电脑、拖放或悬停事件。默认情况下,它处于禁用状态。此行为由 QGraphicsView::drawItems() 或 QGraphicsScene::d rawItems() 强制执行。此标志是在 Qt 4.3 中引入的。 |
QGraphicsItem::ItemIgnoresTransformations | 项目将忽略继承的变换(即,其位置仍锚定到其父项,但父项或视图旋转、缩放或切变变换将被忽略)。此标志对于保持文本标签项水平且未缩放非常有用,因此,如果转换了视图,这些项仍可读。设置后,项目的视图几何图形和场景几何图形将分别保留。您必须调用 deviceTransform() 来映射坐标并检测视图中的冲突。默认情况下,此标志处于禁用状态。此标志是在 Qt 4.3 中引入的。 |
QGraphicsItem::ItemSendsGeometryChanges | 该项目为 ItemPositionChange、ItemPositionHasChanged、ItemMatrixChange、ItemTransformChange、ItemTransformHasChanged、ItemRotationChange、ItemRotationHasChange、ItemScaleChange、ItemScaleHasChanged、ItemTransformOriginPointChange 和 ItemTransformOriginPoint HasChanged 启用 itemChange() 通知。出于性能原因,默认情况下禁用这些通知。您必须启用此标志才能接收位置和转换更改的通知。此标志是在 Qt 4.6 中引入的。 |
有重载view类鼠标事件的拖动
如果是单独自定义Item类,并开启了对应标志位,就能实现Item类的自由拖动。但是如果还需要点击view空白区域产生节点,这个时候没有任何节点会收到鼠标点击事件,所以就需要自定义View类,并且重载鼠标事件。
view鼠标事件的代码逻辑是:如果我所点击的地方没有节点,那就创造新的节点。
QGraphicsItem *item = itemAt(event->pos())
event->pos()是鼠标所在的位置,itemAt会返回鼠标位置所在的Item类,如果没有就返回空。通过这个函数能判断鼠标下是否已存在节点。
那为什么拖动要涉及到这个呢?因为我发现当我们重载View类的鼠标事件后,拖动失效了。
要搞明白这件事情,我们需要了解View框架下鼠标事件的传导过程,一般当鼠标按下,按下列顺序进行传导:【view】->【scene】->【item】。
我们在拖动过程中只开启了一个标志位,而没有去用函数检测鼠标下面的item到底是哪个item。其实这个工作是scene完成,而且Qt已经帮我们写好了代码。scene接收到来自view的鼠标事件,会辨别这是应该归属于哪个item,从而将信号发给对应item的鼠标事件。
但是我们如果重载了view的鼠标事件函数,就会把【view】->【scene】这一步截断,导致item无法自由拖动。通过自己添加的qDebug,可以发现都是view在发出信息。
void MyGraphicsView::mouseReleaseEvent(QMouseEvent *event){
qDebug()<<"【view】mouseRelease";
}
那要怎么解决这个问题呢?
很简单,只需要在重载的鼠标事件里面,添加上原来的鼠标事件就可以了。以鼠标释放事件举例。
//view类鼠标释放事件
void MyGraphicsView::mouseReleaseEvent(QMouseEvent *event){
qDebug()<<"【view】mouseRelease";
QGraphicsView::mouseReleaseEvent(event);
}
通过输入能看到最终的信息通路是这样的:
1.释放鼠标,进入MyGraphicsView::mouseReleaseEvent鼠标事件。
2.发出【view】mouseRelease。
3.进入Qt原有的鼠标事件,QGraphicsView::mouseReleaseEvent(event);这个函数会把信号发给scene,并做进一步区分。
4.进入节点的重载鼠标释放事件,MyGraphicsVexItem::mouseReleaseEvent。
5.发出【item】mouseRelease。
6.完成某些操作。