目录
一、分清Scene、View、Item
View是正常可见部分,Scene不在View范围里的是不可见但仍然存在的部分,可以通过在View中移动滚动条将不可见部分变为可见(形象地说,假设你要去看演出,Scene指代是整个演出,包括演员、场景、舞台、幕后等,View就是舞台,View之外的是幕后,Item就是演员)。
二、了解坐标变换
在做一些图形位移、旋转时经常需要进行坐标位置的转换。Scene、View、Item都有自己本身的坐标(View、Item是左上角为坐标原点,右下角为正方向),Scene中可以通过setSceneRect()函数设置左上角的坐标。
如
this->setSceneRect(QRectF(0,0,4000,4000));
这里设置了左上角坐标为(0,0),Scene的长宽均为4000。
【注意】只用QGraphicsItem和QGraphicsView有坐标转换的功能,也就是说QGraphicsItem和QGraphicsView都可以QGraphicsScene直接进行坐标转换,但是QGraphicsItem和QGraphicsView之间的坐标转换需要通过QGraphicsScene进行转换。
所有的图元QGraphicsItem都是放到QGraphicsScene中,所以QGraphicsScene是所有的图元的父图元。
QGraphicsView::mapToScene() - 视图 -> 场景
QGraphicsView::mapFromScene() - 场景 -> 视图
QGraphicsItem::mapToScene() - 图元 -> 场景
QGraphicsItem::mapFromScene() - 场景 -> 图元
QGraphicsItem::mapToParent() - 子图元 -> 父图元
QGraphicsItem::mapFromParent() - 父图元 -> 子图元
QGraphicsItem::mapToItem() - 本图元 -> 其他图元
QGraphicsItem::mapFromItem() - 其他图元 -> 本图元
QPointF、QPolygonF、QPainterPath
QPointF存的是点坐标(x,y)。
QPolygonF是vector<QPoint>,可以用数据流的方式存入,例如
QPolygonF polygon;
polygon << QPointF(10.4, 20.5) << QPointF(20.2, 30.2);
QPainterPath是点的画图路径(顺序),QPolygonF可以通过addPolygon(QPolygonF)方式添加进入QPainterPath,顺序为vector下标。
setPos(QPointF)
setPos用于设置目标元件左上角在其parent中的坐标位置,若没有parent则设置其在Scene中的坐标位置。
scenePos(QPointF)
获取目标在Scene中的坐标位置。
Pos()
获取目标在parent下的位置坐标。
moveCenter(QPointF)
移动目标的中心位置到指定点。
三、重写
Qt中的一些Event事件本身在QT源码中就已经存在,在开发的时候,需要根据自己的需求进行重写(当作虚函数),并且要传递给Qt本身的函数实现重写。
四、鼠标事件
在鼠标事件中,常用的包括了左击、右击和滚轮。鼠标点击分为mousePressEvent(QGraphicsSceneMouseEvent *event)
、mouseMoveEvent(QGraphicsSceneMouseEvent *event)
、mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
,鼠标滚轮为mouseReleaseEvent(QWheelEvent *event)。
在鼠标滚动事件中,需要判断是前滚动还是后滚动。可以通过计算滚动的单位长度来判断方向,+为向前,-为向后。确定滚动方向后就可以确定缩放大小。具体代码如下:
if(event->modifiers() != Qt::ControlModifier){//按下Control
QGraphicsView::wheelEvent(event);
return;
}
QPoint delta = event->angleDelta();//滚动的距离,+为前,-为后
if (delta.y() == 0){//没有滚动
event->ignore();
return;
}
this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);//鼠标下方的点用作锚点
double const d = delta.y() / std::abs(delta.y());//单位距离,判断前后
if(d < 0 && this->matrix().m11() < 0.000001){//放缩过小可以忽略
this->scale(1, 1);
return;
}
if (d > 0.0)
scaleUp();//自定义函数:放大
else
scaleDown();//自定义函数:缩小
也可以用下面这个进行方法 (通用)
void A::wheelEvent(QWheelEvent *event)
{
if ( QApplication::keyboardModifiers () == Qt::ControlModifier)
{
int wheelValue = event->angleDelta().y();
this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);//鼠标下方的点用作锚点
double ratio = (double)wheelValue / (double)1200 + 1;
scale(ratio, ratio);
}
}
关于transform
获取位置坐标
在图形的移动中,需要获取图像坐标。
在让图像移动时,要注意,计算差值offset来位置更好,可以避免一些不必要的错误。
offset是当前变化点的位置和某一个固定点的位置差,固定点可以是Scene左上角,也可以是Item中心点在Scene中的坐标。
auto bkRect = scene->_pixmapItem->sceneBoundingRect();//scene中被打开的图片的左上角坐标
auto rect = this->sceneBoundingRect();//Item左上角在图片中的坐标
event->scenePos()是鼠标点击位置在scene的坐标
rect.center()是Item中心点在scene的坐标
rect.moveCenter(event->scenePos() - _offset);//对图像中心点进行移动 【注意】一定是减法
五、获取画框控制点
画框共有8个控制点(左上,右上,右下,左下,左中,右中,上中,下中)
没写完,下次补上
六、获取鼠标下的场景中的所有Item
如果想要实现鼠标拖拽一个Item放入另外一个Item,并且被放入的Item要显示高亮。这个时候需要获取鼠标下的所有Item,也就是Item的List。
可以使用items(event->scenePos()),返回当前位置下的所有items的指针List,items的返回类型QList<QGraphicsItem*>。然后可以根据item->type()判断类型来找到自己需要的Item(item的type是自己重写的哦,用于区分自己定义的不同Item)
--------------------------------------------------------------------------------------------------------------------------------
关于自定义Item的type。可以在子类public这样写
int type() const override {return E_Group;}
这个E_Group就是你在基类中定义的用于区分不同子类的ItemType。
--------------------------------------------------------------------------------------------------------------------------------
关于ItemType,可以在基类public中这样写
enum ItemType{
E_Group= UserType + 1, //组
E_BaseNode //node基类
};
--------------------------------------------------------------------------------------------------------------------------------
获取到List后,可以遍历List,对每一个Item进行类型判断,如果找到了想要的Item,记得将Item转为实例哦!!!!!因为这里获取到的仅仅是一个指针。
关于将Item指针转为实例,可以这样写
if (item->type() == E_Group && !item->isSelected()){
GroupItem *group = dynamic_cast<GroupItem *>(item);
}
如果出现提示dynamic_cast的目标类型无效