使用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_())