【发布时间】:2014-01-23 10:06:36
【问题描述】:
我正在编写一个简单的图像查看器并实现平移和缩放功能(分别使用鼠标拖动和鼠标滚轮滚动)。我已经成功实现了平移(简单模式)和天真的“进入左上角”缩放。
我现在想细化缩放,使缩放时用户鼠标的坐标成为“焦点”:也就是说,在缩放时,平移会更新,以便用户鼠标下的像素(图像)保持不变相同(以便他们真正放大该区域)
通过在一个普通的 QWidget 上覆盖 paintEvent 来查看图像。
尽我所能尝试直观的方法,我似乎无法实现正确的缩放行为。
属性scale 表示当前的缩放级别(比例为 2 表示图像是实际大小的两倍,0.5 表示一半,比例 > 0),position 是顶部的坐标 -当前查看的图像区域的左角(通过平移)。
这是实际图像显示的执行方式
def paintEvent(self, event):
painter = QtGui.QPainter()
painter.begin(self)
painter.drawImage(0, 0,
self.image.scaled(
self.image.width() * self.scale,
self.image.height() * self.scale,
QtCore.Qt.KeepAspectRatio),
self.position[0], self.position[1])
painter.end()
这里是平移代码(比较简单):
(pressed 和 anchor 完全用于平移,分别指鼠标初始按下的位置和当时的图像视图位置)
def mousePressEvent(self, event):
self.pressed = event.pos()
self.anchor = self.position
def mouseReleaseEvent(self, event):
self.pressed = None
def mouseMoveEvent(self, event):
if (self.pressed):
dx, dy = event.x() - self.pressed.x(), event.y() - self.pressed.y()
self.position = (self.anchor[0] - dx, self.anchor[1] - dy)
self.repaint()
这是缩放代码没有尝试调整平移。它会导致所有内容从/到屏幕的左上角缩小或增长
def wheelEvent(self, event):
oldscale = self.scale
self.scale += event.delta() / 1200.0
if (self.scale < 0.1):
self.scale = oldscale
self.repaint()
这是缩放代码通过平移保留(锚定)可见区域的左上角。放大时,屏幕左上角的像素不会改变。
def wheelEvent(self, event):
oldscale = self.scale
self.scale += event.delta() / 1200
if (self.scale < 0.1):
self.scale = oldscale
self.position = (self.position[0] * (self.scale / oldscale),
self.position[1] * (self.scale / oldscale))
self.repaint()
我想要上面的效果,但是滚动时锚点要在用户的鼠标上。这是我的尝试,效果非常轻微:缩放仍然不是我想要的,而是滚动到鼠标的一般区域,没有锚定。事实上,保持鼠标在同一个位置并放大,似乎是沿着一条弯曲的路径,向右平移,然后向左平移。
def wheelEvent(self, event):
oldscale = self.scale
self.scale += event.delta() / 1200.0
if (self.scale < 0.1):
self.scale = oldscale
oldpoint = self.mapFromGlobal(QtGui.QCursor.pos())
dx, dy = oldpoint.x() - self.position[0], oldpoint.y() - self.position[1]
newpoint = (oldpoint.x() * (self.scale/oldscale),
oldpoint.y() * (self.scale/oldscale))
self.position = (newpoint[0] - dx, newpoint[1] - dy)
这背后的理论是,在缩放之前,鼠标“下方”的像素距离左上角(位置)的长度为 dx 和 dy )。缩放后,我们计算这个像素的新位置,并通过调整我们的self.position为dx和dy西和北强制它在屏幕上的相同坐标下像素。
我不完全确定我哪里出错了:我怀疑 old point 到我的屏幕坐标的映射在某种程度上是错误的,或者更有可能是:我的数学是错误的,因为我混淆了像素和屏幕坐标.
我尝试了一些直观的变化,但没有什么能接近预期的锚定。
我想这对于文件查看器来说是一项相当常见的任务(因为大多数似乎都像这样缩放),但我发现研究算法非常困难。
这是修改缩放的完整代码(需要 PyQt4):
http://pastebin.com/vvpdZy9g
感谢任何帮助!
【问题讨论】:
标签: python image zooming pyqt4 pan