如何修改Qt PaintEvent代码实现Pixmap相对鼠标指针缩放
解决方案:围绕鼠标点缩放QPixmap并保持位置不变
我帮你搞定这个缩放问题!核心思路是在缩放前后计算鼠标对应的图像坐标,然后调整平移量,让这个图像点始终落在鼠标的屏幕位置上。下面是具体的修改方案和代码解释:
第一步:添加必要的成员变量
首先,我们需要一个变量来记录图像在窗口中的平移偏移量,替代你原来的m_where(可以直接替换或新增):
private: QPixmap m_pixmap; qreal m_scale = 1.0; qreal m_zoom = 1.0; QPointF m_translate; // 新增:图像的平移偏移量(窗口坐标系)
第二步:修改wheelEvent(核心逻辑)
这是实现围绕鼠标缩放的关键,我们需要:
- 把鼠标的窗口坐标转换成原始图像坐标系中的点(不考虑缩放)
- 计算新的缩放比例
- 根据图像点反推新的平移量,确保缩放后该点仍在鼠标位置
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
相关产品推荐
相关产品推荐

