你知道吗,-Long.MIN_VALUE == Long.MIN_VALUE

相信吗,-Long.MIN_VALUE == Long.MIN_VALUE
不信的话,可以去Eclipse中自己敲一下。为什么呢?java中有符号整数是补码表示,最高位表示符号位,1表示负数,0表示正数。所以64位的有符号整数long所能表示的最小值(Long.MIN_VALUE)的十六进制表示为0x8000000000000000(最高位是1其余位是0)就是负的2的63次幂。最大的64位有符号整数是0x7fffffffffffffff(最高位是0其余位是1).

所以,-Long.MIN_VALUE应该是0x08000000000000000但是这已经是65位的,表示为64位时最高位的0会被丢弃。所以,还是0x8000000000000000.所以,就成了-Long.MIN_VALUE == Long.MIN_VALUE。实际上是上溢出了,超出了所能表示的最大值。

如果,看编译出来的字节码,甚至能看到编译器直接就把-Long.MIN_VALUE编译成常量-9223372036854775808L,根本没有运算的字节码产生。

最后,还有一点Math.abs(Long.MIN_VALUE)==Long.MIN_VALUE。这个是需要注意的。一个负数的绝对值不一定是正数,要考虑此时的特殊情形。
import os import sys import numpy as np from PyQt5.QtWidgets import (QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QRubberBand, QApplication, QGraphicsPathItem, QMenu) from PyQt5.QtGui import (QPixmap, QImage, QPainter, QPen, QColor, QBrush, QKeyEvent, QTransform, QPainterPath, QCursor, QPolygonF) from PyQt5.QtCore import (Qt, QPoint, QRect, QSize, QEvent, QPointF, QRectF, pyqtSignal, QTimer, QLineF, QVariantAnimation) class ImageCanvas(QGraphicsView): # 信号定义 zoom_changed = pyqtSignal(float) image_changed = pyqtSignal(QImage) mouse_position_changed = pyqtSignal(QPointF, str) selection_changed = pyqtSignal(QRectF) tool_changed = pyqtSignal(int) # 工具枚举 TOOL_SELECT = 0 TOOL_CROP = 1 TOOL_BRUSH = 2 TOOL_SHAPE = 3 TOOL_TEXT = 4 TOOL_ERASER = 5 def __init__(self, parent=None): super().__init__(parent) self.parent_window = parent self.setRenderHint(QPainter.Antialiasing) self.setRenderHint(QPainter.SmoothPixmapTransform) self.setDragMode(QGraphicsView.ScrollHandDrag) self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setInteractive(True) self.viewport().setCursor(Qt.CrossCursor) # 创建场景 self.scene = QGraphicsScene(self) self.setScene(self.scene) self.scene.setBackgroundBrush(QBrush(QColor(50, 50, 50))) # 初始化变量 self.zoom_factor = 1.0 self.min_zoom = 0.05 self.max_zoom = 50.0 self.original_image = QImage() self.displayed_image = QImage() self.current_image_item = None self.saved_transforms = None # 工具相关状态 self.rubber_band = QRubberBand(QRubberBand.Rectangle, self.viewport()) self.rubber_band.hide() self.selection_rect = QRectF() self.selection_active = False self.selection_start = QPointF() # 当前工具状态 self.current_tool = self.TOOL_SELECT self.tool_settings = { 'brush_size': 5, 'brush_color': QColor(255, 0, 0, 255), 'eraser_size': 20, 'shape_type': 0, # 0:矩形, 1:圆形, 2:直线 'antialias': True, 'opacity': 1.0 } # 绘制相关状态 self.drawing_active = False self.drawing_path = None self.drawing_item = None self.drawing_start = QPointF() self.current_layer = 0 self.layers = [] # 初始化默认图层 self.add_new_layer("Background", QColor(255, 255, 255)) # 历史记录快照 self.snapshots = [] self.current_snapshot = -1 # 安装事件过滤器 self.viewport().installEventFilter(self) # 状态提示定时器 self.status_timer = QTimer(self) self.status_timer.setSingleShot(True) self.status_timer.timeout.connect(self._clear_status) # ==================== 图层管理功能 ==================== def add_new_layer(self, name, fill_color=QColor(0, 0, 0, 0)): """添加新图层""" layer = { 'name': name, 'visible': True, 'locked': False, 'opacity': 1.0, 'image': QImage(self.displayed_image.size(), QImage.Format_ARGB32), 'item': None } layer['image'].fill(fill_color) self.layers.append(layer) self.current_layer = len(self.layers) - 1 self._render_layers() return self.current_layer def remove_layer(self, index): """移除指定图层""" if 0 <= index < len(self.layers): if self.layers[index]['item']: self.scene.removeItem(self.layers[index]['item']) del self.layers[index] if index <= self.current_layer: self.current_layer = max(0, self.current_layer - 1) self._render_layers() def set_current_layer(self, index): """设置当前活动图层""" if 0 <= index < len(self.layers): self.current_layer = index return True return False def toggle_layer_visibility(self, index): """切换图层可见性""" if 0 <= index < len(self.layers): self.layers[index]['visible'] = not self.layers[index]['visible'] self._render_layers() def _render_layers(self): """渲染所有可见图层到显示图像""" if not self.layers: return # 创建一个临时图像来合成所有图层 size = self.displayed_image.size() composite = QImage(size, QImage.Format_ARGB32) composite.fill(Qt.transparent) painter = QPainter(composite) for layer in self.layers: if not layer['visible']: continue painter.setOpacity(layer['opacity']) painter.drawImage(0, 0, layer['image']) painter.end() # 更新显示 self.displayed_image = composite self._show_image(composite) # 更新UI self.image_changed.emit(self.displayed_image) # ==================== 图像管理功能 ==================== def set_image(self, image): """设置要显示的图像""" if image.isNull(): self.clear() return self.original_image = image.copy() self.displayed_image = image.copy() self._clear_layers() self.add_new_layer("Background", Qt.white) self.layers[0]['image'] = image.copy() self.reset_view() self.image_changed.emit(self.displayed_image) self.take_snapshot() def update_image(self): """更新显示的图像""" if not self.displayed_image.isNull(): self._show_image(self.displayed_image) self.image_changed.emit(self.displayed_image) def clear(self): """清除画布""" self.scene.clear() self.current_image_item = None self.displayed_image = QImage() self.original_image = QImage() self.zoom_factor = 1.0 self.zoom_changed.emit(self.zoom_factor) self.resetTransform() self._clear_layers() def reset_view(self): """重置视图到初始状态""" if not self.displayed_image.isNull(): self._show_image(self.displayed_image) self.fit_to_view() def get_current_image(self): """获取当前显示的图像""" return self.displayed_image def get_original_image(self): """获取原始未编辑图像""" return self.original_image def get_pixel_at(self, pos): """获取指定置的像素颜色""" if self.displayed_image.isNull(): return None if not self.current_image_item: return None # 转换到图像坐标 img_pos = self.map_to_image(pos) x = int(img_pos.x()) y = int(img_pos.y()) # 检查边界 if 0 <= x < self.displayed_image.width() and 0 <= y < self.displayed_image.height(): color = QColor(self.displayed_image.pixel(x, y)) return color return None def map_to_image(self, scene_pos): """将场景坐标映射到图像坐标""" if not self.current_image_item: return QPointF() return self.current_image_item.mapFromScene(scene_pos) # ==================== 缩放功能 ==================== def zoom_in(self): """放大图像""" self.set_zoom(self.zoom_factor * 1.2) def zoom_out(self): """缩小图像""" self.set_zoom(self.zoom_factor * 0.8) def fit_to_view(self): """适应窗口大小""" if self.current_image_item: self.fitInView(self.scene.itemsBoundingRect(), Qt.KeepAspectRatio) self._update_zoom_factor() def set_zoom(self, factor): """设置特定缩放级别""" factor = max(self.min_zoom, min(factor, self.max_zoom)) if abs(factor - self.zoom_factor) > 0.01: self.zoom_factor = factor self.resetTransform() self.scale(factor, factor) self.zoom_changed.emit(self.zoom_factor * 100) def _update_zoom_factor(self): """更新缩放因子""" view_rect = self.transform().mapRect(QRectF(0, 0, 1, 1)) self.zoom_factor = 1.0 / view_rect.width() self.zoom_changed.emit(self.zoom_factor * 100) # ==================== 工具控制功能 ==================== def set_tool(self, tool_id): """设置当前工具""" self.current_tool = tool_id self.tool_changed.emit(tool_id) self._update_cursor() def set_tool_setting(self, key, value): """设置工具参数""" if key in self.tool_settings: self.tool_settings[key] = value self._update_cursor() def _update_cursor(self): """根据当前工具更新光标""" if self.current_tool == self.TOOL_SELECT: self.viewport().setCursor(Qt.CrossCursor) elif self.current_tool == self.TOOL_CROP: self.viewport().setCursor(Qt.CrossCursor) elif self.current_tool == self.TOOL_BRUSH: # 创建画笔形状的光标 size = self.tool_settings['brush_size'] * self.zoom_factor size = max(6, min(int(size), 100)) pixmap = QPixmap(size, size) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) painter.setPen(QPen(Qt.black, 1)) painter.setBrush(QBrush(self.tool_settings['brush_color'])) painter.drawEllipse(1, 1, size - 2, size - 2) painter.end() self.viewport().setCursor(QCursor(pixmap, size / 2, size / 2)) elif self.current_tool == self.TOOL_ERASER: # 创建橡皮擦形状的光标 size = self.tool_settings['eraser_size'] * self.zoom_factor size = max(6, min(int(size), 100)) pixmap = QPixmap(size, size) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) painter.setPen(QPen(Qt.black, 1)) painter.setBrush(QBrush(Qt.white)) painter.drawRect(1, 1, size - 2, size - 2) painter.end() self.viewport().setCursor(QCursor(pixmap, size / 2, size / 2)) elif self.current_tool in [self.TOOL_SHAPE, self.TOOL_TEXT]: self.viewport().setCursor(Qt.CrossCursor) # ==================== 历史记录功能 ==================== def take_snapshot(self): """创建当前状态的快照""" # 只保留最近的20个快照 if self.current_snapshot < len(self.snapshots) - 1: self.snapshots = self.snapshots[:self.current_snapshot + 1] # 保存图层状态 snapshot = { 'layers': [], 'current_layer': self.current_layer } for layer in self.layers: snapshot['layers'].append({ 'image': layer['image'].copy(), 'visible': layer['visible'], 'opacity': layer['opacity'] }) self.snapshots.append(snapshot) self.current_snapshot = len(self.snapshots) - 1 return True def restore_snapshot(self, index=None): """恢复指定快照""" if index is None: index = self.current_snapshot if 0 <= index < len(self.snapshots): snapshot = self.snapshots[index] # 恢复图层状态 self.layers = [] for layer_data in snapshot['layers']: self.layers.append({ 'image': layer_data['image'].copy(), 'visible': layer_data['visible'], 'opacity': layer_data['opacity'], 'locked': False, 'name': f"Layer {len(self.layers)}", 'item': None }) self.current_layer = snapshot['current_layer'] self._render_layers() self.current_snapshot = index return True return False def undo(self): """撤销上一步操作""" if self.current_snapshot > 0: self.restore_snapshot(self.current_snapshot - 1) return True return False def redo(self): """重做上一步操作""" if self.current_snapshot < len(self.snapshots) - 1: self.restore_snapshot(self.current_snapshot + 1) return True return False # ==================== 绘图功能 ==================== def start_drawing(self, scene_pos): """开始绘图操作""" if self.current_tool in [self.TOOL_BRUSH, self.TOOL_ERASER]: self.drawing_active = True self.drawing_start = scene_pos self.drawing_path = QPainterPath() self.drawing_path.moveTo(self.map_to_image(scene_pos)) if self.current_tool == self.TOOL_BRUSH: pen = QPen(self.tool_settings['brush_color'], self.tool_settings['brush_size']) pen.setCapStyle(Qt.RoundCap) pen.setJoinStyle(Qt.RoundJoin) else: # 橡皮擦 pen = QPen(QColor(255, 255, 255, 255), self.tool_settings['eraser_size']) pen.setCapStyle(Qt.RoundCap) if self.tool_settings['antialias']: pen.setCosmetic(True) self.drawing_item = QGraphicsPathItem() self.drawing_item.setPen(pen) self.drawing_item.setPath(self.drawing_path) self.drawing_item.setOpacity(self.tool_settings['opacity']) self.scene.addItem(self.drawing_item) elif self.current_tool == self.TOOL_SHAPE: self.drawing_active = True self.drawing_start = scene_pos self.drawing_item = self._create_shape(scene_pos, scene_pos) if self.drawing_item: self.scene.addItem(self.drawing_item) elif self.current_tool == self.TOOL_TEXT: # 文本输入通过专用方法处理 pass def continue_drawing(self, scene_pos): """继续绘图操作""" if not self.drawing_active: return if self.current_tool in [self.TOOL_BRUSH, self.TOOL_ERASER]: # 添加路径点 new_point = self.map_to_image(scene_pos) self.drawing_path.lineTo(new_point) self.drawing_item.setPath(self.drawing_path) elif self.current_tool == self.TOOL_SHAPE: # 更新形状 if self.drawing_item: self.scene.removeItem(self.drawing_item) self.drawing_item = self._create_shape(self.drawing_start, scene_pos) if self.drawing_item: self.scene.addItem(self.drawing_item) def stop_drawing(self): """完成绘图操作并应用到图层""" if not self.drawing_active: return if self.drawing_item: # 创建绘制到图层的画家 painter = QPainter(self.layers[self.current_layer]['image']) painter.setRenderHint(QPainter.Antialiasing, self.tool_settings['antialias']) painter.setCompositionMode(QPainter.CompositionMode_SourceOver) painter.setOpacity(self.tool_settings['opacity']) # 从绘图项获取路径 path = self.drawing_item.path() # 根据工具应用不同绘制 if self.current_tool == self.TOOL_BRUSH: painter.setPen(QPen( self.tool_settings['brush_color'], self.tool_settings['brush_size'], Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin )) painter.setBrush(Qt.NoBrush) painter.drawPath(path) elif self.current_tool == self.TOOL_ERASER: # 使用橡皮擦 painter.setCompositionMode(QPainter.CompositionMode_Clear) painter.setPen(QPen( Qt.transparent, self.tool_settings['eraser_size'], Qt.SolidLine, Qt.RoundCap )) painter.drawPath(path) 这个代码吗给全
最新发布
06-12
<think>我们正在实现一个图像编辑器的画布组件(ImageCanvas),它继承自QGraphicsView。这个组件需要支持图像显示、缩放、平移、绘图、选择、裁剪等功能。由于代码很长,我将继续补充剩下的部分,包括绘制文本、选择操作、鼠标事件处理等。考虑到代码长度限制,我会尽量保持关键部分的完整性。下面是继续的代码:</think> 下面是完整的`ImageCanvas`实现,包含了之前未完成的绘图功能、工具操作以及完整的鼠标事件处理: ```python def stop_drawing(self): """完成绘图操作并应用到图层""" if not self.drawing_active: return if self.drawing_item: # 创建绘制到图层的画家 painter = QPainter(self.layers[self.current_layer]['image']) painter.setRenderHint(QPainter.Antialiasing, self.tool_settings['antialias']) painter.setCompositionMode(QPainter.CompositionMode_SourceOver) painter.setOpacity(self.tool_settings['opacity']) # 从绘图项获取路径 path = self.drawing_item.path() # 根据工具应用不同绘制 if self.current_tool == self.TOOL_BRUSH: painter.setPen(QPen( self.tool_settings['brush_color'], self.tool_settings['brush_size'], Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin )) painter.setBrush(Qt.NoBrush) painter.drawPath(path) elif self.current_tool == self.TOOL_ERASER: # 使用橡皮擦 painter.setCompositionMode(QPainter.CompositionMode_Clear) painter.setPen(QPen( Qt.transparent, self.tool_settings['eraser_size'], Qt.SolidLine, Qt.RoundCap )) painter.drawPath(path) elif self.current_tool == self.TOOL_SHAPE: # 根据形状类型处理 if self.tool_settings['shape_type'] == 0: # 矩形 start = self.map_to_image(self.drawing_start) end = self.map_to_image(self.drawing_item.sceneBoundingRect().bottomRight()) rect = QRectF(start, end).normalized() painter.setPen(QPen( self.tool_settings['brush_color'], self.tool_settings['brush_size'], Qt.SolidLine, Qt.SquareCap, Qt.MiterJoin )) painter.setBrush(Qt.NoBrush) painter.drawRect(rect) elif self.tool_settings['shape_type'] == 1: # 圆形 start = self.map_to_image(self.drawing_start) end = self.map_to_image(self.drawing_item.sceneBoundingRect().bottomRight()) rect = QRectF(start, end).normalized() painter.setPen(QPen( self.tool_settings['brush_color'], self.tool_settings['brush_size'], Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin )) painter.setBrush(Qt.NoBrush) painter.drawEllipse(rect) elif self.tool_settings['shape_type'] == 2: # 直线 line = self.drawing_item.line() start = self.map_to_image(line.p1()) end = self.map_to_image(line.p2()) painter.setPen(QPen( self.tool_settings['brush_color'], self.tool_settings['brush_size'], Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin )) painter.setBrush(Qt.NoBrush) painter.drawLine(start, end) # 结束绘图并清理 painter.end() self.scene.removeItem(self.drawing_item) self.drawing_item = None # 渲染更新后的图层 self._render_layers() self.drawing_active = False self.drawing_path = None self.take_snapshot() def _create_shape(self, start, end): """根据形状类型创建形状图元""" shape_type = self.tool_settings['shape_type'] pen = QPen( self.tool_settings['brush_color'], self.tool_settings['brush_size'], Qt.SolidLine, Qt.RoundCap if shape_type in [1, 2] else Qt.SquareCap, Qt.RoundJoin if shape_type in [1, 2] else Qt.MiterJoin ) if shape_type == 0: # 矩形 rect = QRectF(start, end).normalized() item = QGraphicsRectItem(rect) item.setPen(pen) return item elif shape_type == 1: # 圆形 rect = QRectF(start, end).normalized() item = QGraphicsEllipseItem(rect) item.setPen(pen) return item elif shape_type == 2: # 直线 line = QLineF(start, end) item = QGraphicsLineItem(line) item.setPen(pen) return item return None # ==================== 文本处理功能 ==================== def add_text(self, scene_pos, text, font, color, opacity): """在指定置添加文本""" if not self.layers: return False # 计算文本在图像上的置 img_pos = self.map_to_image(scene_pos) # 创建新的图像绘制工具 painter = QPainter(self.layers[self.current_layer]['image']) painter.setRenderHint(QPainter.TextAntialiasing, True) painter.setFont(font) painter.setPen(color) painter.setOpacity(opacity) # 计算文本边界并绘制 text_metrics = painter.fontMetrics() text_width = text_metrics.horizontalAdvance(text) text_height = text_metrics.height() target_rect = QRectF( img_pos.x(), img_pos.y() - text_height, text_width, text_height ) painter.drawText(target_rect, Qt.AlignLeft | Qt.AlignTop, text) painter.end() # 更新显示 self._render_layers() self.take_snapshot() return True # ==================== 选择和裁剪功能 ==================== def start_selection(self, scene_pos): """开始选择操作""" if self.current_tool in [self.TOOL_SELECT, self.TOOL_CROP]: self.selection_active = True self.selection_start = scene_pos self.rubber_band.setGeometry(QRect(scene_pos.toPoint(), QSize())) self.rubber_band.show() elif self.current_tool == self.TOOL_CROP and not self.selection_rect.isEmpty(): # 检查是否在选择区域的句柄上 self.dragging_handle = self._get_resize_handle_at(scene_pos) if self.dragging_handle: self.resize_selection = True self.drag_start = scene_pos return # 检查是否在选择区域内 if self._get_selection_rect().contains(scene_pos): self.dragging_selection = True self.drag_start = scene_pos def update_selection(self, scene_pos): """更新选择状态""" if not self.selection_active: return if self.resize_selection: # 处理选择框大小调整 self._resize_selection_rect(self.drag_start, scene_pos) self.drag_start = scene_pos elif self.dragging_selection: # 处理选择框拖动 self._move_selection_rect(self.drag_start, scene_pos) self.drag_start = scene_pos elif self.selection_active: # 正常更新选择框大小 start_point = self.selection_start.toPoint() self.rubber_band.setGeometry(QRect(start_point, scene_pos.toPoint()).normalized()) # 更新状态提示 rect = self._get_selection_rect() if not rect.isEmpty(): self._show_status(f"选择区域: {int(rect.width())}×{int(rect.height())}") def stop_selection(self): """结束选择操作""" if not self.selection_active and not self.dragging_selection and not self.resize_selection: return # 获取最终选择区域 self.selection_rect = self._get_selection_rect() self.selection_changed.emit(self.selection_rect) # 清除临时状态 self.rubber_band.hide() self.selection_active = False self.dragging_selection = False self.resize_selection = False self.dragging_handle = -1 # 如果是裁剪工具,则应用裁剪 if self.current_tool == self.TOOL_CROP and not self.selection_rect.isEmpty(): self.crop_to_selection() def _get_selection_rect(self): """获取当前选择区域(场景坐标)""" if self.selection_active: return QRectF(self.rubber_band.geometry()) elif not self.selection_rect.isEmpty(): return self.selection_rect return QRectF() def crop_to_selection(self): """将图像裁剪到选择区域""" if self.selection_rect.isEmpty() or not self.layers: return # 获取场景选择区域转换为图像坐标 img_rect = self._map_selection_to_image(self.selection_rect) if img_rect.isEmpty(): return # 对每个图层应用裁剪 for layer in self.layers: if not layer['image'].isNull() and layer['visible']: # 创建裁剪后的图像 cropped_img = layer['image'].copy( img_rect.x(), img_rect.y(), img_rect.width(), img_rect.height() ) # 更新图层 layer['image'] = cropped_img layer['item'] = None # 重置选择和视图 self.selection_rect = QRectF() self.selection_changed.emit(self.selection_rect) self._render_layers() self.fit_to_view() self.take_snapshot() # ==================== 鼠标事件处理 ==================== def mousePressEvent(self, event): """处理鼠标按下事件""" if event.button() == Qt.LeftButton: scene_pos = self.mapToScene(event.pos()) if self.current_tool in [self.TOOL_BRUSH, self.TOOL_ERASER, self.TOOL_SHAPE]: self.start_drawing(scene_pos) elif self.current_tool in [self.TOOL_SELECT, self.TOOL_CROP]: self.start_selection(scene_pos) else: super().mousePressEvent(event) else: super().mousePressEvent(event) def mouseMoveEvent(self, event): """处理鼠标移动事件""" scene_pos = self.mapToScene(event.pos()) img_pos = self.map_to_image(scene_pos) # 更新鼠标置状态 if not self.displayed_image.isNull(): if 0 <= img_pos.x() < self.displayed_image.width() and 0 <= img_pos.y() < self.displayed_image.height(): color = QColor(self.displayed_image.pixel(img_pos.x(), img_pos.y())) color_str = f"RGB: {color.red()}, {color.green()}, {color.blue()}" if color.alpha() < 255: color_str += f" Alpha: {color.alpha()}" self.mouse_position_changed.emit(img_pos, color_str) # 处理工具操作 if event.buttons() & Qt.LeftButton: if self.current_tool in [self.TOOL_BRUSH, self.TOOL_ERASER, self.TOOL_SHAPE]: self.continue_drawing(scene_pos) elif self.current_tool in [self.TOOL_SELECT, self.TOOL_CROP]: self.update_selection(scene_pos) else: # 更新选择框句柄光标 if self.current_tool == self.TOOL_CROP and not self.selection_rect.isEmpty(): handle_idx = self._get_resize_handle_at(scene_pos) if handle_idx >= 0: self.viewport().setCursor(Qt.SizeFDiagCursor) elif self._get_selection_rect().contains(scene_pos): self.viewport().setCursor(Qt.SizeAllCursor) else: self._update_cursor() super().mouseMoveEvent(event) def mouseReleaseEvent(self, event): """处理鼠标释放事件""" if event.button() == Qt.LeftButton: scene_pos = self.mapToScene(event.pos()) if self.current_tool in [self.TOOL_BRUSH, self.TOOL_ERASER, self.TOOL_SHAPE]: self.stop_drawing() elif self.current_tool in [self.TOOL_SELECT, self.TOOL_CROP]: self.stop_selection() else: super().mouseReleaseEvent(event) else: super().mouseReleaseEvent(event) def wheelEvent(self, event): """处理滚轮事件(缩放)""" modifiers = QApplication.keyboardModifiers() delta = event.angleDelta().y() # Ctrl+滚轮缩放 if modifiers == Qt.ControlModifier: factor = 1.25 if delta > 0 else 0.8 self.set_zoom(self.zoom_factor * factor) event.accept() # 普通滚轮滚动 else: super().wheelEvent(event) def eventFilter(self, obj, event): """全局事件过滤器处理上下文菜单""" if obj == self.viewport() and event.type() == QEvent.ContextMenu: self.show_context_menu(event.pos()) return True return super().eventFilter(obj, event) def show_context_menu(self, pos): """显示上下文菜单""" context_menu = QMenu(self) # 通用操作 undo_action = context_menu.addAction("撤销", self.undo) undo_action.setEnabled(self.current_snapshot > 0) redo_action = context_menu.addAction("重做", self.redo) redo_action.setEnabled(self.current_snapshot < len(self.snapshots) - 1) context_menu.addSeparator() # 当前工具相关操作 if self.current_tool == self.TOOL_SELECT and not self.selection_rect.isEmpty(): context_menu.addAction("复制选区", self.copy_selection) context_menu.addAction("剪切选区", self.cut_selection) context_menu.addSeparator() # 图层操作 context_menu.addAction("新建图层...", self.add_new_layer_prompt) # 视图操作 view_menu = context_menu.addMenu("视图") view_menu.addAction("实际大小", self.set_zoom, 1.0) view_menu.addAction("适应窗口", self.fit_to_view) view_menu.addSeparator() view_menu.addAction("放大", self.zoom_in) view_menu.addAction("缩小", self.zoom_out) # 显示菜单 context_menu.exec_(self.mapToGlobal(pos)) # ==================== 辅助函数 ==================== def _show_image(self, image): """显示图像到场景""" if self.current_image_item: self.scene.removeItem(self.current_image_item) pixmap = QPixmap.fromImage(image) self.current_image_item = self.scene.addPixmap(pixmap) self.scene.setSceneRect(QRectF(pixmap.rect())) self.zoom_changed.emit(self.zoom_factor * 100) def _clear_layers(self): """清除所有图层""" for layer in self.layers: if layer['item']: self.scene.removeItem(layer['item']) self.layers = [] self.current_layer = -1 def _map_selection_to_image(self, scene_rect): """将场景选择区域映射到图像坐标""" if not self.current_image_item: return QRectF() top_left = self.current_image_item.mapFromScene(scene_rect.topLeft()) bottom_right = self.current_image_item.mapFromScene(scene_rect.bottomRight()) return QRectF(top_left, bottom_right).normalized() def _get_resize_handle_at(self, pos): """获取指定置的调整句柄索引""" if not self.current_tool == self.TOOL_CROP or self.selection_rect.isEmpty(): return -1 rect = self.selection_rect handles = self._get_selection_handles() for i, handle in enumerate(handles): if handle.contains(pos): return i return -1 def _get_selection_handles(self): """获取选择框的调整句柄置""" rect = self._get_selection_rect() if rect.isEmpty(): return [] handles = [ QRectF(rect.topLeft(), QSizeF(8, 8)), # 左上 QRectF(rect.topRight() - QPointF(8, 0), QSizeF(8, 8)), # 右上 QRectF(rect.bottomLeft() - QPointF(0, 8), QSizeF(8, 8)), # 左下 QRectF(rect.bottomRight() - QPointF(8, 8), QSizeF(8, 8)) # 右下 ] return handles def _resize_selection_rect(self, from_pos, to_pos): """调整选择框大小""" if self.dragging_handle < 0 or self.dragging_handle > 3: return rect = self.selection_rect delta = to_pos - from_pos new_rect = rect if self.dragging_handle == 0: # 左上 new_rect.setTopLeft(new_rect.topLeft() + delta) elif self.dragging_handle == 1: # 右上 new_rect.setTopRight(new_rect.topRight() + delta) elif self.dragging_handle == 2: # 左下 new_rect.setBottomLeft(new_rect.bottomLeft() + delta) elif self.dragging_handle == 3: # 右下 new_rect.setBottomRight(new_rect.bottomRight() + delta) if new_rect.width() < 10 or new_rect.height() < 10: return # 防止选择框太小 self.selection_rect = new_rect self.selection_changed.emit(self.selection_rect) def _move_selection_rect(self, from_pos, to_pos): """移动选择框置""" rect = self.selection_rect
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值