Qt 5.0.2 QOpenGLWidget图形缩放平移实现及代码问题咨询
Qt + OpenGL 对勾图形缩放与平移实现方案
核心实现思路
- 平移:跟踪鼠标拖拽的像素差,转换为OpenGL世界坐标的偏移量,结合当前缩放因子调整平移幅度,保证拖拽速度一致
- 缩放:以鼠标当前位置为中心进行缩放,先将图形平移到鼠标世界坐标原点,缩放后再平移回原位置,避免图形偏移
完整实现代码
头文件(GLWidget.h)
#ifndef GLWIDGET_H #define GLWIDGET_H #include <QOpenGLWidget> #include <QOpenGLFunctions> #include <QMouseEvent> #include <QWheelEvent> class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: explicit GLWidget(QWidget *parent = nullptr); protected: void initializeGL() override; void resizeGL(int w, int h) override; void paintGL() override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; private: float m_translateX = 0.0f; float m_translateY = 0.0f; float m_scale = 1.0f; QPoint m_lastMousePos; bool m_isDragging = false; QPointF screenToWorld(const QPoint &screenPos); }; #endif // GLWIDGET_H
源文件(GLWidget.cpp)
#include "GLWidget.h" #include <QMatrix4x4> GLWidget::GLWidget(QWidget *parent) : QOpenGLWidget(parent) { setMouseTracking(true); } void GLWidget::initializeGL() { initializeOpenGLFunctions(); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); } void GLWidget::resizeGL(int w, int h) { glViewport(0, 0, w, h); } void GLWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT); QMatrix4x4 modelView; modelView.translate(m_translateX, m_translateY); modelView.scale(m_scale); glMatrixMode(GL_MODELVIEW); glLoadMatrixf(modelView.data()); // 绘制对勾 glColor3f(0.0f, 0.5f, 0.0f); glBegin(GL_LINES); glVertex2f(-0.3f, 0.0f); glVertex2f(0.0f, 0.3f); glVertex2f(0.0f, 0.3f); glVertex2f(0.6f, -0.3f); glEnd(); } void GLWidget::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::LeftButton) { m_isDragging = true; m_lastMousePos = event->pos(); } } void GLWidget::mouseMoveEvent(QMouseEvent *event) { if (m_isDragging) { QPoint delta = event->pos() - m_lastMousePos; // 转换屏幕像素差为世界坐标偏移,修正Y轴方向 float xOffset = 2.0f * delta.x() / width(); float yOffset = -2.0f * delta.y() / height(); // 缩放状态下,平移量乘以缩放因子保证拖拽速度一致 m_translateX += xOffset * m_scale; m_translateY += yOffset * m_scale; m_lastMousePos = event->pos(); update(); } } void GLWidget::wheelEvent(QWheelEvent *event) { QPointF worldPos = screenToWorld(event->pos()); float scaleFactor = event->angleDelta().y() > 0 ? 1.1f : 0.9f; // 围绕鼠标位置缩放:平移到中心→缩放→平移回原位 m_translateX -= worldPos.x(); m_translateY -= worldPos.y(); m_scale *= scaleFactor; m_translateX += worldPos.x() * scaleFactor; m_translateY += worldPos.y() * scaleFactor; update(); } QPointF GLWidget::screenToWorld(const QPoint &screenPos) { // 屏幕坐标转OpenGL归一化设备坐标 float x = 2.0f * screenPos.x() / width() - 1.0f; float y = 1.0f - 2.0f * screenPos.y() / height(); // 通过逆模型视图矩阵转换为世界坐标 QMatrix4x4 modelView; modelView.translate(m_translateX, m_translateY); modelView.scale(m_scale); QMatrix4x4 invModelView = modelView.inverted(); QVector4d worldVec = invModelView * QVector4d(x, y, 0.0f, 1.0f); return QPointF(worldVec.x(), worldVec.y()); }
新手常见错误及修正
- 拖拽方向反转:Qt屏幕Y轴向上,OpenGL世界Y轴向下,转换偏移量时需对Y轴取反
- 缩放后拖拽速度异常:未将平移增量乘以当前缩放因子,导致缩放越大拖拽越快
- 缩放中心不合理:直接围绕窗口中心缩放,而非鼠标位置,交互体验差,需通过矩阵变换实现围绕鼠标点缩放
- 直接修改顶点坐标:未使用模型视图矩阵管理变换,代码冗余且难以扩展,应保持顶点坐标不变,通过矩阵实现平移缩放
- 鼠标事件处理不全:未开启鼠标追踪或未标记拖拽状态,导致拖拽无响应,需调用
setMouseTracking(true)并维护m_isDragging标记
内容的提问来源于stack exchange,提问作者Wakie
相关产品推荐
相关产品推荐

