You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 06:54:23