diff --git a/info_core/Img_edit.py b/info_core/Img_edit.py index 1bc91a9..0b4b8c4 100644 --- a/info_core/Img_edit.py +++ b/info_core/Img_edit.py @@ -1,196 +1,308 @@ -from PySide6.QtCore import Qt, QPointF, QRectF, Signal -from PySide6.QtGui import QPixmap, QPainter, QPen, QBrush, QCursor, QColor, QPainterPath +from PySide6.QtCore import Qt, QPointF, QRectF, Signal, QSize, QLineF +from PySide6.QtGui import (QPixmap, QPainter, QPen, QBrush, QCursor, QColor, QImage, + QPainterPath, QTransform) from PySide6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QPushButton, QGraphicsView, QGraphicsScene, QGraphicsItem, QGraphicsRectItem, QGraphicsEllipseItem, QGraphicsPixmapItem, QSizePolicy) - +import math class ResizableGraphicsItem(QGraphicsRectItem): - def __init__(self, x, y, width, height, parent=None): - super().__init__(x, y, width, height, parent) - self.setFlag(QGraphicsItem.ItemIsSelectable, True) - self.setFlag(QGraphicsItem.ItemIsMovable, True) - self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) + handle_radius = 12 # 手柄半径(视觉+点击区域) + rotate_offset = 0 # 旋转手柄离矩形顶边的距离 + + def __init__(self, x, y, w, h, parent=None): + super().__init__(x, y, w, h, parent) + self.setFlags(QGraphicsItem.ItemIsSelectable | + QGraphicsItem.ItemIsMovable | + QGraphicsItem.ItemSendsGeometryChanges) self.setAcceptHoverEvents(True) - - self.handle_size = 8 - self.handles = {} - self.selected_handle = None - self.mouse_press_pos = None - self.mouse_press_rect = None - - # 创建调整大小的手柄 - self.create_handles() - - def create_handles(self): - """创建调整大小的手柄""" - rect = self.rect() - self.handles = { - "top_left": QRectF(rect.left(), rect.top(), self.handle_size, self.handle_size), - "top_right": QRectF(rect.right() - self.handle_size, rect.top(), self.handle_size, self.handle_size), - "bottom_left": QRectF(rect.left(), rect.bottom() - self.handle_size, self.handle_size, self.handle_size), - "bottom_right": QRectF(rect.right() - self.handle_size, rect.bottom() - self.handle_size, self.handle_size, self.handle_size), - "top": QRectF(rect.center().x() - self.handle_size/2, rect.top(), self.handle_size, self.handle_size), - "bottom": QRectF(rect.center().x() - self.handle_size/2, rect.bottom() - self.handle_size, self.handle_size, self.handle_size), - "left": QRectF(rect.left(), rect.center().y() - self.handle_size/2, self.handle_size, self.handle_size), - "right": QRectF(rect.right() - self.handle_size, rect.center().y() - self.handle_size/2, self.handle_size, self.handle_size), - } - - def update_handles(self): - """更新手柄位置""" - rect = self.rect() - self.handles["top_left"].moveTopLeft(rect.topLeft()) - self.handles["top_right"].moveTopRight(rect.topRight()) - self.handles["bottom_left"].moveBottomLeft(rect.bottomLeft()) - self.handles["bottom_right"].moveBottomRight(rect.bottomRight()) - self.handles["top"].moveCenter(QPointF(rect.center().x(), rect.top() + self.handle_size/2)) - self.handles["bottom"].moveCenter(QPointF(rect.center().x(), rect.bottom() - self.handle_size/2)) - self.handles["left"].moveCenter(QPointF(rect.left() + self.handle_size/2, rect.center().y())) - self.handles["right"].moveCenter(QPointF(rect.right() - self.handle_size/2, rect.center().y())) - + self.setPen(QPen(Qt.blue, 2)) + self.setBrush(QBrush(Qt.transparent)) + + self.setTransformOriginPoint(self.rect().center()) + + self._rotating = False + self._resize_corner = None + self._orig_rect = QRectF() + self._press_scene = QPointF() + self._orig_rotation = 0.0 + + self._rotate_anchor = QPointF() # 旋转中心(item 坐标) + self._last_vec = QPointF() # 上一帧鼠标相对向量 + + def setRect(self, rect): + super().setRect(rect) + self.setTransformOriginPoint(rect.center()) + def paint(self, painter, option, widget=None): - """绘制项和手柄""" - # 绘制主矩形 - pen = QPen(Qt.blue, 2, Qt.SolidLine) - if self.isSelected(): - pen.setStyle(Qt.DashLine) - painter.setPen(pen) - painter.setBrush(QBrush(Qt.transparent)) - painter.drawRect(self.rect()) + super().paint(painter, option, widget) + if not self.isSelected(): + return + + painter.save() + painter.setRenderHint(QPainter.Antialiasing) # 消除残影 + painter.setPen(QPen(Qt.black, 1)) + + for name, pt in self._handles_local().items(): + color = Qt.red if name == 'rotate' else Qt.green + painter.setBrush(QBrush(color)) + painter.drawEllipse(pt, self.handle_radius, self.handle_radius) + painter.restore() + + def drawTransformHandles(self, painter): + """绘制变换控制点""" + rect = self.rect() + handle_size = self.handle_radius * 2 - # 如果选中,绘制手柄 - if self.isSelected(): - painter.setBrush(QBrush(Qt.white)) - painter.setPen(QPen(Qt.black, 1)) - for handle in self.handles.values(): - painter.drawRect(handle) - - def hoverMoveEvent(self, event): - """鼠标悬停时检查是否在手柄上""" - for handle_name, handle_rect in self.handles.items(): - if handle_rect.contains(event.pos()): - # 根据手柄位置设置不同的光标 - if handle_name in ["top_left", "bottom_right"]: - self.setCursor(Qt.SizeFDiagCursor) - elif handle_name in ["top_right", "bottom_left"]: - self.setCursor(Qt.SizeBDiagCursor) - elif handle_name in ["top", "bottom"]: - self.setCursor(Qt.SizeVerCursor) - elif handle_name in ["left", "right"]: - self.setCursor(Qt.SizeHorCursor) - self.selected_handle = handle_name - return + # 四个角的控制点 + corners = [ + rect.topLeft(), + rect.topRight(), + rect.bottomLeft(), + rect.bottomRight() + ] - self.setCursor(Qt.SizeAllCursor) - self.selected_handle = None - super().hoverMoveEvent(event) - - def hoverLeaveEvent(self, event): - """鼠标离开时恢复默认光标""" - self.setCursor(Qt.ArrowCursor) - self.selected_handle = None - super().hoverLeaveEvent(event) - - def mousePressEvent(self, event): - """鼠标按下事件""" - if event.button() == Qt.LeftButton: - self.mouse_press_pos = event.pos() - self.mouse_press_rect = self.rect() - - # 如果点击的是手柄,则开始调整大小 - if self.selected_handle: - event.accept() - return + # 旋转控制点(顶部中间) + rotate_pos = QPointF(rect.center().x(), rect.top() - 20) - super().mousePressEvent(event) - + # 绘制控制点 + painter.setBrush(QBrush(Qt.green)) + painter.setPen(QPen(Qt.black, 1)) + for corner in corners: + painter.drawEllipse(corner, handle_size, handle_size) + + # 绘制旋转控制点 + painter.setBrush(QBrush(Qt.red)) + painter.drawEllipse(rotate_pos, handle_size, handle_size) + def mouseMoveEvent(self, event): - """鼠标移动事件""" - if event.buttons() & Qt.LeftButton and self.mouse_press_pos: - if self.selected_handle: - # 调整大小 - self.resize_item(event.pos()) - event.accept() - return - else: - # 移动项 - super().mouseMoveEvent(event) - else: - super().mouseMoveEvent(event) - - def resize_item(self, mouse_pos): - """根据鼠标位置调整项大小""" - rect = self.mouse_press_rect - pos = mouse_pos - new_rect = QRectF(rect) + if self._rotating: + # 当前鼠标向量 + cur_vec = event.scenePos() - self._rotate_center + # 计算角度增量 + old_angle = math.atan2(self._press_vec.y(), self._press_vec.x()) + new_angle = math.atan2(cur_vec.y(), cur_vec.x()) + delta = math.degrees(new_angle - old_angle) + + # 直接累加旋转角,Qt 会按 TransformOriginPoint 旋转 + self.setRotation(self.rotation() + delta) + + self._press_vec = cur_vec + event.accept() + return + + if self._resize_corner: + delta = event.pos() - self._press_local + r = self._orig_rect + new_r = QRectF(r) + if self._resize_corner == 'topLeft': + new_r.setTopLeft(r.topLeft() + delta) + elif self._resize_corner == 'topRight': + new_r.setTopRight(r.topRight() + delta) + elif self._resize_corner == 'bottomLeft': + new_r.setBottomLeft(r.bottomLeft() + delta) + elif self._resize_corner == 'bottomRight': + new_r.setBottomRight(r.bottomRight() + delta) + + if new_r.width() >= 10 and new_r.height() >= 10: + self.setRect(new_r) + event.accept() + return + + super().mouseMoveEvent(event) + + def mousePressEvent(self, event): + if event.button() != Qt.LeftButton: + super().mousePressEvent(event) + return + + hit = self._hit_handle(event.pos()) + if hit == 'rotate': + self._rotating = True + # 记录旋转中心(场景坐标) + self._rotate_center = self.mapToScene(self.rect().center()) + # 记录鼠标第一帧向量(场景坐标) + self._press_vec = event.scenePos() - self._rotate_center + event.accept() + return + + if hit in ('topLeft', 'topRight', 'bottomLeft', 'bottomRight'): + self._resize_corner = hit + self._orig_rect = self.rect() + self._press_local = event.pos() + event.accept() + return - # 根据选中的手柄调整矩形 - if self.selected_handle == "top_left": - new_rect.setTopLeft(pos) - elif self.selected_handle == "top_right": - new_rect.setTopRight(pos) - elif self.selected_handle == "bottom_left": - new_rect.setBottomLeft(pos) - elif self.selected_handle == "bottom_right": - new_rect.setBottomRight(pos) - elif self.selected_handle == "top": - new_rect.setTop(pos.y()) - elif self.selected_handle == "bottom": - new_rect.setBottom(pos.y()) - elif self.selected_handle == "left": - new_rect.setLeft(pos.x()) - elif self.selected_handle == "right": - new_rect.setRight(pos.x()) - - # 确保矩形不会太小 - if new_rect.width() < 10: - if self.selected_handle in ["left", "top_left", "bottom_left"]: - new_rect.setLeft(new_rect.right() - 10) - else: - new_rect.setRight(new_rect.left() + 10) - - if new_rect.height() < 10: - if self.selected_handle in ["top", "top_left", "top_right"]: - new_rect.setTop(new_rect.bottom() - 10) - else: - new_rect.setBottom(new_rect.top() + 10) - - self.setRect(new_rect) - self.update_handles() - + if hit == 'rotate': + self._rotating = True + self._rotate_anchor = self.rect().center() # 旋转中心 + self._last_vec = event.pos() - self._rotate_anchor # 第一帧向量 + event.accept() + return + + super().mousePressEvent(event) + def mouseReleaseEvent(self, event): - """鼠标释放事件""" - self.mouse_press_pos = None - self.mouse_press_rect = None - self.selected_handle = None + self._rotating = False + self._resize_corner = None + self.setCursor(Qt.ArrowCursor) super().mouseReleaseEvent(event) - - def itemChange(self, change, value): - """项变化时更新手柄位置""" - if change == QGraphicsItem.ItemSelectedChange: - self.update_handles() - elif change == QGraphicsItem.ItemPositionHasChanged or change == QGraphicsItem.ItemTransformHasChanged: - self.update_handles() - - return super().itemChange(change, value) - + + def hoverMoveEvent(self, event): + if not self.isSelected(): + super().hoverMoveEvent(event) + return + hit = self._hit_handle(event.pos()) + self.setCursor(Qt.PointingHandCursor if hit == 'rotate' else + Qt.SizeFDiagCursor if hit in ('topLeft', 'bottomRight') else + Qt.SizeBDiagCursor if hit in ('topRight', 'bottomLeft') else + Qt.ArrowCursor) + super().hoverMoveEvent(event) + + def _handles_local(self): + r = self.rect() + return { + 'topLeft' : r.topLeft(), + 'topRight' : r.topRight(), + 'bottomLeft' : r.bottomLeft(), + 'bottomRight': r.bottomRight(), + 'rotate' : QPointF(r.center().x(), r.top() - self.rotate_offset) + } + + def boundingRect(self): + m = self.handle_radius + 5 + return self.rect().adjusted(-m, -m - self.rotate_offset, m, m) + + def _handles(self): + """返回所有手柄的 scene 坐标""" + rect = self.rect() + center = self.mapToScene(rect.center()) + top_mid = self.mapToScene(QPointF(rect.center().x(), rect.top())) + rotate = top_mid + QPointF(0, -20) # 旋转手柄 + return { + 'topLeft' : self.mapToScene(rect.topLeft()), + 'topRight' : self.mapToScene(rect.topRight()), + 'bottomLeft' : self.mapToScene(rect.bottomLeft()), + 'bottomRight': self.mapToScene(rect.bottomRight()), + 'rotate' : rotate + } + + def _hit_handle(self, pos_local): + for name, pt in self._handles_local().items(): + if (pos_local - pt).manhattanLength() <= self.handle_radius: + return name + return None class ResizableEllipseItem(ResizableGraphicsItem): def paint(self, painter, option, widget=None): - """绘制椭圆和手柄""" - # 绘制主椭圆 - pen = QPen(Qt.red, 2, Qt.SolidLine) - if self.isSelected(): - pen.setStyle(Qt.DashLine) - painter.setPen(pen) - painter.setBrush(QBrush(Qt.transparent)) + # 先画椭圆 + painter.setPen(QPen(Qt.red, 2)) + painter.setBrush(Qt.NoBrush) painter.drawEllipse(self.rect()) - - # 如果选中,绘制手柄 + + # 选中时画控制手柄 if self.isSelected(): - painter.setBrush(QBrush(Qt.white)) - painter.setPen(QPen(Qt.black, 1)) - for handle in self.handles.values(): - painter.drawRect(handle) + self.drawTransformHandles(painter) + +class CustomGraphicsView(QGraphicsView): + def __init__(self, scene, editor, parent=None): + super().__init__(scene, parent) + self.editor = editor + self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) + self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) + self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self._pan = False + self._pan_start = QPointF() + self._last_pan_value = QPointF() + + def wheelEvent(self, event): + """鼠标滚轮缩放""" + factor = 1.2 + if event.angleDelta().y() < 0: + factor = 1.0 / factor + + self.scale(factor, factor) + self.editor.scale_factor *= factor + self.limit_view_to_image() + + def mousePressEvent(self, event): + if event.button() == Qt.LeftButton: + scene_pos = self.mapToScene(event.position().toPoint()) + items = self.scene().items(scene_pos) + + # 如果点到了框线,让框线处理,且禁止 pan + if any(isinstance(it, (ResizableGraphicsItem, ResizableEllipseItem)) + for it in items): + super().mousePressEvent(event) + return + + # 选择模式下点空白,启动 pan + if self.editor.mouse_state == "select": + self.scene().clearSelection() # 取消所有选中 + self._pan = True + self._pan_start = event.position() + self.setCursor(Qt.ClosedHandCursor) + return + + if self.editor.mouse_state in ["create_rect", "create_ellipse"]: + self.editor.start_pos = scene_pos + if self.editor.mouse_state == "create_rect": + self.editor.current_item = ResizableGraphicsItem(0, 0, 0, 0) + else: + self.editor.current_item = ResizableEllipseItem(0, 0, 0, 0) + self.editor.current_item.setRect(QRectF(scene_pos, scene_pos)) + self.scene().addItem(self.editor.current_item) + self.editor.current_item.setSelected(True) + return + + super().mousePressEvent(event) + + + def mouseMoveEvent(self, event): + if self._pan: + delta = event.position() - self._pan_start + self._pan_start = event.position() + hs = self.horizontalScrollBar() + vs = self.verticalScrollBar() + hs.setValue(hs.value() - int(delta.x())) + vs.setValue(vs.value() - int(delta.y())) + return + if self.editor.current_item and self.editor.start_pos: + scene_pos = self.mapToScene(event.position().toPoint()) + rect = QRectF(self.editor.start_pos, scene_pos).normalized() + self.editor.current_item.setRect(rect) + return + + super().mouseMoveEvent(event) + + def mouseReleaseEvent(self, event): + if event.button() == Qt.MiddleButton or self._pan: + self._pan = False + self.setCursor(Qt.ArrowCursor) + return + if self.editor.current_item: + if self.editor.current_item.rect().width() < 10 or self.editor.current_item.rect().height() < 10: + self.scene().removeItem(self.editor.current_item) + else: + self.editor.set_select_mode() + self.editor.current_item = None + self.editor.start_pos = None + return + + super().mouseReleaseEvent(event) + + def limit_view_to_image(self): + """限制视图范围不超过图片边界""" + if not self.editor.pixmap_item: + return + + # 确保滚动条不会超出范围 + h_scroll = self.horizontalScrollBar() + v_scroll = self.verticalScrollBar() + + h_scroll.setValue(max(0, min(h_scroll.value(), h_scroll.maximum()))) + v_scroll.setValue(max(0, min(v_scroll.value(), v_scroll.maximum()))) class DefectMarkEditor(QDialog): def __init__(self, parent=None, image_path="", current_description=""): @@ -204,7 +316,11 @@ class DefectMarkEditor(QDialog): self.start_pos = None self.current_item = None self.scale_factor = 1.0 - + self.is_panning = False + self.pan_start_pos = QPointF() + self.max_view_size = QSize(1920, 1080) # 最大视图尺寸 + self.pixmap_item = None + self.init_ui() self.load_image() @@ -239,8 +355,8 @@ class DefectMarkEditor(QDialog): self.scene = QGraphicsScene(self) self.view = CustomGraphicsView(self.scene, self) self.view.setRenderHint(QPainter.Antialiasing) - self.view.setDragMode(QGraphicsView.RubberBandDrag) self.view.setMouseTracking(True) + self.view.setDragMode(QGraphicsView.NoDrag) main_layout.addLayout(toolbar) main_layout.addWidget(self.view) @@ -251,13 +367,32 @@ class DefectMarkEditor(QDialog): return self.scene.clear() - self.pixmap_item = QGraphicsPixmapItem(QPixmap(self.image_path)) + + # 加载图片并计算合适的缩放比例 + image = QImage(self.image_path) + if image.isNull(): + return + + # 计算缩放比例以适应视图 + img_size = image.size() + + # 如果图片大于最大视图尺寸,则缩放 + if img_size.width() > self.max_view_size.width() or img_size.height() > self.max_view_size.height(): + img_size.scale(self.max_view_size, Qt.KeepAspectRatio) + scaled_pixmap = QPixmap.fromImage(image.scaled(img_size, Qt.KeepAspectRatio, Qt.SmoothTransformation)) + else: + scaled_pixmap = QPixmap.fromImage(image) + + self.pixmap_item = QGraphicsPixmapItem(scaled_pixmap) self.pixmap_item.setFlag(QGraphicsItem.ItemIsMovable, False) self.pixmap_item.setFlag(QGraphicsItem.ItemIsSelectable, False) self.scene.addItem(self.pixmap_item) # 设置场景大小为图片大小 self.scene.setSceneRect(self.pixmap_item.boundingRect()) + + # 自动调整视图以显示整个图片 + self.view.fitInView(self.pixmap_item, Qt.KeepAspectRatio) def set_select_mode(self): """设置为选择模式""" @@ -265,7 +400,7 @@ class DefectMarkEditor(QDialog): self.select_btn.setChecked(True) self.rect_btn.setChecked(False) self.ellipse_btn.setChecked(False) - self.view.setDragMode(QGraphicsView.RubberBandDrag) + self.view.setCursor(Qt.ArrowCursor) def set_create_rect_mode(self): """设置为创建矩形模式""" @@ -273,7 +408,7 @@ class DefectMarkEditor(QDialog): self.select_btn.setChecked(False) self.rect_btn.setChecked(True) self.ellipse_btn.setChecked(False) - self.view.setDragMode(QGraphicsView.NoDrag) + self.view.setCursor(Qt.CrossCursor) def set_create_ellipse_mode(self): """设置为创建圆形模式""" @@ -281,94 +416,90 @@ class DefectMarkEditor(QDialog): self.select_btn.setChecked(False) self.rect_btn.setChecked(False) self.ellipse_btn.setChecked(True) - self.view.setDragMode(QGraphicsView.NoDrag) + self.view.setCursor(Qt.CrossCursor) def clear_all_items(self): """清除所有标记项""" for item in self.scene.items(): if isinstance(item, (ResizableGraphicsItem, ResizableEllipseItem)): self.scene.removeItem(item) - - def mouse_press_on_scene(self, pos): - """场景鼠标按下事件""" - if self.mouse_state == "select": - return - - self.start_pos = pos - if self.mouse_state == "create_rect": - self.current_item = ResizableGraphicsItem(pos.x(), pos.y(), 1, 1) - elif self.mouse_state == "create_ellipse": - self.current_item = ResizableEllipseItem(pos.x(), pos.y(), 1, 1) - - if self.current_item: - self.scene.addItem(self.current_item) - - def mouse_move_on_scene(self, pos): - """场景鼠标移动事件""" - if not self.start_pos or not self.current_item: - return - - rect = QRectF(self.start_pos, pos).normalized() - self.current_item.setRect(rect) - - def mouse_release_on_scene(self, pos): - """场景鼠标释放事件""" - if self.current_item and self.mouse_state != "select": - # 如果创建的项太小,则删除 - if self.current_item.rect().width() < 10 or self.current_item.rect().height() < 10: - self.scene.removeItem(self.current_item) - - self.start_pos = None - self.current_item = None - - # 创建完成后自动回到选择模式 - self.set_select_mode() -class CustomGraphicsView(QGraphicsView): - def __init__(self, scene, editor, parent=None): - super().__init__(scene, parent) - self.editor = editor - self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse) - self.setResizeAnchor(QGraphicsView.AnchorUnderMouse) - self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn) - self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + def export_annotated_image(self, output_dir): + """ + 将当前场景(原图 + 所有标记)按原分辨率导出为 PNG/JPG, + 文件名与原图完全一致,保存到 output_dir。 + """ + import os + from PySide6.QtGui import QPainter, QImage + + if not self.pixmap_item: + return False + + # 1. 取原图 QImage(保持原始分辨率) + source_image = QImage(self.image_path) + if source_image.isNull(): + return False + + # 2. 创建与原图同尺寸的 QImage 作为画布 + out_image = QImage(source_image.size(), QImage.Format_ARGB32) + out_image.fill(Qt.transparent) + + painter = QPainter(out_image) + painter.setRenderHint(QPainter.Antialiasing) + + # 3. 先把原图完整画上去 + painter.drawImage(0, 0, source_image) + + # 4. 计算场景 → 原图坐标 的缩放因子 + scene_rect = self.pixmap_item.sceneBoundingRect() + sx = source_image.width() / scene_rect.width() + sy = source_image.height() / scene_rect.height() + + # 5. 逐个绘制标记(仅最终形状,不绘制手柄) + for item in self.scene.items(): + if isinstance(item, (ResizableGraphicsItem, ResizableEllipseItem)): + # 关闭选中状态,避免手柄被绘制 + was_selected = item.isSelected() + item.setSelected(False) + + # 计算 item 在场景中的实际几何 + shape_path = item.mapToScene(item.shape()) # 考虑旋转 + painter.setTransform(QTransform()) # 重置 painter + painter.scale(sx, sy) # 映射到原图坐标 + painter.translate(-scene_rect.x(), -scene_rect.y()) # 对齐偏移 + + # 设置画笔 + pen = item.pen() + pen.setCosmetic(False) # 让线宽随缩放 + painter.setPen(pen) + painter.setBrush(Qt.NoBrush) + + # 绘制形状 + if isinstance(item, ResizableEllipseItem): + painter.drawPath(shape_path) + else: + painter.drawPath(shape_path) + + # 恢复选中状态 + item.setSelected(was_selected) + + painter.end() + + # 6. 保存文件 + os.makedirs(output_dir, exist_ok=True) + base_name = os.path.basename(self.image_path) + save_path = os.path.join(output_dir, base_name) + return out_image.save(save_path) - def wheelEvent(self, event): - """鼠标滚轮缩放""" - factor = 1.2 - if event.angleDelta().y() < 0: - factor = 1.0 / factor - - self.scale(factor, factor) - self.editor.scale_factor *= factor - - def mousePressEvent(self, event): - """鼠标按下事件""" - if event.button() == Qt.LeftButton: - scene_pos = self.mapToScene(event.pos()) - items = self.scene().items(scene_pos) - - # 如果点击的是背景或图片,则开始创建 - if not items or items[-1] == self.editor.pixmap_item: - self.editor.mouse_press_on_scene(scene_pos) - else: - super().mousePressEvent(event) - else: - super().mousePressEvent(event) - - def mouseMoveEvent(self, event): - """鼠标移动事件""" - if self.editor.start_pos and self.editor.current_item: - scene_pos = self.mapToScene(event.pos()) - self.editor.mouse_move_on_scene(scene_pos) - else: - super().mouseMoveEvent(event) - - def mouseReleaseEvent(self, event): - """鼠标释放事件""" - if event.button() == Qt.LeftButton and self.editor.start_pos: - scene_pos = self.mapToScene(event.pos()) - self.editor.mouse_release_on_scene(scene_pos) - else: - super().mouseReleaseEvent(event) + def closeEvent(self, event): + """关闭窗口时保存标记""" + self.export_annotated_image("/home/dtyx/桌面/yhh/ReportGenerator/output") + super().closeEvent(event) + +if __name__ == '__main__': + from PySide6.QtWidgets import QApplication + import sys + app = QApplication(sys.argv) + editor = DefectMarkEditor(image_path=r"/home/dtyx/桌面/yhh/ReportGenerator/测试数据/山东国华无棣风电场叶片外部数据/A1(31301)/1#(1-GW68.6C-I190809C)/DJI_20250606162745_0005_Z.JPG") + editor.show() + sys.exit(app.exec()) diff --git a/info_core/MyQtClass.py b/info_core/MyQtClass.py index 960deaf..ee13f38 100644 --- a/info_core/MyQtClass.py +++ b/info_core/MyQtClass.py @@ -2586,7 +2586,7 @@ class DictionaryBrowser(QWidget): current_desc = self.current_dict_data.get(key, "") if key in self.current_dict_data else "" # 创建并显示编辑对话框 - editor = DefectDescriptionEditor(self, current_description=current_desc) + editor = DefectDescriptionEditor(self, current_description=current_desc, img_path=img_path) if editor.exec_() == QDialog.Accepted: # 获取新描述 new_desc = editor.get_description() @@ -2648,7 +2648,7 @@ class DefectDescriptionEditor(QDialog): main_layout.setContentsMargins(5, 5, 5, 5) # 左侧 - 图片编辑器 (使用DefectMarkEditor) - self.image_editor = DefectMarkEditor(self, self.img_path) + self.image_editor = DefectMarkEditor(image_path=self.img_path) self.image_editor.setMinimumSize(600, 600) main_layout.addWidget(self.image_editor, stretch=2)