1. 程式人生 > >互動式 QGraphicsView(平移/縮放/旋轉)

互動式 QGraphicsView(平移/縮放/旋轉)

簡述

Graphics View提供了一個平臺,用於大量自定義 2D 圖元的管理與互動,框架包括一個事件傳播架構,支援場景 Scene 中的圖元 Item 進行精確的雙精度互動功能。Item 可以處理鍵盤事件、滑鼠按下、移動、釋放和雙擊事件,同時也能跟蹤滑鼠移動。

和 Google 地圖一樣,在管理大量 Item 的時候,通常需要 View 具有互動(平移/縮放/旋轉)功能。

|

互動式 QGraphicsView

便於以後複用,實現一個互動式 QGraphicsView - InteractiveView。

主要功能包括:

  • 平移:
    • 方式一:滑鼠左鍵按下,然後移動
    • 方式二:按下上/下/左/右鍵分別向各個方向移動
  • 縮放:
    • 方式一:滑鼠滾輪向上滾動放大,向下滾動縮小
    • 方式二:按加號鍵(帶 Shift)進行放大,按減號鍵縮小
  • 旋轉:按空格鍵逆時針旋轉,回車鍵順時針旋轉

效果

這裡寫圖片描述

原始碼

interactive_view.h

#ifndef INTERACTIVE_VIEW_H
#define INTERACTIVE_VIEW_H

#include <QGraphicsView>

class QWheelEvent;
class QKeyEvent;

class InteractiveView : public QGraphicsView
{
    Q_OBJECT
public
: explicit InteractiveView(QWidget *parent = 0); // 平移速度 void setTranslateSpeed(qreal speed); qreal translateSpeed() const; // 縮放的增量 void setZoomDelta(qreal delta); qreal zoomDelta() const; protected: // 上/下/左/右鍵向各個方向移動、加/減鍵進行縮放、空格/回車鍵旋轉 void keyPressEvent(QKeyEvent *event
) Q_DECL_OVERRIDE; // 平移 void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; // 放大/縮小 void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; public Q_SLOTS: void zoomIn(); // 放大 void zoomOut(); // 縮小 void zoom(float scaleFactor); // 縮放 - scaleFactor:縮放的比例因子 void translate(QPointF delta); // 平移 private: Qt::MouseButton m_translateButton; // 平移按鈕 qreal m_translateSpeed; // 平移速度 qreal m_zoomDelta; // 縮放的增量 bool m_bMouseTranslate; // 平移標識 QPoint m_lastMousePos; // 滑鼠最後按下的位置 qreal m_scale; // 縮放值 }; #endif // INTERACTIVE_VIEW_H

平移速度預設為 1.0,可以使用 setTranslateSpeed() 來改變。縮放的增量大小也可以使用 setZoomDelta() 改變。

interactive_view.cpp

#include <QWheelEvent>
#include <QKeyEvent>
#include "interactive_view.h"

#define VIEW_CENTER viewport()->rect().center()
#define VIEW_WIDTH  viewport()->rect().width()
#define VIEW_HEIGHT viewport()->rect().height()

InteractiveView::InteractiveView(QWidget *parent)
    : QGraphicsView(parent),
      m_translateButton(Qt::LeftButton),
      m_scale(1.0),
      m_zoomDelta(0.1),
      m_translateSpeed(1.0),
      m_bMouseTranslate(false)
{
    // 去掉滾動條
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setCursor(Qt::PointingHandCursor);
    setRenderHint(QPainter::Antialiasing);

    setSceneRect(INT_MIN/2, INT_MIN/2, INT_MAX, INT_MAX);
    centerOn(0, 0);
}

// 平移速度
void InteractiveView::setTranslateSpeed(qreal speed)
{
    // 建議速度範圍
    Q_ASSERT_X(speed >= 0.0 && speed <= 2.0,
               "InteractiveView::setTranslateSpeed", "Speed should be in range [0.0, 2.0].");
    m_translateSpeed = speed;
}

qreal InteractiveView::translateSpeed() const
{
    return m_translateSpeed;
}

// 縮放的增量
void InteractiveView::setZoomDelta(qreal delta)
{
    // 建議增量範圍
    Q_ASSERT_X(delta >= 0.0 && delta <= 1.0,
               "InteractiveView::setZoomDelta", "Delta should be in range [0.0, 1.0].");
    m_zoomDelta = delta;
}

qreal InteractiveView::zoomDelta() const
{
    return m_zoomDelta;
}

// 上/下/左/右鍵向各個方向移動、加/減鍵進行縮放、空格/回車鍵旋轉
void InteractiveView::keyPressEvent(QKeyEvent *event)
{
    switch (event->key()) {
    case Qt::Key_Up:
        translate(QPointF(0, -2));  // 上移
        break;
    case Qt::Key_Down:
        translate(QPointF(0, 2));  // 下移
        break;
    case Qt::Key_Left:
        translate(QPointF(-2, 0));  // 左移
        break;
    case Qt::Key_Right:
        translate(QPointF(2, 0));  // 右移
        break;
    case Qt::Key_Plus:  // 放大
        zoomIn();
        break;
    case Qt::Key_Minus:  // 縮小
        zoomOut();
        break;
    case Qt::Key_Space:  // 逆時針旋轉
        rotate(-5);
        break;
    case Qt::Key_Enter:  // 順時針旋轉
    case Qt::Key_Return:
        rotate(5);
        break;
    default:
        QGraphicsView::keyPressEvent(event);
    }
}

// 平移
void InteractiveView::mouseMoveEvent(QMouseEvent *event)
{
    if (m_bMouseTranslate){
        QPointF mouseDelta = mapToScene(event->pos()) - mapToScene(m_lastMousePos);
        translate(mouseDelta);
    }

    m_lastMousePos = event->pos();

    QGraphicsView::mouseMoveEvent(event);
}

void InteractiveView::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == m_translateButton) {
        // 當游標底下沒有 item 時,才能移動
        QPointF point = mapToScene(event->pos());
        if (scene()->itemAt(point, transform()) == NULL)  {
            m_bMouseTranslate = true;
            m_lastMousePos = event->pos();
        }
    }

    QGraphicsView::mousePressEvent(event);
}

void InteractiveView::mouseReleaseEvent(QMouseEvent *event)
{
    if (event->button() == m_translateButton)
        m_bMouseTranslate = false;

    QGraphicsView::mouseReleaseEvent(event);
}

// 放大/縮小
void InteractiveView::wheelEvent(QWheelEvent *event)
{
    // 滾輪的滾動量
    QPoint scrollAmount = event->angleDelta();
    // 正值表示滾輪遠離使用者(放大),負值表示朝向使用者(縮小)
    scrollAmount.y() > 0 ? zoomIn() : zoomOut();
}

// 放大
void InteractiveView::zoomIn()
{
    zoom(1 + m_zoomDelta);
}

// 縮小
void InteractiveView::zoomOut()
{
    zoom(1 - m_zoomDelta);
}

// 縮放 - scaleFactor:縮放的比例因子
void InteractiveView::zoom(float scaleFactor)
{
    // 防止過小或過大
    qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
    if (factor < 0.07 || factor > 100)
        return;

    scale(scaleFactor, scaleFactor);
    m_scale *= scaleFactor;
}

// 平移
void InteractiveView::translate(QPointF delta)
{
    // 根據當前 zoom 縮放平移數
    delta *= m_scale;
    delta *= m_translateSpeed;

    // view 根據滑鼠下的點作為錨點來定位 scene
    setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    QPoint newCenter(VIEW_WIDTH / 2 - delta.x(),  VIEW_HEIGHT / 2 - delta.y());
    centerOn(mapToScene(newCenter));

    // scene 在 view 的中心點作為錨點
    setTransformationAnchor(QGraphicsView::AnchorViewCenter);
}

這裡,主要重寫了鍵盤及滑鼠事件,具體說明請參考註釋!