Qt QGraphics 实现可移动缩放的矩形框

做图像处理有时需要在图像中选择一个矩形区域,矩形框需要可移动、缩放、查找当前位置,最近查了些资料,使用Qt完成了这个功能,为简化说明,这里只使用鼠标操作,完成矩形框的移动和缩放,键盘等其他功能和鼠标原理基本一致。

效果展示

在这里插入图片描述

概述

完成该功能选择Graphics View Framework这个框架,重写QGraphicsItem、QGraphicsScene、QGraphicsView三个类,然后基本就是完成mousePressEvent、mouseMoveEvent、mouseReleaseEvent这几个事件,再加上坐标位置的变换。
有一个问题是鼠标在矩形边缘位置拖动缩放时,常见的矩形边缘的特殊点,开始时在Qt的类中查看好像并没有这个功能,参考了其他一些资料,大多实现此功能是人为在边缘特殊点绘制8个小矩形。
源码及发布版本点此下载

实现缩放时用到的小矩形

此功能定义为一个类:SizeHandleRect,继承自QGraphicsRectItem
主要内容为对应位置的记录、鼠标位置的判断、坐标移动、是否选择时的隐藏与显示。
头文件:sizehandlerect.h

#ifndef SIZEHANDLERECT_H
#define SIZEHANDLERECT_H

#include <QGraphicsRectItem>

enum SelectionHandleState { SelectionHandleOff, SelectionHandleInactive, SelectionHandleActive };

class SizeHandleRect : public QGraphicsRectItem
{
public:
    enum Direction { LeftTop , Top, RightTop, Right, RightBottom, Bottom, LeftBottom, Left , Center, None};
    SizeHandleRect(QGraphicsItem* parent , QRectF rect, Direction dir);
    Direction dir() const;
    bool hitTest( const QPointF & point );
    void move(qreal x, qreal y );
    void setState(SelectionHandleState st);
protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
private:
    const Direction m_dir;
    SelectionHandleState m_state;
};

#endif // SIZEHANDLERECT_H

其中Direction为一个枚举类型,用于表示小矩形所代表的位置,边缘特殊点共8个。
bool hitTest( const QPointF & point );用于判断当前鼠标位置是否在小矩形区域中,实现如下:

bool SizeHandleRect::hitTest(const QPointF &point)
{
    QPointF pt = mapFromScene(point);
    return rect().contains(pt);
}

void setState(SelectionHandleState st);用于设置小矩形选中状态时的隐藏和显示,实现如下:

void SizeHandleRect::setState(SelectionHandleState st)
{
    if (st == m_state)
        return;
    switch (st) {
    case SelectionHandleOff:
        hide();
        break;
    case SelectionHandleInactive:
    case SelectionHandleActive:
        show();
        break;
    }
    m_state = st;
}

实现所要绘制的矩形类

类名定义为GraphicsRectItem,继承自QGraphicsItem
主要内容为:
1、在构造函数中作为父类实例化8个小矩形SizeHandleRect,并将其放在对应的位置。
2、当大小变化时,重新绘制。
3、根据不同位置设置鼠标的形状。
4、管理鼠标位置引起的标志变化。
5、选择项目变化时的事件处理。
头文件:graphicsrectitem.h

#ifndef GRAPHICSRECTITEM_H
#define GRAPHICSRECTITEM_H

#include <QGraphicsItem>
#include "sizehandlerect.h"

class GraphicsRectItem : public QGraphicsItem
{
public:
    GraphicsRectItem(const QRect & rect ,QGraphicsItem * parent);

    QRectF boundingRect() const;
    virtual void resizeTo(SizeHandleRect::Direction dir, const QPointF & point );
    void move(const QPointF & point);
    virtual Qt::CursorShape getCursor(SizeHandleRect::Direction dir );
    SizeHandleRect::Direction  hitTest( const QPointF & point ) const;
    virtual QRectF  rect() const;
    virtual void updateGeometry();
    QRectF m_rect;

private:

    typedef QVector<SizeHandleRect*> Handles;
    Handles m_handles;
    int selection_handle_size = 4;

protected:
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

    void setState(SelectionHandleState st);
    QVariant itemChange(GraphicsItemChange change, const QVariant &value);
};

#endif // GRAPHICSRECTITEM_H

构造函数中绘制8个小矩形,并设置一些标志属性:

GraphicsRectItem::GraphicsRectItem(const QRect &rect, QGraphicsItem *parent) :
    QGraphicsItem(parent)
{
    m_rect = rect;
    m_handles.reserve(SizeHandleRect::None);
    for (int i = SizeHandleRect::LeftTop; i <= SizeHandleRect::Left; ++i) {
        SizeHandleRect *shr = new SizeHandleRect(this, QRectF(0,0,4,4), static_cast<SizeHandleRect::Direction>(i));
        m_handles.push_back(shr);
    }
    updateGeometry();
    setFlag(QGraphicsItem::ItemIsMovable, true);
    setFlag(QGraphicsItem::ItemIsSelectable, true);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
    this->setAcceptHoverEvents(true);
}

其他函数代码比较多,可以直接下载示例查看

重写QGraphicsScene类完成主要功能及鼠标事件

类名定义为GraphicsScene
主要内容:
1、完成鼠标三事件,mousePressEvent、mouseMoveEvent、mouseReleaseEvent
2、创建矩形
3、添加图像
头文件:graphicsscene.h

#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H

#include <QGraphicsScene>
#include "GraphicsRect/graphicsrectitem.h"
#include <QGraphicsSceneMouseEvent>

class GraphicsScene : public QGraphicsScene
{
    Q_OBJECT

public:
    GraphicsScene();
    void creatRect();
    void SetBackGroundImage(QPixmap pix, int width, int height);

    GraphicsRectItem *m_RectItem = NULL;

private:
    void setCursor(const QCursor & cursor );
//    void keyPressEvent(QKeyEvent *event);
    void mousePressEvent(QGraphicsSceneMouseEvent *event);
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};

#endif // GRAPHICSSCENE_H

下面着重看下鼠标事件:

enum SelectMode
{
    none,
    netSelect,
    move, //移动
    size, //改变大小
    rotate //反转
};

用于记录鼠标事件中的不同任务。
鼠标点击

void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    QList<QGraphicsItem *> items = this->selectedItems();
    GraphicsRectItem *item = 0;
    if ( items.count() == 1 )
    {
        item = qgraphicsitem_cast<GraphicsRectItem*>(items.first());

        nDragHandle = item->hitTest(event->scenePos());
        if ( nDragHandle !=SizeHandleRect::None)
            selectMode = size;
        else
            selectMode =  move;
    }
    if(selectMode == move || selectMode == none){
        QGraphicsScene::mousePressEvent(event);
    }
}

判断鼠标位置,确定操作类型:缩放-size,移动-move,无操作-none
鼠标移动:

void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    QList<QGraphicsItem *> items = this->selectedItems();
    if(items.count() == 1){
        GraphicsRectItem *item = qgraphicsitem_cast<GraphicsRectItem*>(items.first());
        if ( nDragHandle != SizeHandleRect::None && selectMode == size ){
            item->resizeTo(nDragHandle,event->scenePos());
        }
        else if(nDragHandle == SizeHandleRect::None && selectMode == none ){


            SizeHandleRect::Direction handle = item->hitTest(event->scenePos());
            if ( handle != SizeHandleRect::None){
                setCursor(item->getCursor(handle));
            }else{
                setCursor(Qt::ArrowCursor);
            }
        }
        else if(nDragHandle == SizeHandleRect::None && selectMode == move ){
            //QGraphicsScene::mouseMoveEvent(event);//错误
            item->move(event->scenePos());
        }
    }

    this->update();
}

鼠标释放:

void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    setCursor(Qt::ArrowCursor);
    selectMode = none;
    nDragHandle = SizeHandleRect::None;
    QGraphicsScene::mouseReleaseEvent(event);
}

重写QGraphicsView

类名定义为GraphicsView
小知识点:重写控件类,可以在Qt ui中拖入一个控件,右键“提升”为重写的控件类。
重写此类对于要完成的功能没什么作用,添加上示范一下鼠标滚轮放大,键盘等效果。
头文件:graphicsview.h

#ifndef GRAPHICSVIEW_H
#define GRAPHICSVIEW_H

#include <QGraphicsView>
#include <QKeyEvent>
#include <QWheelEvent>

class GraphicsView : public QGraphicsView
{
    Q_OBJECT

public:
    GraphicsView(QWidget *parent = 0);

protected:
    void keyPressEvent(QKeyEvent *event);
    void wheelEvent(QWheelEvent *event);
};

#endif // GRAPHICSVIEW_H

比如鼠标滚轮事件:

void GraphicsView::wheelEvent(QWheelEvent *event)
{
    // 滚轮的滚动量
    QPoint scrollAmount = event->angleDelta();
    // 正值表示滚轮远离使用者(放大),负值表示朝向使用者(缩小)
    scrollAmount.y() > 0 ? scale(1.2, 1.2) : scale(1 / 1.2, 1 / 1.2);
}

其他功能可下载示例源码查看。

源码及发布版本
貌似没办法设置积分,本意是公开的。

  • 10
    点赞
  • 88
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值