放大图像时控制平移(定位点)

2024-10-02 02:24:14 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在编写一个简单的图像查看器,并实现了一个平移和缩放功能(分别使用鼠标拖动和鼠标滚轮滚动)。我成功地实现了pan(简单模式)和一个天真的“em>到左上角的”缩放。
我现在想优化缩放,使用户的鼠标在缩放时的坐标成为“焦点”:也就是说,当缩放时,平移会被更新,这样用户鼠标下的像素(图像的)保持不变(这样他们就真正地将放大到那个区域)

通过在其他普通的QWidget上覆盖paintEvent来查看图像。
尽管我可能会尝试使用直观的方法,但我似乎无法实现正确的缩放行为。在

属性scale表示当前的缩放级别(比例为2表示图像的实际大小为原来的两倍,0.5表示为一半,scale>;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()

以下是平移代码(相对简单):
pressedanchor完全用于平移,并分别参考鼠标初始按下时和图像视图位置的位置)

^{pr2}$

以下是缩放代码,而不尝试调整平移。它会导致屏幕左上角的所有内容缩小或增长

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)

这背后的理论是,在缩放之前,鼠标下方的像素是左上角(位置)的长度dxdy。缩放之后,我们计算出这个像素的新位置,并通过调整self.position为像素的西部和北部,使其位于屏幕上相同的坐标下。在

我不完全确定我哪里出错了:我怀疑old point到屏幕坐标的映射某种程度上是错误的,或者更可能的是:我的数学错误,因为我混淆了像素和屏幕坐标。
我尝试了一些直觉上的变化,但没有什么能接近预期的锚定。在

我想这对于文件浏览者来说是一个相当常见的任务(因为大多数人看起来都是这样缩放的),但是我发现研究这些算法相当困难。在

以下是修补缩放的完整代码(需要PyQt4):
http://pastebin.com/vvpdZy9g

感谢任何帮助!在


Tags: 代码图像selfevent区域屏幕defposition
1条回答
网友
1楼 · 发布于 2024-10-02 02:24:14

好吧,我设法让它工作了

def wheelEvent(self, event):
    oldscale = self.scale
    self.scale += event.delta() / 1200.0
    if (self.scale < 0.1):
        self.scale = oldscale

    screenpoint = self.mapFromGlobal(QtGui.QCursor.pos())
    dx, dy = screenpoint.x(), screenpoint.y()
    oldpoint = (screenpoint.x() + self.position[0], screenpoint.y() + self.position[1])
    newpoint = (oldpoint[0] * (self.scale/oldscale),
                oldpoint[1] * (self.scale/oldscale))
    self.position = (newpoint[0] - dx, newpoint[1] - dy)

这里的逻辑是:

  • 我们用屏幕上的像素和屏幕之间的距离来确定
  • 我们使用屏幕点位置来查找鼠标相对于图像平面的坐标(即:悬停像素的2D索引),即oldpoint
  • 应用我们的缩放,我们计算像素的新2D索引(new point)
  • 我们希望这个像素出现在屏幕上,但不是在左上角:我们希望它位于左上角(位置)的dxdy

问题确实是图像和显示坐标之间的一个小小的混淆。在

相关问题 更多 >

    热门问题