diff --git a/main.py b/main.py index 3b5c162..efb3d73 100644 --- a/main.py +++ b/main.py @@ -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) # -----------------------------