如何在PyQt5中为无边框主窗口实现自定义调整大小框架?
实现窗口边缘/角落的光标切换功能
修复QGridLayout方案的主窗口扩展问题
你用QGridLayout布局8个边缘框架+1个主窗口的思路是可行的,主窗口无法扩展是因为没设置布局拉伸因子,让中间网格占据剩余空间。以下是修正后的实现步骤(以PyQt5为例,C++ Qt逻辑一致):
- 创建所有框架和主窗口,设置边缘框架的固定尺寸:
from PyQt5.QtWidgets import QWidget, QFrame, QGridLayout from PyQt5.QtCore import Qt # 初始化容器和组件 container = QWidget() grid_layout = QGridLayout(container) # 创建8个边缘框架 top_left = QFrame() top = QFrame() top_right = QFrame() left = QFrame() right = QFrame() bottom_left = QFrame() bottom = QFrame() bottom_right = QFrame() main_widget = QWidget() # 你的主内容窗口 # 设置边缘框架的固定尺寸 edge_size = 10 top_left.setFixedSize(edge_size, edge_size) top.setFixedHeight(edge_size) top_right.setFixedSize(edge_size, edge_size) left.setFixedWidth(edge_size) right.setFixedWidth(edge_size) bottom_left.setFixedSize(edge_size, edge_size) bottom.setFixedHeight(edge_size) bottom_right.setFixedSize(edge_size, edge_size)
- 将组件添加到网格,并设置拉伸因子让主窗口占满空间:
# 填充网格布局 grid_layout.addWidget(top_left, 0, 0) grid_layout.addWidget(top, 0, 1) grid_layout.addWidget(top_right, 0, 2) grid_layout.addWidget(left, 1, 0) grid_layout.addWidget(main_widget, 1, 1) grid_layout.addWidget(right, 1, 2) grid_layout.addWidget(bottom_left, 2, 0) grid_layout.addWidget(bottom, 2, 1) grid_layout.addWidget(bottom_right, 2, 2) # 设置拉伸因子:第1行、第1列(主窗口所在网格)占据所有剩余空间 grid_layout.setRowStretch(1, 1) grid_layout.setColumnStretch(1, 1)
- 给每个边缘框架添加光标切换逻辑:
可以通过重写框架的enterEvent和leaveEvent,或者安装事件过滤器:
# 示例:给左上框架设置对角线光标 def set_cursor_on_enter(frame, cursor): def enter_event(event): frame.setCursor(cursor) def leave_event(event): frame.setCursor(Qt.ArrowCursor) frame.enterEvent = enter_event frame.leaveEvent = leave_event # 为每个边缘框架绑定对应光标 set_cursor_on_enter(top_left, Qt.SizeFDiagCursor) set_cursor_on_enter(top, Qt.SizeVerCursor) set_cursor_on_enter(top_right, Qt.SizeBDiagCursor) set_cursor_on_enter(left, Qt.SizeHorCursor) set_cursor_on_enter(right, Qt.SizeHorCursor) set_cursor_on_enter(bottom_left, Qt.SizeBDiagCursor) set_cursor_on_enter(bottom, Qt.SizeVerCursor) set_cursor_on_enter(bottom_right, Qt.SizeFDiagCursor)
更简洁的“单容器+鼠标位置判断”方案
不需要额外创建8个框架,直接在主容器中监听鼠标位置,根据相对窗口边界的距离切换光标,逻辑更简洁:
class ResizableContainer(QWidget): def __init__(self, parent=None): super().__init__(parent) self.setMouseTracking(True) # 开启无按压状态下的鼠标跟踪 self.border_trigger_width = 10 # 触发光标切换的边缘宽度 def mouseMoveEvent(self, event): pos = event.pos() window_rect = self.rect() current_cursor = Qt.ArrowCursor # 判断角落区域 if pos.x() <= self.border_trigger_width and pos.y() <= self.border_trigger_width: current_cursor = Qt.SizeFDiagCursor elif pos.x() >= window_rect.width() - self.border_trigger_width and pos.y() <= self.border_trigger_width: current_cursor = Qt.SizeBDiagCursor elif pos.x() <= self.border_trigger_width and pos.y() >= window_rect.height() - self.border_trigger_width: current_cursor = Qt.SizeBDiagCursor elif pos.x() >= window_rect.width() - self.border_trigger_width and pos.y() >= window_rect.height() - self.border_trigger_width: current_cursor = Qt.SizeFDiagCursor # 判断上下边缘 elif pos.y() <= self.border_trigger_width or pos.y() >= window_rect.height() - self.border_trigger_width: current_cursor = Qt.SizeVerCursor # 判断左右边缘 elif pos.x() <= self.border_trigger_width or pos.x() >= window_rect.width() - self.border_trigger_width: current_cursor = Qt.SizeHorCursor self.setCursor(current_cursor) # 使用方式:将你的主窗口放入这个容器 container = ResizableContainer() main_widget = QWidget() layout = QVBoxLayout(container) layout.addWidget(main_widget) layout.setContentsMargins(0,0,0,0) # 取消内边距,让主窗口占满容器
这个方案不需要额外的布局组件,通过mouseMoveEvent直接判断鼠标位置,避免了布局拉伸的问题,代码量更少。
内容的提问来源于stack exchange,提问作者Slender548
相关产品推荐
相关产品推荐

