使用PyQt5实现控件移动和缩放,以及相对鼠标中心的缩放

1.效果展示

支持对控件移动,缩放,按鼠标中心缩放,双击复原的功能。
贴有完整代码在末尾。

单移动效果

2.单移动功能实现

单移动

使用self.move_flag定义移动标记
使用self.move_px_x,self.move_px_y定义控件移动的绝对偏移量

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys

class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.move_flag = False  # 开启移动的标记
        self.move_px_x = 0 # 定义移动控件相对原始坐标移动的位置x
        self.move_px_y = 0 # 定义移动控件相对原始坐标移动的位置y
        self.setup()

    # 初始化
    def setup(self):
        btn = QPushButton(self)
        btn.move(220,110)
        btn.resize(200,100)
        self.all_need_move_widget = [btn,btn.x(),btn.y(),btn.width(),btn.height()] # 保持获得控件的原始位置

    # 更新控件位置 移动
    def ChangeWidgetXY(self):
        child,original_x,original_y,original_width,original_height = self.all_need_move_widget
        child.move(original_x+self.move_px_x,original_y+self.move_px_y)

    # 鼠标按下事件
    def mousePressEvent(self, a0: QMouseEvent) -> None:
        if a0.button() == Qt.MouseButton.MiddleButton:  # 限定鼠标中键
            self.lock_glox_pre,self.lock_gloy_pre = a0.globalX(),a0.globalY() # 锁定鼠标点击相对屏幕的坐标
            self.lock_move_px_x, self.lock_move_px_y = self.move_px_x,self.move_px_y # 锁定移动控件相对原始坐标移动的位置
            self.move_flag = True # 开启移动的标记
        return super().mousePressEvent(a0)   
    # 鼠标松开事件
    def mouseReleaseEvent(self, a0: QMouseEvent) -> None:
        self.move_flag = False  # 关闭移动的标记
        return super().mouseReleaseEvent(a0) 
    # 鼠标移动事件
    def mouseMoveEvent(self, a0: QMouseEvent) -> None:
        move_count = 1 # 移动比例系数
        if self.move_flag == False:
            return super().mouseMoveEvent(a0) # 如果关闭,退出  
        # 得到移动向量=用新的坐标减去原来的坐标 再乘以移动比例系数 最后加上锁定坐标
        self.move_px_x = (a0.globalX() - self.lock_glox_pre)*move_count+ self.lock_move_px_x
        self.move_px_y = (a0.globalY() - self.lock_gloy_pre)*move_count+ self.lock_move_px_y
        self.ChangeWidgetXY()# 更新控件位置 移动和缩放
        return super().mouseMoveEvent(a0)

if __name__ == '__main__':
    # 第一:读取cmd命令
    app = QApplication(sys.argv)
    # 第二:创建QWidget控件
    window = Window()
    # 第三:显示窗口(QWidget.show方法)
    window.show()
    # 第四:开启事件循环
    sys.exit(app.exec_())

3.加入缩放功能

可以实现移动和缩放,但是只能基于屏幕原点缩放。
在这里插入图片描述
1.使用 self.sca_num = 1 定义放大倍数;统一使用ChangeWidgetXY函数更新位置大小。
2.核心是在move()方法中需要加上缩放量:

child.move(int((original_x+self.move_px_x) * self.sca_num),int((original_y+self.move_px_y) * self.sca_num))
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys

class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.move_flag = False  # 开启移动的标记
        self.move_px_x = 0 # 定义移动控件相对原始坐标移动的位置x
        self.move_px_y = 0 # 定义移动控件相对原始坐标移动的位置y
        self.sca_num = 1 # 定义放大倍数
        self.setup()
        
    # 初始化
    def setup(self):
        self.btn1 = QPushButton(self)
        self.btn1.move(100,100)
        self.btn1.resize(200,100)
        self.btn2 = QPushButton(self)
        self.btn2.move(300,120)
        self.btn2.resize(200,100)
        self.GetOriginalWidgetXY() # 遍历所需要更改位置的控件,获得控件的位置
    # 遍历所有需要更改位置的控件的原始位置信息
    def GetOriginalWidgetXY(self):
        # 遍历所有的子控
        self.all_need_move_widget = []
        for child in self.children(): # 获得所有父控件
            if child.inherits("QPushButton"): # 筛选
                self.all_need_move_widget.append([child,child.pos(),child.size()])

    # 更新控件位置 移动和缩放
    def ChangeWidgetXY(self):
        # 更新大小和位置
        for move_child in self.all_need_move_widget:
            child,original_x,original_y,original_width,original_height = move_child[0],move_child[1].x(),move_child[1].y(),move_child[2].width(),move_child[2].height()
            child.move(int((original_x+self.move_px_x) * self.sca_num),int((original_y+self.move_px_y) * self.sca_num))
            child.resize(int(original_width * self.sca_num),int(original_height * self.sca_num))

    # 鼠标按下事件
    def mousePressEvent(self, a0: QMouseEvent) -> None:
        if a0.button() == Qt.MouseButton.MiddleButton:  # 限定鼠标中键
            self.lock_glox_pre,self.lock_gloy_pre = a0.globalX(),a0.globalY() # 锁定鼠标点击相对屏幕的坐标
            self.lock_move_px_x, self.lock_move_px_y = self.move_px_x,self.move_px_y # 锁定移动控件相对原始坐标移动的位置
            self.move_flag = True # 开启移动的标记
        return super().mousePressEvent(a0)   
    # 鼠标松开事件
    def mouseReleaseEvent(self, a0: QMouseEvent) -> None:
        self.move_flag = False  # 关闭移动的标记
        return super().mouseReleaseEvent(a0) 
    # 鼠标移动事件
    def mouseMoveEvent(self, a0: QMouseEvent) -> None:
        move_count = 1 # 移动比例系数
        if self.move_flag == False:
            return super().mouseMoveEvent(a0) # 如果关闭,退出  
        # 得到移动向量=用新的坐标减去原来的坐标 再乘以移动比例系数 最后加上锁定坐标
        self.move_px_x = (a0.globalX() - self.lock_glox_pre)*move_count+ self.lock_move_px_x
        self.move_px_y = (a0.globalY() - self.lock_gloy_pre)*move_count+ self.lock_move_px_y
        self.ChangeWidgetXY()# 更新控件位置 移动和缩放
        return super().mouseMoveEvent(a0)
   # 滚轮滚动事件
    def wheelEvent(self, a0: QWheelEvent) -> None:
        sca_count = 0.1 # 缩放比例系数
        angleY = a0.angleDelta().y() # 得到滚轮信息
        if angleY>0: # 放大
            self.sca_num  = self.sca_num * (1 + sca_count)
        else:
            self.sca_num  = self.sca_num * (1 - sca_count)
        self.ChangeWidgetXY()# 更新控件位置 移动和缩放
        return super().wheelEvent(a0)

if __name__ == '__main__':
    # 第一:读取cmd命令
    app = QApplication(sys.argv)
    # 第二:创建QWidget控件
    window = Window()
    # 第三:显示窗口(QWidget.show方法)
    window.show()
    # 第四:开启事件循环
    sys.exit(app.exec_())

4.基于鼠标位置的缩放-已鼠标为中心缩放

在这里插入图片描述

核心是ChangeWidgetXY()函数。
具体原理不做解释


from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from DeviceBlockWidget import DeviceBlock
import sys
import time
"""
QWidget的提升控件类
"""

class ImportWidgetWindow(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.move_flag = False  # 开启移动的标记
        self.move_px_x = 0 # 定义移动控件相对原始坐标移动的位置x
        self.move_px_y = 0 # 定义移动控件相对原始坐标移动的位置y
        self.sca_num = 1 # 定义放大倍数
        self.setup_ui()

    def setup_ui(self):
        self.btn1 = QPushButton(self)
        self.btn1.move(150,150)
        self.btn1.resize(200,100)
        self.btn2 = QPushButton(self)
        self.btn2.move(300,120)
        self.btn2.resize(200,100)
        self.GetOriginalWidgetXY() # 遍历所需要更改位置的控件,获得控件的位置
    # 遍历所有需要更改位置的控件的原始位置信息
    def GetOriginalWidgetXY(self):
        # 遍历所有的子控
        self.all_need_move_widget = []
        for child in self.children(): # 获得所有父控件
            if child.inherits("QPushButton"): # 筛选
                self.all_need_move_widget.append([child,child.pos(),child.size()])
    # 更新控件位置 移动和缩放 mouse_x,mouse_y:表示基于那个点缩放,sca_count:缩放的大小(若将mouse_x,mouse_y与鼠标位置关联,则可以实现以鼠标为中心缩放)
    def ChangeWidgetXY(self,mouse_x=0,mouse_y=0,sca_count=0):
        # 第一步:检查数据
        if self.sca_num == 0: self.sca_num = 1 # 除数不能为零
        if sca_count == 0: sca_count = 1 # 除数不能为零
        # 第二步:保存原来的放大倍数,和更新新的放大倍数
        sca_num_old = self.sca_num
        self.sca_num = self.sca_num * sca_count
        # 第三步:计算补偿量
        move_compensation_x = mouse_x / sca_count - mouse_x # 变化后的坐标 - 原来的坐标
        move_compensation_y = mouse_y / sca_count - mouse_y
        # 第四步:更新移动量
        self.move_px_x = self.move_px_x + move_compensation_x/sca_num_old # 旧的移动量 + 补偿
        self.move_px_y = self.move_px_y + move_compensation_y/sca_num_old
        # 第五步:遍历所有的子控件,更新大小和位置
        for move_child in self.all_need_move_widget:
            child,original_x,original_y,original_width,original_height = move_child[0],move_child[1].x(),move_child[1].y(),move_child[2].width(),move_child[2].height()
            child.move(int((original_x+self.move_px_x)*self.sca_num),int((original_y+self.move_px_y)*self.sca_num))
            child.resize(int(original_width * self.sca_num),int(original_height * self.sca_num))

    # 鼠标按下事件
    def mousePressEvent(self, a0: QMouseEvent) -> None:
        if a0.button() == Qt.MouseButton.MiddleButton:  # 限定鼠标中键
            self.lock_glox_pre,self.lock_gloy_pre = a0.globalX(),a0.globalY() # 锁定鼠标点击相对屏幕的坐标
            self.lock_move_px_x, self.lock_move_px_y = self.move_px_x,self.move_px_y # 锁定移动控件相对原始坐标移动的位置
            self.move_flag = True
        return super().mousePressEvent(a0)   
    # 鼠标松开事件
    def mouseReleaseEvent(self, a0: QMouseEvent) -> None:
        self.move_flag = False  # 关闭移动的标记
        return super().mouseReleaseEvent(a0) 
    # 鼠标移动事件
    def mouseMoveEvent(self, a0: QMouseEvent) -> None:
        move_count = 1 # 移动比例系数
        if self.move_flag == False:
            return super().mouseMoveEvent(a0) # 如果关闭,退出  
        # 得到移动向量=用新的指标减去原来的指标 再成移动系数 最后加上锁定坐标
        self.move_px_x = (a0.globalX() - self.lock_glox_pre)*move_count/self.sca_num + self.lock_move_px_x
        self.move_px_y = (a0.globalY() - self.lock_gloy_pre)*move_count/self.sca_num + self.lock_move_px_y
        self.ChangeWidgetXY()# 更新控件位置 移动和缩放
        return super().mouseMoveEvent(a0)
   # 滚轮滚动事件
    def wheelEvent(self, a0: QWheelEvent) -> None:
        sca_count = 1.1 # 放大比例系数,大于0
        angleY = a0.angleDelta().y() # 得到滚轮信息
        if angleY>0: # 放大
            sca_count = sca_count
        else:
            sca_count = 1 / sca_count
        self.ChangeWidgetXY(mouse_x=a0.x(),mouse_y=a0.y(),sca_count=sca_count)# 更新控件位置 移动和缩放
        return super().wheelEvent(a0)

if __name__ == '__main__':
    # 第一:读取cmd命令
    app = QApplication(sys.argv)
    # 第二:创建QWidget控件
    window = ImportWidgetWindow()
    # 第三:显示窗口(QWidget.show方法)
    window.show()
    # 第四:开启事件循环
    sys.exit(app.exec_())

5.加入双击复原功能,完整代码如下:


from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys

class Window(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.move_flag = False  # 开启移动的标记
        self.move_px_x = 0 # 定义移动控件相对原始坐标移动的位置x
        self.move_px_y = 0 # 定义移动控件相对原始坐标移动的位置y
        self.sca_num = 1 # 定义放大倍数
        self.setup()

    # 初始化
    def setup(self):
        def single_click_time():
            self.click_count = 0 # 双击间隔时间到了,清除标志
        self.timer = QTimer() # 定义一个定时器,用于检测鼠标滚轮双击事件
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(single_click_time)
        self.click_count = 0 # 双击标志
        # 加入控件
        for i in range(4):
            for j in range(3):
                btn = QPushButton(self)
                btn.move(220*i,110*j)
                btn.resize(200,100)
        # 遍历所需要更改位置的控件,获得控件的原始位置
        self.GetOriginalWidgetXY()

    # 遍历所有需要更改位置的控件的原始位置信息
    def GetOriginalWidgetXY(self):
        # 遍历所有的子控,保持到列表里
        self.all_need_move_widget = []
        for child in self.children(): # 获得所有父控件
            if child.inherits("QPushButton"): # 筛选
                self.all_need_move_widget.append([child,child.pos(),child.size()])
    # 更新控件位置 移动和缩放 mouse_x,mouse_y:表示基于那个点缩放,sca_count:缩放的大小(若将mouse_x,mouse_y与鼠标位置关联,则可以实现以鼠标为中心缩放)
    def ChangeWidgetXY(self,mouse_x=0,mouse_y=0,sca_count=0):
        # 第一步:检查数据
        if self.sca_num == 0: self.sca_num = 1 # 除数不能为零
        if sca_count == 0: sca_count = 1 # 除数不能为零
        # 第二步:保存原来的放大倍数,和更新新的放大倍数
        sca_num_old = self.sca_num
        self.sca_num = self.sca_num * sca_count
        # 第三步:计算补偿量
        move_compensation_x = mouse_x / sca_count - mouse_x # 变化后的坐标 - 原来的坐标
        move_compensation_y = mouse_y / sca_count - mouse_y
        # 第四步:更新移动量
        self.move_px_x = self.move_px_x + move_compensation_x/sca_num_old # 旧的移动量 + 补偿
        self.move_px_y = self.move_px_y + move_compensation_y/sca_num_old
        # 第五步:遍历所有的子控件,更新大小和位置
        for move_child in self.all_need_move_widget:
            child,original_x,original_y,original_width,original_height = move_child[0],move_child[1].x(),move_child[1].y(),move_child[2].width(),move_child[2].height()
            child.move(int((original_x+self.move_px_x)*self.sca_num),int((original_y+self.move_px_y)*self.sca_num))
            child.resize(int(original_width * self.sca_num),int(original_height * self.sca_num))

    # 鼠标按下事件
    def mousePressEvent(self, a0: QMouseEvent) -> None:
        if a0.button() == Qt.MouseButton.MiddleButton:  # 限定鼠标中键
            self.lock_glox_pre,self.lock_gloy_pre = a0.globalX(),a0.globalY() # 锁定鼠标点击相对屏幕的坐标
            self.lock_move_px_x, self.lock_move_px_y = self.move_px_x,self.move_px_y # 锁定移动控件相对原始坐标移动的位置
            self.move_flag = True
            # 监控双击事件代码
            self.click_count += 1
            if self.click_count == 1:
                self.timer.start(250)  # 设置定时器时间(单位:毫秒),例如 250 毫秒
            elif self.click_count == 2:
                self.timer.stop()
                self.click_count = 0
                self.move_px_x = self.move_px_y = 0
                self.sca_num = 1
                self.ChangeWidgetXY()
        return super().mousePressEvent(a0)   
    # 鼠标松开事件
    def mouseReleaseEvent(self, a0: QMouseEvent) -> None:
        self.move_flag = False  # 关闭移动的标记
        return super().mouseReleaseEvent(a0) 
    # 鼠标移动事件
    def mouseMoveEvent(self, a0: QMouseEvent) -> None:
        move_count = 1 # 移动比例系数
        if self.move_flag == False:
            return super().mouseMoveEvent(a0) # 如果关闭,退出  
        # 得到移动向量=用新的坐标减去原来的坐标 再乘以移动比例系数 最后加上锁定坐标
        self.move_px_x = (a0.globalX() - self.lock_glox_pre)*move_count/self.sca_num + self.lock_move_px_x
        self.move_px_y = (a0.globalY() - self.lock_gloy_pre)*move_count/self.sca_num + self.lock_move_px_y
        self.ChangeWidgetXY()# 更新控件位置 移动和缩放
        return super().mouseMoveEvent(a0)
   # 滚轮滚动事件
    def wheelEvent(self, a0: QWheelEvent) -> None:
        sca_count = 1.1 # 放大比例系数(大于0)
        angleY = a0.angleDelta().y() # 得到滚轮信息
        if angleY>0: # 放大
            sca_count = sca_count
        else: # 缩小
            sca_count = 1 / sca_count
        self.ChangeWidgetXY(mouse_x=a0.x(),mouse_y=a0.y(),sca_count=sca_count)# 更新控件位置 移动和缩放
        return super().wheelEvent(a0)

if __name__ == '__main__':
    # 第一:读取cmd命令
    app = QApplication(sys.argv)
    # 第二:创建QWidget控件
    window = Window()
    # 第三:显示窗口(QWidget.show方法)
    window.show()
    # 第四:开启事件循环
    sys.exit(app.exec_())


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值