Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/348.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 放大图像时控制平移(以定位点)_Python_Image_Zooming_Pyqt4_Pan - Fatal编程技术网

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

Python 放大图像时控制平移(以定位点),python,image,zooming,pyqt4,pan,Python,Image,Zooming,Pyqt4,Pan,我正在编写一个简单的图像查看器并实现一个平移和缩放功能(分别使用鼠标拖动和鼠标滚轮滚动)。我已经成功地实现了平移(轻松模式)和简单的“进入左上角”缩放。 现在,我想细化缩放,以便在缩放时用户鼠标的坐标成为“焦点”:也就是说,在缩放时,平移会更新,以便用户鼠标下的像素(图像)保持不变(以便它们真正缩放到该区域) 通过覆盖其他普通QWidget上的paintEvent来查看图像。 尽管我可能会尝试使用直观的方法,但我似乎无法实现正确的缩放行为 属性scale表示当前的缩放级别(比例为2表示查看图像的

我正在编写一个简单的图像查看器并实现一个平移和缩放功能(分别使用鼠标拖动和鼠标滚轮滚动)。我已经成功地实现了平移(轻松模式)和简单的“进入左上角”缩放。
现在,我想细化缩放,以便在缩放时用户鼠标的坐标成为“焦点”:也就是说,在缩放时,平移会更新,以便用户鼠标下的像素(图像)保持不变(以便它们真正缩放到该区域)

通过覆盖其他普通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 wheelEvent(self, event):
    oldscale = self.scale
    self.scale += event.delta() / 1200.0
    if (self.scale < 0.1):
        self.scale = oldscale
    self.repaint()
def wheelEvent(自身,事件):
oldscale=self.scale
self.scale+=event.delta()/1200.0
如果(自标度<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(自身,事件):
oldscale=self.scale
self.scale+=event.delta()/1200
如果(自标度<0.1):
self.scale=oldscale
self.position=(self.position[0]*(self.scale/oldscale),
自身位置[1]*(自身刻度/旧刻度))
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)
def wheelEvent(自身,事件):
oldscale=self.scale
self.scale+=event.delta()/1200.0
如果(自标度<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()*(自缩放/oldscale))
self.position=(新点[0]-dx,新点[1]-dy)
这背后的理论是,在缩放之前,鼠标“下方”的像素是距离左上角(位置)的长度dx和dy。缩放后,我们计算该像素的新位置,并通过调整
self.position
为像素西面和北面的dx和dy,将其强制在屏幕上的同一坐标下

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

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

以下是修补缩放的完整代码(需要PyQt4):


感谢您的帮助

好的,我设法让它工作了

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)
def wheelEvent(自身,事件):
oldscale=self.scale
self.scale+=event.delta()/1200.0
如果(自标度<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.position=(新点[0]-dx,新点[1]-dy)
这里的逻辑是:

  • 我们得到鼠标在屏幕上的位置(screenpoint),利用它,我们得到了锚定像素和屏幕边缘之间的距离(根据定义)
  • 我们使用screenpoint和position查找鼠标在图像平面上的坐标(即:悬停像素的2D索引),作为oldpoint
  • 应用我们的缩放,我们计算像素的新2D索引(新点)
  • 我们希望这个像素出现在屏幕上,但不在左上角:我们希望它是左上角(位置)的dx和dy
问题确实是图像坐标和显示坐标之间的一个细微的混淆

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)