一 简述
Graphics View提供了一个平台,用于大量自定义 2D 图元的管理与交互,框架包括一个事件传播架构,支持场景 Scene 中的图元 Item 进行精确的双精度交互功能。Item 可以处理键盘事件、鼠标按下、移动、释放和双击事件,同时也能跟踪鼠标移动。
和 Google 地图一样,在管理大量 Item 的时候,通常需要 View 具有交互(平移/缩放/旋转)功能。
二 交互式 QGraphicsView
便于以后复用,实现一个交互式 QGraphicsView - InteractiveView。
主要功能包括:
平移:
1. 方式一:鼠标左键按下,然后移动
2.
方式二:按下上/下/左/右键分别向各个方向移动
缩放:
1. 方式一:鼠标滚轮向上滚动放大,向下滚动缩小
2.
方式二:按加号键(带 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);
}