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); }这里主要重写了键盘及鼠标事件具体说明请参考注释
