注:系列文章,前后关联,请结合完整代码参考本系列文章;现已开源在 GitHub PyOc
参考链接
关于鼠标追踪 (重点) ***
-
属性
mouseTracking : bool
默认值 为False
只在鼠标任一按键按键按下时触发鼠标移动事件 -
方法
void setMouseTracking(bool enable)
设置鼠标追踪属性 -
方法
bool hasMouseTracking()
获取当前鼠标追踪状态
注意:若想在 QMainWindow 中若要开启鼠标追踪,必须:
-
为
主窗口
及centralWidget
同时开启鼠标追踪 -
并且开启所有覆盖在你想开启鼠标追踪区域的所有
子控件
的鼠标追踪
以上两个条件必须同同时满足,否则鼠标追踪会被子控件遮挡,没有效果
核心代码
窗口移动需要使用到鼠标事件,用新旧坐标之差计算偏移量:
def _resize(self, event):
"""实现拖动调整窗口大小的函数
以新旧坐标差计算偏移量,使用 QRect 实例附带位置坐标;
核心算法做了三重校验,以确保任意情况下窗口都能以正确的方式调整大小:
一: 横纵坐标与最值校验,确保在最值范围内调整大小;
二: 横纵坐标与左右区块校验,确保鼠标在窗口边缘时才调整大小;
三: 横纵坐标与极值偏移量校验,确保在改变坐标的情况下,窗口不会发生漂移
"""
# 鼠标在窗口中的区域
area = self._area
# 鼠标偏移量
offsetPos = event.globalPos() - self._posLast
# 鼠标在窗口中的坐标
winPos = event.pos()
# 矩形实例,被赋予窗口的几何属性(x, y, width, height)
# 利用其改变左上角坐标,但右下角坐标不变的特性,实现窗口移动效果
rect = QRect(self.geometry())
x = rect.x()
y = rect.y()
width = rect.width()
height = rect.height()
minWidth = self.minimumWidth()
minHeight = self.minimumHeight()
maxWidth = self.maximumWidth()
maxHeight = self.maximumHeight()
# 根据不同区域选择不同操作
if area == 11:
# 左上
pos = rect.topLeft()
if offsetPos.x() < 0 and width < maxWidth or offsetPos.x() > 0 and width > minWidth:
if offsetPos.x() < 0 and winPos.x() <= 0 or offsetPos.x() > 0 and winPos.x() >= 0:
if (maxWidth - width) >= -offsetPos.x() and (width - minWidth) >= offsetPos.x():
pos.setX(pos.x() + offsetPos.x())
if offsetPos.y() < 0 and height < maxHeight or offsetPos.y() > 0 and height > minHeight:
if offsetPos.y() < 0 and winPos.y() <= 0 or offsetPos.y() > 0 and winPos.y() >= 0:
if (maxHeight - height) >= -offsetPos.y() and (height - minHeight) >= offsetPos.y():
pos.setY(pos.y() + offsetPos.y())
rect.setTopLeft(pos)
elif area == 13:
# 右上
pos = rect.topRight()
if offsetPos.x() < 0 and width > minWidth or offsetPos.x() > 0 and width < maxWidth:
if offsetPos.x() < 0 and winPos.x() <= width or offsetPos.x() > 0 and winPos.x() >= width:
pos.setX(pos.x() + offsetPos.x())
if offsetPos.y() < 0 and height < maxHeight or offsetPos.y() > 0 and height > minHeight:
if offsetPos.y() < 0 and winPos.y() <= 0 or offsetPos.y() > 0 and winPos.y() >= 0:
if (maxHeight - height) >= -offsetPos.y() and (height - minHeight) >= offsetPos.y():
pos.setY(pos.y() + offsetPos.y())
rect.setTopRight(pos)
elif area == 31:
# 左下
pos = rect.bottomLeft()
if offsetPos.x() < 0 and width < maxWidth or offsetPos.x() > 0 and width > minWidth:
if offsetPos.x() < 0 and winPos.x() <= 0 or offsetPos.x() > 0 and winPos.x() >= 0:
if (maxWidth - width) >= -offsetPos.x() and (width - minWidth) >= offsetPos.x():
pos.setX(pos.x() + offsetPos.x())
if offsetPos.y() < 0 and height > minHeight or offsetPos.y() > 0 and height < maxHeight:
if offsetPos.y() < 0 and winPos.y() <= height or offsetPos.y() > 0 and winPos.y() >= height:
pos.setY(pos.y() + offsetPos.y())
rect.setBottomLeft(pos)
elif area == 33:
# 右下
pos = rect.bottomRight()
if offsetPos.x() < 0 and width > minWidth or offsetPos.x() > 0 and width < maxWidth:
if offsetPos.x() < 0 and winPos.x() <= width or offsetPos.x() > 0 and winPos.x() >= width:
pos.setX(pos.x() + offsetPos.x())
if offsetPos.y() < 0 and height > minHeight or offsetPos.y() > 0 and height < maxHeight:
if offsetPos.y() < 0 and winPos.y() <= height or offsetPos.y() > 0 and winPos.y() >= height:
pos.setY(pos.y() + offsetPos.y())
rect.setBottomRight(pos)
elif area == 12:
# 中上
if offsetPos.y() < 0 and height < maxHeight or offsetPos.y() > 0 and height > minHeight:
if offsetPos.y() < 0 and winPos.y() <= 0 or offsetPos.y() > 0 and winPos.y() >= 0:
if (maxHeight - height) >= -offsetPos.y() and (height - minHeight) >= offsetPos.y():
rect.setTop(rect.top() + offsetPos.y())
elif area == 21:
# 中左
if offsetPos.x() < 0 and width < maxWidth or offsetPos.x() > 0 and width > minWidth:
if offsetPos.x() < 0 and winPos.x() <= 0 or offsetPos.x() > 0 and winPos.x() >= 0:
if (maxWidth - width) >= -offsetPos.x() and (width - minWidth) >= offsetPos.x():
rect.setLeft(rect.left() + offsetPos.x())
elif area == 23:
# 中右
if offsetPos.x() < 0 and width > minWidth or offsetPos.x() > 0 and width < maxWidth:
if offsetPos.x() < 0 and winPos.x() <= width or offsetPos.x() > 0 and winPos.x() >= width:
rect.setRight(rect.right() + offsetPos.x())
elif area == 32:
# 中下
if offsetPos.y() < 0 and height > minHeight or offsetPos.y() > 0 and height < maxHeight:
if offsetPos.y() < 0 and winPos.y() <= height or offsetPos.y() > 0 and winPos.y() >= height:
rect.setBottom(rect.bottom() + offsetPos.y())
# 设置窗口几何属性(坐标,宽高)
self.setGeometry(rect)
设置光标图标
def _change_cursor_icon(self, area):
"""改变光标在窗口边缘时的图片"""
# 宽度固定时不应改变宽度
if self.maximumWidth() == self.minimumWidth() and (area == 21 or area == 23):
return None
# 高度固定时不应改变高度
if self.maximumHeight() == self.minimumHeight() and (area == 12 or area == 32):
return None
if area == 11 or area == 33:
self.setCursor(Qt.SizeFDiagCursor) # 倾斜光标
elif area == 12 or area == 32:
self.setCursor(Qt.SizeVerCursor) # 垂直大小光标
elif area == 13 or area == 31:
self.setCursor(Qt.SizeBDiagCursor) # 反倾斜光标
elif area == 21 or area == 23:
self.setCursor(Qt.SizeHorCursor) # 水平大小光标
else:
self.setCursor(Qt.ArrowCursor) # 默认光标
事件
def mousePressEvent(self, event):
"""重写继承的鼠标按住事件"""
self._isPressed = True # 判断是否按下
self._press_button = event.button() # 按下的鼠标按键
self._area = self._compute_area(event.pos()) # 计算鼠标所在区域
self._move_count = 0 # 鼠标移动计数,用于降低灵敏度
self._posLast = event.globalPos() # 当前坐标
return QMainWindow.mousePressEvent(self, event) # 交由原事件函数处理
def mouseReleaseEvent(self, event):
"""重写继承的鼠标释放事件"""
self._isPressed = False # 重置按下状态
self._press_button = None # 清空按下的鼠标按键
self._area = None # 清空鼠标区域
self._move_count = 0 # 清空移动计数
self.setCursor(Qt.ArrowCursor) # 还原光标图标
return QMainWindow.mouseReleaseEvent(self, event)
def mouseMoveEvent(self, event):
"""重写继承的鼠标移动事件,实现窗口移动及拖动改变窗口大小"""
area = self._compute_area(event.pos()) # 计算鼠标区域
# 调整窗口大小及移动
if self._isPressed and self._press_button == Qt.LeftButton:
if self._area == 22:
self._move(event) # 调用移动窗口的函数
elif not self.isMaximized():
self._resize(event) # 调用调整窗口大小的函数
# 更新鼠标全局坐标
self._posLast = event.globalPos()
return None
if not self._isPressed and not self.isMaximized():
# 调整鼠标图标,按下鼠标后锁定状态
self._change_cursor_icon(area)
return QMainWindow.mouseMoveEvent(self, event)
本文为 one-ccs 原创文章,引用必须注明出处!
https://blog.csdn.net/qq_43155814/article/details/104679678