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

如何修改Qt PaintEvent代码实现Pixmap相对鼠标指针缩放

解决方案:围绕鼠标点缩放QPixmap并保持位置不变

我帮你搞定这个缩放问题!核心思路是在缩放前后计算鼠标对应的图像坐标,然后调整平移量,让这个图像点始终落在鼠标的屏幕位置上。下面是具体的修改方案和代码解释:


第一步:添加必要的成员变量

首先,我们需要一个变量来记录图像在窗口中的平移偏移量,替代你原来的m_where(可以直接替换或新增):

private:
    QPixmap m_pixmap;
    qreal m_scale = 1.0;
    qreal m_zoom = 1.0;
    QPointF m_translate; // 新增:图像的平移偏移量(窗口坐标系)

第二步:修改wheelEvent(核心逻辑)

这是实现围绕鼠标缩放的关键,我们需要:

  1. 把鼠标的窗口坐标转换成原始图像坐标系中的点(不考虑缩放)
  2. 计算新的缩放比例
  3. 根据图像点反推新的平移量,确保缩放后该点仍在鼠标位置
void DSSImageWidget::wheelEvent(QWheelEvent* e) {
    // 获取鼠标在当前窗口中的位置
    QPointF mouseScreenPos = e->position();

    // 计算缩放前,鼠标位置对应的原始图像坐标
    // 公式:(窗口坐标 - 平移量) / (总缩放比例)
    qreal totalScale = m_scale * m_zoom;
    QPointF mouseImagePos = (mouseScreenPos - m_translate) / totalScale;

    // 计算缩放因子(和你原来的逻辑一致)
    qreal degrees = -e->angleDelta().y() / 8.0;
    qreal steps = degrees / 60.0;
    m_zoom = std::clamp(m_zoom * std::pow(1.125, steps), 1.0, 5.0);

    // 计算新的平移量:让原始图像点仍落在鼠标的屏幕位置上
    // 公式:新平移量 = 鼠标屏幕位置 - 原始图像点 * 新的总缩放比例
    totalScale = m_scale * m_zoom;
    m_translate = mouseScreenPos - mouseImagePos * totalScale;

    update();
    Inherited::wheelEvent(e);
}

第三步:修改paintEvent

调整绘制顺序:先平移painter,再缩放,最后绘制图像,这样平移和缩放的逻辑就统一了:

void DSSImageWidget::paintEvent(QPaintEvent* event) {
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);

    // 先平移到目标位置,再应用缩放
    painter.translate(m_translate);
    painter.scale(m_scale * m_zoom, m_scale * m_zoom);

    // 绘制原始图像
    painter.drawPixmap(QPointF(0.0, 0.0), m_pixmap);

    // 绘制红色边框(注意边框线宽是基于原始图像坐标系的,所以0.25的宽度会随缩放变化)
    painter.setPen(QPen(QColor(255, 0, 0, 255), 0.25, Qt::SolidLine, Qt::FlatCap, Qt::BevelJoin));
    painter.setBrush(Qt::NoBrush);
    painter.drawRect(QRectF(0, 0, m_pixmap.width(), m_pixmap.height()).adjusted(-2, -2, 2, 2));

    painter.end();
}

第四步:完善resizeEvent

窗口大小变化时,按比例调整平移量,同时保持图像的相对位置,初始时让图像居中:

void DSSImageWidget::resizeEvent(QResizeEvent* e) {
    QSize oldSize = e->oldSize();
    QSize newSize = e->size();

    // 计算新旧缩放比例(和你原来的逻辑一致)
    qreal oldHScale = oldSize.isValid() ? (qreal)oldSize.width() / (m_pixmap.width() + 4) : 1.0;
    qreal oldVScale = oldSize.isValid() ? (qreal)oldSize.height() / (m_pixmap.height() + 4) : 1.0;
    qreal oldTotalScale = std::min(oldHScale, oldVScale) * m_zoom;

    qreal newHScale = (qreal)newSize.width() / (m_pixmap.width() + 4);
    qreal newVScale = (qreal)newSize.height() / (m_pixmap.height() + 4);
    m_scale = std::min(newHScale, newVScale);
    qreal newTotalScale = m_scale * m_zoom;

    // 调整平移量,保持图像相对位置
    if (oldSize.isValid() && oldTotalScale != 0) {
        // 按缩放比例调整平移量
        m_translate *= (newTotalScale / oldTotalScale);
        // 加上窗口大小变化的中心偏移,避免图像偏移到角落
        QPointF centerOffset = QPointF(newSize.width() - oldSize.width(), newSize.height() - oldSize.height()) / 2.0;
        m_translate += centerOffset;
    } else {
        // 初始状态:让图像居中显示
        m_translate = QPointF(
            (newSize.width() - m_pixmap.width() * newTotalScale) / 2.0,
            (newSize.height() - m_pixmap.height() * newTotalScale) / 2.0
        );
    }

    update();
    Inherited::resizeEvent(e);
}

第五步:初始化平移量

在构造函数中初始化m_translate,确保窗口首次显示时图像居中:

DSSImageWidget::DSSImageWidget(QWidget *parent) : QWidget(parent) {
    m_zoom = 1.0;
    // 假设m_pixmap已经加载完成
    if (!m_pixmap.isNull()) {
        m_scale = std::min((qreal)width()/(m_pixmap.width()+4), (qreal)height()/(m_pixmap.height()+4));
        qreal totalScale = m_scale * m_zoom;
        m_translate = QPointF(
            (width() - m_pixmap.width() * totalScale)/2.0,
            (height() - m_pixmap.height() * totalScale)/2.0
        );
    }
}

这样修改后,你滚动鼠标滚轮时,图像就会围绕鼠标指针所在的点缩放,鼠标下的图像内容会保持在原来的屏幕位置上。

内容的提问来源于stack exchange,提问作者David Partridge

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 21:47:52