好不容易缕清了之前做的图片根据鼠标指向进行放大缩小功能,现在来记录一下。

注: 以下内容是紧接着: 的内容的,之前演示了如何移动和缩放label显示的图片,以下内容演示如何让label跟随鼠标指针指向来缩放。如果大佬有更好的方法,希望能够指点一二。

稍微构想一下,想要实现根据鼠标当前坐标放大图片,图片放大是完全不影响的,需要考虑的是图片的坐标,即让鼠标当前指向的图片上的位置所指向的点保持不动。

1、解释方法之前,先看我做的两个例子:

  • 以下代码都是发生在滚轮事件中,滚动时会一直触发的。
  • 移动图片和图片的放缩是通过设置labelsetGeometry属性实现的。
(1)沿着label的左上角点放缩:

此方法只要将左上角的点固定就行:

# 这句话中的各个参数分别代表了:
# lflabel1: 显示图片的label
# self.labelx: 指向lflabel1的左上角点的x坐标,除非发生拖拽,否则保持不动
# self.labely: 指向lflabel1的左上角点的y坐标,除非发生拖拽,否则保持不动
# self.label_wid: 指向lflabel1的当前宽度
# self.label_hig: 指向lflabel1的当前高度
self.ui.lflabel1.setGeometry(QtCore.QRect(
                                  self.labelx,
                                  self.labely,
                                  self.label_wid,
                                  self.label_hig))

pyqt mouseMoveEvent 判断鼠标按下 pyqt 鼠标悬停放大图片_宽高


那么label就会沿着lflabel1的左上角的点进行放缩。

(2)沿着label的右下角点放缩:
#写的有些复杂,慢慢解释吧
#resize_point: 是之前定义的缩放系数
#cur_lab_shape:指代label当前的宽高和channel的组

#int((1-self.resize_point/10)*self.cur_lab_shape[1]):
#                表示放缩系数乘以label当前的宽,用1去剪是因为1是下限,减去量是放缩系数的百分比
#int((1-self.resize_point/10)*self.cur_lab_shape[0]):
#                表示放缩系数乘以label当前的高,用1去剪是因为1是下限,减去量是放缩系数的百分比
self.ui.lflabel1.setGeometry(QtCore.QRect(
                         self.labelx+int((1-self.resize_point/10)*self.cur_lab_shape[1]),
                         self.labely+int((1-self.resize_point/10)*self.cur_lab_shape[0]),
                         self.label_wid,
                         self.label_hig))

其实这段代码不用看,就是把图片的放缩量的宽和高,加到左上角的x和y坐标上去。

pyqt mouseMoveEvent 判断鼠标按下 pyqt 鼠标悬停放大图片_宽高_02


那么label就会沿着lflabel1的右下角的点进行放缩。

同理,我只要把加数改为:

int((1-self.resize_point/10)*self.cur_lab_shape[1]/2)

也就是除以了一个2,那label就会变成沿着label的中心点坐标进行放缩。

2、沿着鼠标当前指向的点:

如前面所说,想要使label根据鼠标的指向的点进行放缩,只要把鼠标指向的点固定就行了。然而说的是很简单,但是想要实现可太难了。不使劲想还真想像不出来,我之前也是绞尽脑汁想了很久,然后不断地改参数,不断修改才实现的。

我想到的方法:获取鼠标相对于label的比值!

pyqt mouseMoveEvent 判断鼠标按下 pyqt 鼠标悬停放大图片_鼠标坐标_03


将我鼠标的坐标,与中点或是右下角的点的比值,替换到我的缩放位移中去。

既然放缩系数除以2,就能使它沿着中心点坐标进行放缩,那我把放缩量乘以一个比值,是不是就能根据这个比值量指向的点位来放缩呢?

(1)比如我这样写:
self.ui.lflabel1.setGeometry(QtCore.QRect(
                   self.labelx+int((1-self.resize_point/10)*self.cur_lab_shape[1]/3),
                   self.labely+int((1-self.resize_point/10)*self.cur_lab_shape[0]/2),
                   self.label_wid,
                   self.label_hig))

那放缩时,就会沿着宽度的1/3处,高度的1/2处的点进行放缩。

(2)鼠标坐标的占比:

那我就要获取鼠标坐标对应图片宽高的占比。

大致顺序为:

获得鼠标相对于图片窗口中的位置,将该相对于label的横坐标除以label的宽,相对于label的纵坐标除以label的高,这样两个比值就有了,接着将这个比值替换之前的除数(由于这里是比值,所以应该改成乘)

代码实现:(一句一句解释吧)
# 获取鼠标的横纵坐标,这是相对于整个软件界面的坐标
x_1,y_1 = pag.position()
# 计算鼠标在窗口中的相对于label的横纵坐标
# 这里减去10是去除外部框体,self.ui.lflabel1.x()表示label的横坐标,在label之外的部分,要剔除
x_12 = (x_1 -10 - self.ui.lflabel1.x() )/self.ui.lflabel1.width()
# 同上
y_12 = (y_1 - 135 - self.ui.lflabel1.y())/self.ui.lflabel1.height()
# 由于当x_12小于0或大于1的时候,表示鼠标在窗体外,这时如果按鼠标位置缩放,容易跳出屏幕,所以转int判断
if int(x_12)==0 & int(y_12)==0: 
    # 这里为什么添加一个中转变量呢,因为即使我上面添加了判断,鼠标只要去到过label之外的区域,
    # 上一个值还是会影响我的下一次缩放,导致跳出窗体
    x_11 = x_12
    y_11 = y_12
    self.ui.lflabel1.setGeometry(QtCore.QRect(
                    # 由于除数是个比值,所以将前面的除以 2 替换成乘以 x_11
                    self.labelx +int((1-self.resize_point/10)*self.cur_lab_shape[1]*x_11),
                    self.labely + int((1-self.resize_point/10)*self.cur_lab_shape[0]*y_11),
                    self.label_wid,
                    self.label_hig))

运行结果:

由于只能上传5M以内的gif,抽帧导致效果不是特别明显,但是单是对比图中的第一次缩放和最后的缩放可以明显看到,第一次鼠标在最右,图片缩放后图片右侧没有和外框触碰,之后的缩放都与右侧外框接触了。

pyqt mouseMoveEvent 判断鼠标按下 pyqt 鼠标悬停放大图片_缩放_04

哈哈,然后这个问题解决了,但其实我的工作量并不止这么多,我还要把标注的图案一边对应坐标映射到原图上,然后再显示到label中,一边还要把图案映射到标注的 ground truth 图像上,可以说是困难得多!等有空的时候我会在下面添加一个完整的示例的代码。