From 2824526939c70fe025ccdad4c344da368b9cbf6b Mon Sep 17 00:00:00 2001 From: Jiayan Chen <3385220387@qq.com> Date: Tue, 28 Oct 2025 10:03:39 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B2=BE=E8=AF=BBview.py=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=B9=B6=E5=8A=A0=E4=B8=8A=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- beeref/main_controls.py | 186 +++++++++++------------ beeref/view.py | 328 ++++++++++++++++++++-------------------- 2 files changed, 257 insertions(+), 257 deletions(-) diff --git a/beeref/main_controls.py b/beeref/main_controls.py index 4848a2f..0344681 100644 --- a/beeref/main_controls.py +++ b/beeref/main_controls.py @@ -23,10 +23,10 @@ from beeref.items import BeePixmapItem from beeref import fileio -logger = logging.getLogger(__name__) +logger = logging.getLogger(__name__) # 主控件日志记录器,用于记录主控件的日志信息 -class MainControlsMixin: +class MainControlsMixin: # 主控件混合类,用于处理主窗口和欢迎叠加层的基本控件 """Basic controls shared by the main view and the welcome overlay: * Right-click menu @@ -34,114 +34,114 @@ class MainControlsMixin: * Moving the window without title bar """ - def init_main_controls(self, main_window): - self.main_window = main_window - self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) - self.customContextMenuRequested.connect( - self.control_target.on_context_menu) - self.setAcceptDrops(True) - self.movewin_active = False + def init_main_controls(self, main_window): # 初始化主控件,用于处理主窗口和欢迎叠加层的基本控件 + self.main_window = main_window # 主窗口引用,用于访问主窗口的属性和方法 + self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) # 设置上下文菜单策略为自定义上下文菜单,用于处理右键点击事件 + self.customContextMenuRequested.connect( # 自定义上下文菜单请求信号槽函数,用于处理右键点击事件 + self.control_target.on_context_menu) # 自定义上下文菜单请求信号槽函数,用于处理右键点击事件,调用控制目标的上下文菜单槽函数 + self.setAcceptDrops(True) # 设置接受拖放事件,用于处理文件拖放事件 + self.movewin_active = False - def on_action_movewin_mode(self): - if self.movewin_active: + def on_action_movewin_mode(self): # 移动窗口模式槽函数,用于处理移动窗口模式的切换 + if self.movewin_active: # 如果移动窗口模式已激活 # Pressing the same shortcut again should end the action - self.exit_movewin_mode() + self.exit_movewin_mode() # 退出移动窗口模式槽函数,用于处理移动窗口模式的切换 else: - self.enter_movewin_mode() + self.enter_movewin_mode() # 进入移动窗口模式槽函数,用于处理移动窗口模式的切换 - @property - def viewport_or_self(self): - if hasattr(self, 'viewport'): - return self.viewport() - return self + @property # viewport_or_self属性,用于返回视口或自身,用于处理移动窗口模式的切换 + def viewport_or_self(self): # viewport_or_self属性,用于返回视口或自身,用于处理移动窗口模式的切换 + if hasattr(self, 'viewport'): # 如果主控件有视口属性 + return self.viewport() # 返回视口 + return self # 返回自身 - def enter_movewin_mode(self): - logger.debug('Entering movewin mode') - self.setMouseTracking(True) - self.movewin_active = True - self.viewport_or_self.setCursor(Qt.CursorShape.SizeAllCursor) - self.event_start = QtCore.QPointF(self.cursor().pos()) - if hasattr(self, 'disable_mouse_events'): - self.disable_mouse_events() + def enter_movewin_mode(self): # 进入移动窗口模式槽函数,用于处理移动窗口模式的切换 + logger.debug('Entering movewin mode') # 进入移动窗口模式槽函数,用于处理移动窗口模式的切换 + self.setMouseTracking(True) # 设置鼠标跟踪,用于处理移动窗口模式的切换 + self.movewin_active = True # 移动窗口模式已激活 + self.viewport_or_self.setCursor(Qt.CursorShape.SizeAllCursor) # 设置视口或自身的光标为大小调整光标,用于处理移动窗口模式的切换 + self.event_start = QtCore.QPointF(self.cursor().pos()) # 记录鼠标点击位置,用于处理移动窗口模式的切换 + if hasattr(self, 'disable_mouse_events'): # 如果主控件有禁用鼠标事件属性 + self.disable_mouse_events() # 禁用鼠标事件,用于处理移动窗口模式的切换 - def exit_movewin_mode(self): - logger.debug('Exiting movewin mode') - self.setMouseTracking(False) - self.movewin_active = False - self.viewport_or_self.unsetCursor() - if hasattr(self, 'enable_mouse_events'): - self.enable_mouse_events() + def exit_movewin_mode(self): # 退出移动窗口模式槽函数,用于处理移动窗口模式的切换 + logger.debug('Exiting movewin mode') # 退出移动窗口模式槽函数,用于处理移动窗口模式的切换 + self.setMouseTracking(False) # 退出移动窗口模式槽函数,用于处理移动窗口模式的切换 + self.movewin_active = False # 移动窗口模式未激活 + self.viewport_or_self.unsetCursor() # 退出移动窗口模式槽函数,用于处理移动窗口模式的切换 + if hasattr(self, 'enable_mouse_events'): # 如果主控件有启用鼠标事件属性 + self.enable_mouse_events() # 启用鼠标事件,用于处理移动窗口模式的切换 - def dragEnterEvent(self, event): - mimedata = event.mimeData() - logger.debug(f'Drag enter event: {mimedata.formats()}') - if mimedata.hasUrls(): - event.acceptProposedAction() - elif mimedata.hasImage(): - event.acceptProposedAction() + def dragEnterEvent(self, event): # 拖入事件槽函数,用于处理拖入事件 + mimedata = event.mimeData() # 获取拖入事件的MIME数据,用于处理拖入事件 + logger.debug(f'Drag enter event: {mimedata.formats()}') # 拖入事件槽函数,用于处理拖入事件,记录拖入事件的MIME数据格式 + if mimedata.hasUrls(): # 如果拖入事件的MIME数据包含URL + event.acceptProposedAction() # 接受拖入事件的建议操作,用于处理拖入事件 + elif mimedata.hasImage(): # 如果拖入事件的MIME数据包含图像 + event.acceptProposedAction() # 接受拖入事件的建议操作,用于处理拖入事件 else: - msg = 'Attempted drop not an image or image too big' - logger.info(msg) - widgets.BeeNotification(self.control_target, msg) + msg = 'Attempted drop not an image or image too big' # 拖入事件槽函数,用于处理拖入事件,记录拖入事件的MIME数据格式 + logger.info(msg) # 拖入事件槽函数,用于处理拖入事件,记录拖入事件的MIME数据格式 + widgets.BeeNotification(self.control_target, msg) # 拖入事件槽函数,用于处理拖入事件,显示拖入事件的MIME数据格式 - def dragMoveEvent(self, event): - event.acceptProposedAction() + def dragMoveEvent(self, event): # 拖动移动事件槽函数,用于处理拖动移动事件 + event.acceptProposedAction() # 接受拖动移动事件的建议操作,用于处理拖动移动事件 - def dropEvent(self, event): - mimedata = event.mimeData() - logger.debug(f'Handling file drop: {mimedata.formats()}') - pos = QtCore.QPoint(round(event.position().x()), - round(event.position().y())) - if mimedata.hasUrls(): - logger.debug(f'Found dropped urls: {mimedata.urls()}') - if not self.control_target.scene.items(): + def dropEvent(self, event): # 拖放事件槽函数,用于处理拖放事件 + mimedata = event.mimeData() # 获取拖放事件的MIME数据,用于处理拖放事件 + logger.debug(f'Handling file drop: {mimedata.formats()}') # 拖放事件槽函数,用于处理拖放事件,记录拖放事件的MIME数据格式 + pos = QtCore.QPoint(round(event.position().x()), # 记录拖放事件的位置,用于处理拖放事件 + round(event.position().y())) # 记录拖放事件的位置,用于处理拖放事件 + if mimedata.hasUrls(): # 如果拖放事件的MIME数据包含URL + logger.debug(f'Found dropped urls: {mimedata.urls()}') # 拖放事件槽函数,用于处理拖放事件,记录拖放事件的URL + if not self.control_target.scene.items(): # 如果场景中没有项目(即没有插入的图片项) # Check if we have a bee file we can open directly - path = mimedata.urls()[0] - if (path.isLocalFile() - and fileio.is_bee_file(path.toLocalFile())): - self.control_target.open_from_file(path.toLocalFile()) - return - self.control_target.do_insert_images(mimedata.urls(), pos) - elif mimedata.hasImage(): - img = QtGui.QImage(mimedata.imageData()) - item = BeePixmapItem(img) - pos = self.control_target.mapToScene(pos) + path = mimedata.urls()[0] # 获取拖放事件的URL,用于处理拖放事件 + if (path.isLocalFile() # 如果拖放事件的URL是本地文件 + and fileio.is_bee_file(path.toLocalFile())): # 如果拖放事件的URL是本地文件,且是BeeRef文件 + self.control_target.open_from_file(path.toLocalFile()) # 打开拖放事件的URL,用于处理拖放事件 + return # 如果拖放事件的URL是本地文件,且是BeeRef文件,直接打开文件,不进行插入图片项操作 + self.control_target.do_insert_images(mimedata.urls(), pos) # 插入图片项槽函数,用于处理拖放事件,插入拖放事件的URL对应的图片项 + elif mimedata.hasImage(): # 如果拖放事件的MIME数据包含图像 + img = QtGui.QImage(mimedata.imageData()) # 获取拖放事件的图像数据,用于处理拖放事件 + item = BeePixmapItem(img) # 创建图片项,用于处理拖放事件 + pos = self.control_target.mapToScene(pos) # 记录拖放事件的位置,用于处理拖放事件 self.control_target.undo_stack.push( - commands.InsertItems(self.control_target.scene, [item], pos)) + commands.InsertItems(self.control_target.scene, [item], pos)) # 插入图片项槽函数,用于处理拖放事件,插入拖放事件的URL对应的图片项 else: - logger.info('Drop not an image') + logger.info('Drop not an image') # 拖放事件槽函数,用于处理拖放事件,记录拖放事件的MIME数据格式 - def mousePressEventMainControls(self, event): - if self.movewin_active: - self.exit_movewin_mode() - event.accept() - return True + def mousePressEventMainControls(self, event): # 鼠标按下事件槽函数,用于处理鼠标按下事件 + if self.movewin_active: # 如果移动窗口模式激活 + self.exit_movewin_mode() # 退出移动窗口模式槽函数,用于处理移动窗口模式的切换 + event.accept() # 接受鼠标按下事件的建议操作,用于处理鼠标按下事件 + return True # 如果移动窗口模式激活,直接返回True,不进行其他操作 action, inverted =\ - self.control_target.keyboard_settings.mouse_action_for_event(event) + self.control_target.keyboard_settings.mouse_action_for_event(event) # 获取鼠标按下事件对应的操作,用于处理鼠标按下事件 if action == 'movewindow': - self.enter_movewin_mode() - event.accept() - return True + self.enter_movewin_mode() # 进入移动窗口模式槽函数,用于处理移动窗口模式的切换 + event.accept() # 接受鼠标按下事件的建议操作,用于处理鼠标按下事件 + return True # 如果移动窗口模式激活,直接返回True,不进行其他操作 - def mouseMoveEventMainControls(self, event): - if self.movewin_active: - pos = self.mapToGlobal(event.position()) - delta = pos - self.event_start - self.event_start = pos - self.main_window.move(self.main_window.x() + int(delta.x()), - self.main_window.y() + int(delta.y())) - event.accept() - return True + def mouseMoveEventMainControls(self, event): # 鼠标移动事件槽函数,用于处理鼠标移动事件 + if self.movewin_active: # 如果移动窗口模式激活 + pos = self.mapToGlobal(event.position()) # 获取鼠标移动事件的全局位置,用于处理鼠标移动事件 + delta = pos - self.event_start # 计算鼠标移动事件的 delta 向量,用于处理鼠标移动事件 + self.event_start = pos # 更新鼠标移动事件的起始位置,用于处理鼠标移动事件 + self.main_window.move(self.main_window.x() + int(delta.x()), # 移动主窗口槽函数,用于处理移动窗口模式的切换 + self.main_window.y() + int(delta.y())) # 移动主窗口槽函数,用于处理移动窗口模式的切换 + event.accept() # 接受鼠标移动事件的建议操作,用于处理鼠标移动事件 + return True # 如果移动窗口模式激活,直接返回True,不进行其他操作 - def mouseReleaseEventMainControls(self, event): - if self.movewin_active: - self.exit_movewin_mode() - event.accept() - return True + def mouseReleaseEventMainControls(self, event): # 鼠标释放事件槽函数,用于处理鼠标释放事件 + if self.movewin_active: # 如果移动窗口模式激活 + self.exit_movewin_mode() # 退出移动窗口模式槽函数,用于处理移动窗口模式的切换 + event.accept() # 接受鼠标释放事件的建议操作,用于处理鼠标释放事件 + return True # 如果移动窗口模式激活,直接返回True,不进行其他操作 - def keyPressEventMainControls(self, event): - if self.movewin_active: - self.exit_movewin_mode() - event.accept() - return True + def keyPressEventMainControls(self, event): # 键盘按下事件槽函数,用于处理键盘按下事件 + if self.movewin_active: # 如果移动窗口模式激活 + self.exit_movewin_mode() # 退出移动窗口模式槽函数,用于处理移动窗口模式的切换 + event.accept() # 接受键盘按下事件的建议操作,用于处理键盘按下事件 + return True # 如果移动窗口模式激活,直接返回True,不进行其他操作 diff --git a/beeref/view.py b/beeref/view.py index d2d819e..a83be7c 100644 --- a/beeref/view.py +++ b/beeref/view.py @@ -714,7 +714,7 @@ class BeeGraphicsView(MainControlsMixin, item = BeePixmapItem(img) # 创建图片项对象,参数为图像数据 self.undo_stack.push(commands.InsertItems(self.scene, [item], pos)) # 插入图片项到场景中,参数为场景对象、图片项列表、插入位置 if len(self.scene.items()) == 1: # 如果场景中只有一个项目(即插入的图片项) - if len(self.scene.items()) == 1: # + if len(self.scene.items()) == 1: # # This is the first image in the scene self.on_action_fit_scene() # 缩放场景槽函数,将场景缩放以适应所有项目 return @@ -734,54 +734,54 @@ class BeeGraphicsView(MainControlsMixin, QtGui.QDesktopServices.openUrl( QtCore.QUrl.fromLocalFile(dirname)) # 打开目录路径对应的本地文件资源 - def on_selection_changed(self): + def on_selection_changed(self): # 选择项改变槽函数,用于处理场景中选中项的变化 logger.debug('Currently selected items: %s', - len(self.scene.selectedItems(user_only=True))) - self.actiongroup_set_enabled('active_when_selection', - self.scene.has_selection()) - self.actiongroup_set_enabled('active_when_single_image', - self.scene.has_single_image_selection()) + len(self.scene.selectedItems(user_only=True))) # 调试日志,提示当前选中的项目数量 + self.actiongroup_set_enabled('active_when_selection', # 启用/禁用操作组槽函数,根据场景是否有选中项来启用/禁用操作组 + self.scene.has_selection()) # 启用/禁用操作组槽函数,根据场景是否有选中项来启用/禁用操作组 + self.actiongroup_set_enabled('active_when_single_image', # 启用/禁用操作组槽函数,根据场景是否有单个图片选中项来启用/禁用操作组 + self.scene.has_single_image_selection()) # 启用/禁用操作组槽函数,根据场景是否有单个图片选中项来启用/禁用操作组 - if self.scene.has_selection(): - item = self.scene.selectedItems(user_only=True)[0] - grayscale = getattr(item, 'grayscale', False) - actions.actions['grayscale'].qaction.setChecked(grayscale) - self.viewport().repaint() + if self.scene.has_selection(): # 如果场景有选中项 + item = self.scene.selectedItems(user_only=True)[0] # 获取场景中第一个选中项,参数为用户仅选择项 + grayscale = getattr(item, 'grayscale', False) # 获取选中项的灰度属性,默认值为False + actions.actions['grayscale'].qaction.setChecked(grayscale) # 设置灰度操作项的复选框状态,根据选中项的灰度属性来设置 + self.viewport().repaint() # 重绘视口槽函数,用于刷新视口显示,确保选中项的灰度属性复选框状态正确显示 - def on_cursor_changed(self, cursor): - if self.active_mode is None: - self.viewport().setCursor(cursor) + def on_cursor_changed(self, cursor): # 光标改变槽函数,用于处理场景中光标位置的变化 + if self.active_mode is None: # 如果当前激活模式为None + self.viewport().setCursor(cursor) # 设置视口光标为参数cursor - def on_cursor_cleared(self): - if self.active_mode is None: - self.viewport().unsetCursor() + def on_cursor_cleared(self): # 光标清除槽函数,用于处理场景中光标位置的清除 + if self.active_mode is None: # 如果当前激活模式为None + self.viewport().unsetCursor() # 清除视口光标 - def recalc_scene_rect(self): + def recalc_scene_rect(self): # 重新计算场景矩形槽函数,用于调整场景矩形以适应所有项目 """Resize the scene rectangle so that it is always one view width wider than all items' bounding box at each side and one view width higher on top and bottom. This gives the impression of an infinite canvas.""" - if self.previous_transform: + if self.previous_transform: # 如果之前有变换矩阵 return - logger.trace('Recalculating scene rectangle...') + logger.trace('Recalculating scene rectangle...') # 跟踪日志,提示重新计算场景矩形 try: - topleft = self.mapFromScene( - self.scene.itemsBoundingRect().topLeft()) - topleft = self.mapToScene(QtCore.QPoint( - topleft.x() - self.size().width(), - topleft.y() - self.size().height())) - bottomright = self.mapFromScene( - self.scene.itemsBoundingRect().bottomRight()) - bottomright = self.mapToScene(QtCore.QPoint( - bottomright.x() + self.size().width(), - bottomright.y() + self.size().height())) - self.setSceneRect(QtCore.QRectF(topleft, bottomright)) - except OverflowError: - logger.info('Maximum scene size reached') - logger.trace('Done recalculating scene rectangle') + topleft = self.mapFromScene( # 映射场景矩形的左上角到视口坐标 + self.scene.itemsBoundingRect().topLeft()) # 映射场景矩形的左上角到视口坐标 + topleft = self.mapToScene(QtCore.QPoint( # 映射视口坐标的左上角到场景坐标 + topleft.x() - self.size().width() / 2, # 减去视口宽度的一半,确保场景矩形在视口宽度的一半宽度内 + topleft.y() - self.size().height() / 2)) # 减去视口高度的一半,确保场景矩形在视口高度的一半高度内 + bottomright = self.mapFromScene( # 映射场景矩形的右下角到视口坐标 + self.scene.itemsBoundingRect().bottomRight()) # 映射场景矩形的右下角到视口坐标 + bottomright = self.mapToScene(QtCore.QPoint( # 映射视口坐标的右下角到场景坐标 + bottomright.x() + self.size().width() / 2, # 加上视口宽度的一半,确保场景矩形在视口宽度的一半宽度内 + bottomright.y() + self.size().height() / 2)) # 加上视口高度的一半,确保场景矩形在视口高度的一半高度内 + self.setSceneRect(QtCore.QRectF(topleft, bottomright)) # 设置场景矩形为新计算的矩形 + except OverflowError: # 处理溢出错误,当场景矩形超出最大尺寸时触发 + logger.info('Maximum scene size reached') # 信息日志,提示最大场景尺寸已达到 + logger.trace('Done recalculating scene rectangle') # 跟踪日志,提示完成重新计算场景矩形 - def get_zoom_size(self, func): + def get_zoom_size(self, func): # 获取缩放尺寸槽函数,用于计算场景中所有项目的边界框尺寸 """Calculates the size of all items' bounding box in the view's coordinates. @@ -793,34 +793,34 @@ class BeeGraphicsView(MainControlsMixin, arguments and turns it into a number, for ex. ``min`` or ``max``. """ - topleft = self.mapFromScene( - self.scene.itemsBoundingRect().topLeft()) - bottomright = self.mapFromScene( - self.scene.itemsBoundingRect().bottomRight()) - return func(bottomright.x() - topleft.x(), - bottomright.y() - topleft.y()) + topleft = self.mapFromScene( # 映射场景矩形的左上角到视口坐标 + self.scene.itemsBoundingRect().topLeft()) # 映射场景矩形的左上角到视口坐标 + bottomright = self.mapFromScene( # 映射场景矩形的右下角到视口坐标 + self.scene.itemsBoundingRect().bottomRight()) # 映射场景矩形的右下角到视口坐标 + return func(bottomright.x() - topleft.x(), # 计算场景中所有项目的边界框宽度 + bottomright.y() - topleft.y()) # 计算场景中所有项目的边界框高度 - def scale(self, *args, **kwargs): - super().scale(*args, **kwargs) - self.scene.on_view_scale_change() - self.recalc_scene_rect() + def scale(self, *args, **kwargs): # 缩放槽函数,用于处理场景中项目的缩放 + super().scale(*args, **kwargs) # 调用父类的缩放槽函数,用于处理场景中项目的缩放 + self.scene.on_view_scale_change() # 调用场景中的视图缩放变化槽函数,用于处理场景中项目的缩放变化 + self.recalc_scene_rect() # 重新计算场景矩形槽函数,用于调整场景矩形以适应所有项目 def get_scale(self): - return self.transform().m11() + return self.transform().m11() # 返回当前变换矩阵的缩放比例因子 - def pan(self, delta): - if not self.scene.items(): - logger.debug('No items in scene; ignore pan') + def pan(self, delta): # 平移槽函数,用于处理场景中项目的平移 + if not self.scene.items(): # 如果场景中没有项目 + logger.debug('No items in scene; ignore pan') # 调试日志,提示场景中没有项目,忽略平移 return - hscroll = self.horizontalScrollBar() - hscroll.setValue(int(hscroll.value() + delta.x())) - vscroll = self.verticalScrollBar() - vscroll.setValue(int(vscroll.value() + delta.y())) + hscroll = self.horizontalScrollBar() # 获取水平滚动条 + hscroll.setValue(int(hscroll.value() + delta.x())) # 设置水平滚动条的值为当前值加上平移量的整数部分 + vscroll = self.verticalScrollBar() # 获取垂直滚动条 + vscroll.setValue(int(vscroll.value() + delta.y())) # 设置垂直滚动条的值为当前值加上平移量的整数部分 - def zoom(self, delta, anchor): - if not self.scene.items(): - logger.debug('No items in scene; ignore zoom') + def zoom(self, delta, anchor): # 缩放槽函数,用于处理场景中项目的缩放 + if not self.scene.items(): # 如果场景中没有项目 + logger.debug('No items in scene; ignore zoom') # 调试日志,提示场景中没有项目,忽略缩放 return # We calculate where the anchor is before and after the zoom @@ -828,150 +828,150 @@ class BeeGraphicsView(MainControlsMixin, # We can't use QGraphicsView's AnchorUnderMouse since it # uses the current cursor position while we need the initial mouse # press position for zooming with Ctrl + Middle Drag - anchor = QtCore.QPoint(round(anchor.x()), - round(anchor.y())) - ref_point = self.mapToScene(anchor) - if delta == 0: - return - factor = 1 + abs(delta / 1000) - if delta > 0: - if self.get_zoom_size(max) < 10000000: - self.scale(factor, factor) - else: - logger.debug('Maximum zoom size reached') - return + anchor = QtCore.QPoint(round(anchor.x()), # 四舍五入 anchor 的 x 坐标 + round(anchor.y())) # 四舍五入 anchor 的 y 坐标 + ref_point = self.mapToScene(anchor) # 映射 anchor 到场景坐标 + if delta == 0: # 如果缩放量为 0 + return # 如果缩放量为 0,忽略缩放 + factor = 1 + abs(delta / 1000) # 计算缩放因子 + if delta > 0: # 如果缩放量大于 0 + if self.get_zoom_size(max) < 10000000: # 如果缩放后场景中所有项目的边界框宽度和高度都小于 10000000 + self.scale(factor, factor) # 缩放场景中所有项目的边界框宽度和高度 + else: # 如果缩放后场景中所有项目的边界框宽度和高度都大于等于 10000000 + logger.debug('Maximum zoom size reached') # 调试日志,提示最大缩放尺寸已达到 + return # 如果缩放后场景中所有项目的边界框宽度和高度都大于等于 10000000,忽略缩放 else: - if self.get_zoom_size(min) > 50: - self.scale(1/factor, 1/factor) + if self.get_zoom_size(min) > 50: # 如果缩放后场景中所有项目的边界框宽度和高度都大于 50 + self.scale(1/factor, 1/factor) # 缩放场景中所有项目的边界框宽度和高度 else: - logger.debug('Minimum zoom size reached') - return + logger.debug('Minimum zoom size reached') # 调试日志,提示最小缩放尺寸已达到 + return # 如果缩放后场景中所有项目的边界框宽度和高度都大于 50,忽略缩放 - self.pan(self.mapFromScene(ref_point) - anchor) - self.reset_previous_transform() + self.pan(self.mapFromScene(ref_point) - anchor) # 平移场景中所有项目的边界框宽度和高度 + self.reset_previous_transform() # 重置前一个变换矩阵,用于处理场景中项目的缩放变化 - def wheelEvent(self, event): + def wheelEvent(self, event): # 滚轮事件槽函数,用于处理场景中项目的缩放和平移 action, inverted\ - = self.keyboard_settings.mousewheel_action_for_event(event) + = self.keyboard_settings.mousewheel_action_for_event(event) # 获取滚轮事件的缩放和平移操作 - delta = event.angleDelta().y() - if inverted: - delta = delta * -1 + delta = event.angleDelta().y() # 获取滚轮事件的缩放增量 + if inverted: # 如果滚轮事件的缩放增量为负数 + delta = delta * -1 # 取反缩放增量 - if action == 'zoom': - self.zoom(delta, event.position()) - event.accept() + if action == 'zoom': # 如果滚轮事件的缩放操作 + self.zoom(delta, event.position()) # 调用缩放槽函数,用于处理场景中项目的缩放 + event.accept() # 接受滚轮事件,防止事件冒泡到父类 + return + if action == 'pan_horizontal': # 如果滚轮事件的水平平移操作 + self.pan(QtCore.QPointF(0, 0.5 * delta)) # 调用平移槽函数,用于处理场景中项目的水平平移 + event.accept() # 接受滚轮事件,防止事件冒泡到父类 return - if action == 'pan_horizontal': - self.pan(QtCore.QPointF(0, 0.5 * delta)) - event.accept() - return - if action == 'pan_vertical': - self.pan(QtCore.QPointF(0.5 * delta, 0)) - event.accept() + if action == 'pan_vertical': # 如果滚轮事件的垂直平移操作 + self.pan(QtCore.QPointF(0.5 * delta, 0)) # 调用平移槽函数,用于处理场景中项目的垂直平移 + event.accept() # 接受滚轮事件,防止事件冒泡到父类 return - def mousePressEvent(self, event): - if self.mousePressEventMainControls(event): + def mousePressEvent(self, event): # 鼠标按下事件槽函数,用于处理场景中项目的缩放和平移 + if self.mousePressEventMainControls(event): # 如果鼠标按下事件是主控件的事件 return - if self.active_mode == self.SAMPLE_COLOR_MODE: - if (event.button() == Qt.MouseButton.LeftButton): - color = self.scene.sample_color_at( - self.mapToScene(event.pos())) - if color: - name = qcolor_to_hex(color) - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(name) - self.scene.internal_clipboard = [] - msg = f'Copied color to clipboard: {name}' - logger.debug(msg) - widgets.BeeNotification(self, msg) + if self.active_mode == self.SAMPLE_COLOR_MODE: # 如果当前模式是采样颜色模式 + if (event.button() == Qt.MouseButton.LeftButton): # 如果鼠标按下事件是左键 + color = self.scene.sample_color_at( # 调用场景中的采样颜色函数,用于获取场景中指定位置的颜色 + self.mapToScene(event.pos())) # 映射鼠标按下事件的位置到场景坐标 + if color: # 如果获取到颜色 + name = qcolor_to_hex(color) # 将颜色转换为十六进制字符串 + clipboard = QtWidgets.QApplication.clipboard() # 获取应用程序的剪贴板对象 + clipboard.setText(name) # 将颜色的十六进制字符串复制到剪贴板 + self.scene.internal_clipboard = [] # 清空场景中的内部剪贴板 + msg = f'Copied color to clipboard: {name}' + logger.debug(msg) # 调试日志,提示复制颜色到剪贴板 + widgets.BeeNotification(self, msg) # 显示通知消息,提示复制颜色到剪贴板 else: - logger.debug('No color found') - self.cancel_sample_color_mode() - event.accept() + logger.debug('No color found at %s', event.pos()) # 调试日志,提示未找到颜色 + self.cancel_sample_color_mode() # 取消采样颜色模式 + event.accept() # 接受鼠标按下事件,防止事件冒泡到父类 return - action, inverted = self.keyboard_settings.mouse_action_for_event(event) + action, inverted = self.keyboard_settings.mouse_action_for_event(event) # 获取鼠标按下事件的缩放和平移操作 - if action == 'zoom': - self.active_mode = self.ZOOM_MODE - self.event_start = event.position() - self.event_anchor = event.position() - self.event_inverted = inverted - event.accept() + if action == 'zoom': # 如果鼠标按下事件的缩放操作 + self.active_mode = self.ZOOM_MODE # 设置当前模式为缩放模式 + self.event_start = event.position() # 记录鼠标按下事件的位置 + self.event_anchor = event.position() # 记录鼠标按下事件的位置 + self.event_inverted = inverted # 记录鼠标按下事件的缩放增量是否为负数 + event.accept() # 接受鼠标按下事件,防止事件冒泡到父类 return - if action == 'pan': + if action == 'pan': # 如果鼠标按下事件的平移操作 logger.trace('Begin pan') - self.active_mode = self.PAN_MODE - self.event_start = event.position() - self.viewport().setCursor(Qt.CursorShape.ClosedHandCursor) + self.active_mode = self.PAN_MODE # 设置当前模式为平移模式 + self.event_start = event.position() # 记录鼠标按下事件的位置 + self.viewport().setCursor(Qt.CursorShape.ClosedHandCursor) # 设置视口的光标为关闭手型光标 # ClosedHandCursor and OpenHandCursor don't work, but I # don't know if that's only on my system or a general # problem. It works with other cursors. - event.accept() + event.accept() # 接受鼠标按下事件,防止事件冒泡到父类 return - super().mousePressEvent(event) + super().mousePressEvent(event) # 调用父类的鼠标按下事件槽函数,用于处理其他鼠标按下事件 - def mouseMoveEvent(self, event): - if self.active_mode == self.PAN_MODE: - self.reset_previous_transform() - pos = event.position() - self.pan(self.event_start - pos) - self.event_start = pos - event.accept() + def mouseMoveEvent(self, event): # 鼠标移动事件槽函数,用于处理场景中项目的缩放和平移 + if self.active_mode == self.PAN_MODE: # 如果当前模式是平移模式 + self.reset_previous_transform() # 重置前一次变换,用于处理场景中项目的缩放和平移 + pos = event.position() # 获取鼠标移动事件的位置 + self.pan(self.event_start - pos) # 调用平移槽函数,用于处理场景中项目的平移 + self.event_start = pos # 更新鼠标按下事件的位置 + event.accept() # 接受鼠标移动事件,防止事件冒泡到父类 return - if self.active_mode == self.ZOOM_MODE: - self.reset_previous_transform() - pos = event.position() - delta = (self.event_start - pos).y() - if self.event_inverted: - delta *= -1 - self.event_start = pos - self.zoom(delta * 20, self.event_anchor) - event.accept() + if self.active_mode == self.ZOOM_MODE: # 如果当前模式是缩放模式 + self.reset_previous_transform() # 重置前一次变换,用于处理场景中项目的缩放和平移 + pos = event.position() # 获取鼠标移动事件的位置 + delta = (self.event_start - pos).y() # 计算鼠标移动事件的垂直增量 + if self.event_inverted: # 如果鼠标移动事件的缩放增量为负数 + delta *= -1 # 取反,用于处理场景中项目的缩放 + self.event_start = pos # 更新鼠标按下事件的位置 + self.zoom(delta * 20, self.event_anchor) # 调用缩放槽函数,用于处理场景中项目的缩放 + event.accept() # 接受鼠标移动事件,防止事件冒泡到父类 return - if self.active_mode == self.SAMPLE_COLOR_MODE: - self.sample_color_widget.update( - event.position(), - self.scene.sample_color_at(self.mapToScene(event.pos()))) - event.accept() + if self.active_mode == self.SAMPLE_COLOR_MODE: # 如果当前模式是采样颜色模式 + self.sample_color_widget.update( # 更新采样颜色小部件的显示 + event.position(), # 更新采样颜色小部件的显示位置 + self.scene.sample_color_at(self.mapToScene(event.pos()))) # 更新采样颜色小部件的显示颜色 + event.accept() # 接受鼠标移动事件,防止事件冒泡到父类 return - if self.mouseMoveEventMainControls(event): + if self.mouseMoveEventMainControls(event): # 如果主控件处理了鼠标移动事件 return - super().mouseMoveEvent(event) + super().mouseMoveEvent(event) # 调用父类的鼠标移动事件槽函数,用于处理其他鼠标移动事件 - def mouseReleaseEvent(self, event): - if self.active_mode == self.PAN_MODE: - logger.trace('End pan') - self.viewport().unsetCursor() - self.active_mode = None - event.accept() + def mouseReleaseEvent(self, event): # 鼠标释放事件槽函数,用于处理场景中项目的缩放和平移 + if self.active_mode == self.PAN_MODE: # 如果当前模式是平移模式 + logger.trace('End pan') # 调试日志,提示结束平移 + self.viewport().unsetCursor() # 重置视口的光标为默认光标 + self.active_mode = None # 重置当前模式为 None + event.accept() # 接受鼠标释放事件,防止事件冒泡到父类 return - if self.active_mode == self.ZOOM_MODE: - self.active_mode = None - event.accept() + if self.active_mode == self.ZOOM_MODE: # 如果当前模式是缩放模式 + self.active_mode = None # 重置当前模式为 None + event.accept() # 接受鼠标释放事件,防止事件冒泡到父类 return - if self.mouseReleaseEventMainControls(event): + if self.mouseReleaseEventMainControls(event): # 如果主控件处理了鼠标释放事件 return - super().mouseReleaseEvent(event) + super().mouseReleaseEvent(event) # 调用父类的鼠标释放事件槽函数,用于处理其他鼠标释放事件 - def resizeEvent(self, event): - super().resizeEvent(event) - self.recalc_scene_rect() - self.welcome_overlay.resize(self.size()) + def resizeEvent(self, event): # 场景视图的大小调整事件槽函数,用于处理场景视图的大小调整 + super().resizeEvent(event) # 调用父类的大小调整事件槽函数,用于处理其他大小调整事件 + self.recalc_scene_rect() # 重新计算场景矩形,用于处理场景中项目的缩放和平移 + self.welcome_overlay.resize(self.size()) # 调整欢迎叠加层的大小,用于处理场景视图的大小调整 - def keyPressEvent(self, event): - if self.keyPressEventMainControls(event): + def keyPressEvent(self, event): # 键盘按下事件槽函数,用于处理场景中项目的缩放和平移 + if self.keyPressEventMainControls(event): # 如果主控件处理了键盘按下事件 return - if self.active_mode == self.SAMPLE_COLOR_MODE: - self.cancel_sample_color_mode() - event.accept() + if self.active_mode == self.SAMPLE_COLOR_MODE: # 如果当前模式是采样颜色模式 + self.cancel_sample_color_mode() # 取消采样颜色模式 + event.accept() # 接受键盘按下事件,防止事件冒泡到父类 return - super().keyPressEvent(event) + super().keyPressEvent(event) # 调用父类的键盘按下事件槽函数,用于处理其他键盘按下事件