移除边界条件

main
COT001\DEV 2026-04-01 13:56:14 +08:00
parent d074ac4e16
commit 6d41fbf83d
1 changed files with 10 additions and 195 deletions

205
main.py
View File

@ -109,9 +109,6 @@ class InfluxSettings:
class DashboardItem(QGraphicsRectItem):
"""可拖拽、可简单缩放的组件基类"""
# 磁吸阈值:距离边缘多少像素时触发磁吸
SNAP_THRESHOLD = 10.0
def __init__(self, x: float, y: float, w: float, h: float, parent=None):
super().__init__(parent)
self.setRect(QRectF(0, 0, w, h))
@ -131,8 +128,7 @@ class DashboardItem(QGraphicsRectItem):
self._resize_margin = 8
self._drag_start_rect: Optional[QRectF] = None
self._drag_start_pos = None
self._resize_edge = None # "left", "right", "top", "bottom", "corner" 等
self._snapping = False # 是否正在磁吸,避免递归调用
self._resize_edge = None
def _is_in_resize_area(self, pt: QPointF, r: QRectF) -> Optional[str]:
"""检测鼠标是否在可调整大小的区域,返回边缘标识"""
@ -264,9 +260,13 @@ class DashboardItem(QGraphicsRectItem):
self._resize_edge = None
self._drag_start_rect = None
self._drag_start_pos = None
# 调整大小结束后强制更新,确保位置和大小变化被记录
self.update()
event.accept()
return
# 拖拽结束后也强制更新
super().mouseReleaseEvent(event)
self.update()
# 高亮选中效果
def paint(self, painter, option, widget=None):
@ -312,149 +312,11 @@ class DashboardItem(QGraphicsRectItem):
"""子类可重写:在几何变化后调整内部元素(例如 Web 视图大小、图片缩放等)"""
pass
def _get_canvas_bounds(self) -> Optional[QRectF]:
"""获取画布的边界矩形scene 坐标)"""
scene = self.scene()
if not scene or not isinstance(scene, DashboardScene):
return None
canvas_item = scene.canvas_item
if not canvas_item:
return None
# 画布的实际边界 = canvas_item 的位置 + rect
canvas_pos = canvas_item.pos()
canvas_rect = canvas_item.rect()
return QRectF(
canvas_pos.x(),
canvas_pos.y(),
canvas_rect.width(),
canvas_rect.height()
)
def _get_other_items_bounds(self) -> list[QRectF]:
"""获取场景中其他组件的边界矩形列表scene 坐标)"""
scene = self.scene()
if not scene:
return []
bounds = []
for item in scene.items():
if (isinstance(item, DashboardItem) and
item is not self and
item is not scene.canvas_item):
pos = item.pos()
rect = item.rect()
bounds.append(QRectF(
pos.x(),
pos.y(),
rect.width(),
rect.height()
))
return bounds
def itemChange(self, change: QGraphicsItem.GraphicsItemChange, value):
"""重写 itemChange 以实现磁吸功能(画布边缘 + 组件间吸附)"""
# 箭头组件和标签组件不进行边界限制和磁吸,直接返回
if isinstance(self, (ArrowItem, LabelItem)):
return super().itemChange(change, value)
if change == QGraphicsItem.GraphicsItemChange.ItemPositionChange and not self._snapping:
# 位置变化时,检测是否需要磁吸
new_pos: QPointF = value
r = self.rect()
# 组件在 scene 坐标下的边界
item_left = new_pos.x()
item_top = new_pos.y()
item_right = item_left + r.width()
item_bottom = item_top + r.height()
final_x = new_pos.x()
final_y = new_pos.y()
# 1. 检测画布边缘磁吸 + 边界限制
canvas_bounds = self._get_canvas_bounds()
canvas_left = None
canvas_top = None
canvas_right = None
canvas_bottom = None
if canvas_bounds:
canvas_left = canvas_bounds.x()
canvas_top = canvas_bounds.y()
canvas_right = canvas_left + canvas_bounds.width()
canvas_bottom = canvas_top + canvas_bounds.height()
# 左边缘磁吸
if abs(item_left - canvas_left) < self.SNAP_THRESHOLD:
final_x = canvas_left
# 右边缘磁吸
elif abs(item_right - canvas_right) < self.SNAP_THRESHOLD:
final_x = canvas_right - r.width()
# 上边缘磁吸
if abs(item_top - canvas_top) < self.SNAP_THRESHOLD:
final_y = canvas_top
# 下边缘磁吸
elif abs(item_bottom - canvas_bottom) < self.SNAP_THRESHOLD:
final_y = canvas_bottom - r.height()
# 2. 检测其他组件的磁吸(组件间对齐)
other_bounds = self._get_other_items_bounds()
for other_rect in other_bounds:
other_left = other_rect.x()
other_top = other_rect.y()
other_right = other_left + other_rect.width()
other_bottom = other_top + other_rect.height()
# 左边缘对齐(当前组件的左边缘对齐到其他组件的左边缘)
if abs(item_left - other_left) < self.SNAP_THRESHOLD:
final_x = other_left
# 右边缘对齐(当前组件的右边缘对齐到其他组件的右边缘)
elif abs(item_right - other_right) < self.SNAP_THRESHOLD:
final_x = other_right - r.width()
# 当前组件的左边缘对齐到其他组件的右边缘(相邻)
elif abs(item_left - other_right) < self.SNAP_THRESHOLD:
final_x = other_right
# 当前组件的右边缘对齐到其他组件的左边缘(相邻)
elif abs(item_right - other_left) < self.SNAP_THRESHOLD:
final_x = other_left - r.width()
# 上边缘对齐
if abs(item_top - other_top) < self.SNAP_THRESHOLD:
final_y = other_top
# 下边缘对齐
elif abs(item_bottom - other_bottom) < self.SNAP_THRESHOLD:
final_y = other_bottom - r.height()
# 当前组件的上边缘对齐到其他组件的下边缘(相邻)
elif abs(item_top - other_bottom) < self.SNAP_THRESHOLD:
final_y = other_bottom
# 当前组件的下边缘对齐到其他组件的上边缘(相邻)
elif abs(item_bottom - other_top) < self.SNAP_THRESHOLD:
final_y = other_top - r.height()
# 3. 限制组件不能超出画布边界(在磁吸之后再次检查,确保不会超出)
if canvas_bounds and canvas_left is not None:
# 重新计算组件边界(因为 final_x/final_y 可能已经被磁吸调整过)
item_left = final_x
item_top = final_y
item_right = item_left + r.width()
item_bottom = item_top + r.height()
# 限制在画布范围内
if item_left < canvas_left:
final_x = canvas_left
if item_top < canvas_top:
final_y = canvas_top
if item_right > canvas_right:
final_x = canvas_right - r.width()
if item_bottom > canvas_bottom:
final_y = canvas_bottom - r.height()
# 如果位置被调整了,返回调整后的位置
if final_x != new_pos.x() or final_y != new_pos.y():
self._snapping = True
return QPointF(final_x, final_y)
elif change == QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged:
# 位置变化完成后,重置磁吸标志
self._snapping = False
"""位置变化时的处理"""
if change == QGraphicsItem.GraphicsItemChange.ItemPositionHasChanged:
# 位置变化完成后强制更新
self.update()
return super().itemChange(change, value)
@ -958,34 +820,10 @@ class DashboardView(QGraphicsView):
# 布局持久化
def save_layout(self, path: str):
states = []
canvas_bounds = self._scene.canvas_rect()
canvas_pos = self._scene.canvas_item.pos()
canvas_left = canvas_pos.x()
canvas_top = canvas_pos.y()
canvas_right = canvas_left + canvas_bounds.width()
canvas_bottom = canvas_top + canvas_bounds.height()
for it in self._scene.items():
if isinstance(it, DashboardItem):
# 获取组件状态
state = it.to_state()
state_dict = asdict(state)
# 修正超出边界的位置数据(直接修改字典,不移动组件)
item_left = state_dict["x"]
item_top = state_dict["y"]
item_w = state_dict["w"]
item_h = state_dict["h"]
# 修正超出边界的位置数据
final_x = max(canvas_left, min(item_left, canvas_right - item_w))
final_y = max(canvas_top, min(item_top, canvas_bottom - item_h))
# 更新状态数据
state_dict["x"] = final_x
state_dict["y"] = final_y
states.append(state_dict)
states.append(asdict(state))
with open(path, "w", encoding="utf-8") as f:
payload = {
@ -1026,7 +864,6 @@ class DashboardView(QGraphicsView):
try:
ws = WidgetState(**st)
except TypeError:
# 旧数据或无效条目,跳过
continue
if ws.widget_type == "image":
@ -1040,28 +877,6 @@ class DashboardView(QGraphicsView):
else:
continue
it.apply_state(ws)
# 加载后也应用边界限制,确保组件不超出画布
canvas_bounds = self._scene.canvas_rect()
canvas_pos = self._scene.canvas_item.pos()
canvas_left = canvas_pos.x()
canvas_top = canvas_pos.y()
canvas_right = canvas_left + canvas_bounds.width()
canvas_bottom = canvas_top + canvas_bounds.height()
pos = it.pos()
r = it.rect()
item_left = pos.x()
item_top = pos.y()
item_right = item_left + r.width()
item_bottom = item_top + r.height()
# 修正超出边界的位置
final_x = max(canvas_left, min(item_left, canvas_right - r.width()))
final_y = max(canvas_top, min(item_top, canvas_bottom - r.height()))
if final_x != item_left or final_y != item_top:
it.setPos(final_x, final_y)
# -----------------------------