QGraphicsView入门学习

文章详细阐述了Qt图形视图框架中的Scene、View、Item的区别和作用,以及坐标变换的原理和方法,包括如何在Scene、View之间进行坐标转换。此外,还介绍了如何重写事件处理函数以定制行为,特别是鼠标事件的处理,如点击、移动和滚轮缩放。同时,文章提到了获取图元位置坐标的方法和获取画框控制点的技巧。
摘要由CSDN通过智能技术生成

目录

一、分清Scene、View、Item

二、了解坐标变换

三、重写

四、鼠标事件

关于transform

获取位置坐标

五、获取画框控制点


一、分清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的目标类型无效

可以参考QT:遇到的bug及解决方法_Yanjun2i的博客-CSDN博客 解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值