效果展示
QT 實現可移動縮放的矩形框
完成該功能選擇Graphics View Framework這個框架,重寫QGraphicsItem、QGraphicsScene、QGraphicsView三個類,然后基本就是完成mousePressEvent、mouseMoveEvent、mouseReleaseEvent這幾個事件,再加上坐標位置的變換。
有一個問題是鼠標在矩形邊緣位置拖動縮放時,常見的矩形邊緣的特殊點,開始時在Qt的類中查看好像並沒有這個功能,參考了其他一些資料,大多實現此功能是人為在邊緣特殊點繪制8個小矩形。
實現縮放時用到的小矩形
此功能定義為一個類:SizeHandleRect,繼承自QGraphicsRectItem
主要內容為對應位置的記錄、鼠標位置的判斷、坐標移動、是否選擇時的隱藏與顯示。
頭文件:sizehandlerect.h
1 #ifndef SIZEHANDLERECT_H
2 #define SIZEHANDLERECT_H
3
4 #include <QGraphicsRectItem>
5
6 enum SelectionHandleState { SelectionHandleOff, SelectionHandleInactive, SelectionHandleActive };
7
8 class SizeHandleRect : public QGraphicsRectItem
9 {
10 public:
11 enum Direction { LeftTop , Top, RightTop, Right, RightBottom, Bottom, LeftBottom, Left , Center, None};
12 SizeHandleRect(QGraphicsItem* parent , QRectF rect, Direction dir);
13 Direction dir() const;
14 bool hitTest( const QPointF & point );
15 void move(qreal x, qreal y );
16 void setState(SelectionHandleState st);
17 protected:
18 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
19 private:
20 const Direction m_dir;
21 SelectionHandleState m_state;
22 };
23
24 #endif // SIZEHANDLERECT_H
其中Direction為一個枚舉類型,用於表示小矩形所代表的位置,邊緣特殊點共8個。
bool hitTest( const QPointF & point );用於判斷當前鼠標位置是否在小矩形區域中,實現如下:
1 bool SizeHandleRect::hitTest(const QPointF &point)
2 {
3 QPointF pt = mapFromScene(point);
4 return rect().contains(pt);
5 }
void setState(SelectionHandleState st);用於設置小矩形選中狀態時的隱藏和顯示,實現如下:
1 void SizeHandleRect::setState(SelectionHandleState st)
2 {
3 if (st == m_state)
4 return;
5 switch (st) {
6 case SelectionHandleOff:
7 hide();
8 break;
9 case SelectionHandleInactive:
10 case SelectionHandleActive:
11 show();
12 break;
13 }
14 m_state = st;
15 }
實現所要繪制的矩形類
類名定義為GraphicsRectItem,繼承自QGraphicsItem
主要內容為:
1、在構造函數中作為父類實例化8個小矩形SizeHandleRect,並將其放在對應的位置。
2、當大小變化時,重新繪制。
3、根據不同位置設置鼠標的形狀。
4、管理鼠標位置引起的標志變化。
5、選擇項目變化時的事件處理。
頭文件:graphicsrectitem.h
1 #ifndef GRAPHICSRECTITEM_H
2 #define GRAPHICSRECTITEM_H
3
4 #include <QGraphicsItem>
5 #include "sizehandlerect.h"
6
7 class GraphicsRectItem : public QGraphicsItem
8 {
9 public:
10 GraphicsRectItem(const QRect & rect ,QGraphicsItem * parent);
11
12 QRectF boundingRect() const;
13 virtual void resizeTo(SizeHandleRect::Direction dir, const QPointF & point );
14 virtual Qt::CursorShape getCursor(SizeHandleRect::Direction dir );
15 SizeHandleRect::Direction hitTest( const QPointF & point ) const;
16 virtual QRectF rect() const;
17
18 private:
19 QRectF m_rect;
20 typedef QVector<SizeHandleRect*> Handles;
21 Handles m_handles;
22 int selection_handle_size = 4;
23
24 protected:
25 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
26 virtual void updateGeometry();
27 void setState(SelectionHandleState st);
28 QVariant itemChange(GraphicsItemChange change, const QVariant &value);
29 };
30
31 #endif // GRAPHICSRECTITEM_H
構造函數中繪制8個小矩形,並設置一些標志屬性:
1 GraphicsRectItem::GraphicsRectItem(const QRect &rect, QGraphicsItem *parent) :
2 QGraphicsItem(parent)
3 {
4 m_rect = rect;
5 m_handles.reserve(SizeHandleRect::None);
6 for (int i = SizeHandleRect::LeftTop; i <= SizeHandleRect::Left; ++i) {
7 SizeHandleRect *shr = new SizeHandleRect(this, QRectF(0,0,4,4), static_cast<SizeHandleRect::Direction>(i));
8 m_handles.push_back(shr);
9 }
10 updateGeometry();
11 setFlag(QGraphicsItem::ItemIsMovable, true);
12 setFlag(QGraphicsItem::ItemIsSelectable, true);
13 setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
14 this->setAcceptHoverEvents(true);
15 }
重寫QGRAPHICSSCENE類完成主要功能及鼠標事件
類名定義為GraphicsScene
主要內容:
1、完成鼠標三事件,mousePressEvent、mouseMoveEvent、mouseReleaseEvent
2、創建矩形
3、添加圖像
頭文件:graphicsscene.h
1 #ifndef GRAPHICSSCENE_H
2 #define GRAPHICSSCENE_H
3
4 #include <QGraphicsScene>
5 #include "GraphicsRect/graphicsrectitem.h"
6 #include <QGraphicsSceneMouseEvent>
7
8 class GraphicsScene : public QGraphicsScene
9 {
10 Q_OBJECT
11
12 public:
13 GraphicsScene();
14 void creatRect();
15 void SetBackGroundImage(QPixmap pix, int width, int height);
16
17 GraphicsRectItem *m_RectItem = NULL;
18
19 private:
20 void setCursor(const QCursor & cursor );
21 // void keyPressEvent(QKeyEvent *event);
22 void mousePressEvent(QGraphicsSceneMouseEvent *event);
23 void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
24 void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
25 };
26
27 #endif // GRAPHICSSCENE_H
下面着重看下鼠標事件:
1 enum SelectMode
2 {
3 none,
4 netSelect,
5 move, //移動
6 size, //改變大小
7 rotate //反轉
8 };
用於記錄鼠標事件中的不同任務。
鼠標點擊
1 void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
2 {
3 QList<QGraphicsItem *> items = this->selectedItems();
4 GraphicsRectItem *item = 0;
5 if ( items.count() == 1 )
6 {
7 item = qgraphicsitem_cast<GraphicsRectItem*>(items.first());
8
9 nDragHandle = item->hitTest(event->scenePos());
10 if ( nDragHandle !=SizeHandleRect::None)
11 selectMode = size;
12 else
13 selectMode = move;
14 }
15 if(selectMode == move || selectMode == none){
16 QGraphicsScene::mousePressEvent(event);
17 }
18 }
判斷鼠標位置,確定操作類型:縮放-size,移動-move,無操作-none
鼠標移動:
1 void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
2 {
3 QList<QGraphicsItem *> items = this->selectedItems();
4 if(items.count() == 1){
5 GraphicsRectItem *item = qgraphicsitem_cast<GraphicsRectItem*>(items.first());
6 if ( nDragHandle != SizeHandleRect::None && selectMode == size ){
7 item->resizeTo(nDragHandle,event->scenePos());
8 }
9 else if(nDragHandle == SizeHandleRect::None && selectMode == none ){
10
11
12 SizeHandleRect::Direction handle = item->hitTest(event->scenePos());
13 if ( handle != SizeHandleRect::None){
14 setCursor(item->getCursor(handle));
15 }else{
16 setCursor(Qt::ArrowCursor);
17 }
18 }
19 else if(nDragHandle == SizeHandleRect::None && selectMode == move ){
20 QGraphicsScene::mouseMoveEvent(event);
21 }
22 }
23
24 this->update();
25 }
鼠標釋放:
1 void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
2 {
3 setCursor(Qt::ArrowCursor);
4 selectMode = none;
5 nDragHandle = SizeHandleRect::None;
6 QGraphicsScene::mouseReleaseEvent(event);
7 }
重寫QGRAPHICSVIEW
類名定義為GraphicsView
小知識點:重寫控件類,可以在Qt ui中拖入一個控件,右鍵“提升”為重寫的控件類。
重寫此類對於要完成的功能沒什么作用,添加上示范一下鼠標滾輪放大,鍵盤等效果。
頭文件:graphicsview.h
1 #ifndef GRAPHICSVIEW_H
2 #define GRAPHICSVIEW_H
3
4 #include <QGraphicsView>
5 #include <QKeyEvent>
6 #include <QWheelEvent>
7
8 class GraphicsView : public QGraphicsView
9 {
10 Q_OBJECT
11
12 public:
13 GraphicsView(QWidget *parent = 0);
14
15 protected:
16 void keyPressEvent(QKeyEvent *event);
17 void wheelEvent(QWheelEvent *event);
18 };
19
20 #endif // GRAPHICSVIEW_H
比如鼠標滾輪事件:
1 void GraphicsView::wheelEvent(QWheelEvent *event)
2 {
3 // 滾輪的滾動量
4 QPoint scrollAmount = event->angleDelta();
5 // 正值表示滾輪遠離使用者(放大),負值表示朝向使用者(縮小)
6 scrollAmount.y() > 0 ? scale(1.2, 1.2) : scale(1 / 1.2, 1 / 1.2);
7 }