ReportGeneratorLocal/info_core/Img_edit.py

375 lines
15 KiB
Python
Raw Normal View History

from PySide6.QtCore import Qt, QPointF, QRectF, Signal
from PySide6.QtGui import QPixmap, QPainter, QPen, QBrush, QCursor, QColor, QPainterPath
from PySide6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QPushButton,
QGraphicsView, QGraphicsScene, QGraphicsItem,
QGraphicsRectItem, QGraphicsEllipseItem,
QGraphicsPixmapItem, QSizePolicy)
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)
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()))
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())
# 如果选中,绘制手柄
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
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
super().mousePressEvent(event)
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.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()
def mouseReleaseEvent(self, event):
"""鼠标释放事件"""
self.mouse_press_pos = None
self.mouse_press_rect = None
self.selected_handle = None
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)
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.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)
class DefectMarkEditor(QDialog):
def __init__(self, parent=None, image_path="", current_description=""):
super().__init__(parent)
self.setWindowTitle("缺陷标记编辑器")
self.setMinimumSize(800, 600)
self.image_path = image_path
self.current_description = current_description
self.mouse_state = "select" # select | create_rect | create_ellipse
self.start_pos = None
self.current_item = None
self.scale_factor = 1.0
self.init_ui()
self.load_image()
def init_ui(self):
main_layout = QVBoxLayout(self)
# 工具栏
toolbar = QHBoxLayout()
self.select_btn = QPushButton("选择")
self.select_btn.setCheckable(True)
self.select_btn.setChecked(True)
self.select_btn.clicked.connect(self.set_select_mode)
self.rect_btn = QPushButton("矩形框")
self.rect_btn.setCheckable(True)
self.rect_btn.clicked.connect(self.set_create_rect_mode)
self.ellipse_btn = QPushButton("圆形框")
self.ellipse_btn.setCheckable(True)
self.ellipse_btn.clicked.connect(self.set_create_ellipse_mode)
self.clear_btn = QPushButton("清除所有标记")
self.clear_btn.clicked.connect(self.clear_all_items)
toolbar.addWidget(self.select_btn)
toolbar.addWidget(self.rect_btn)
toolbar.addWidget(self.ellipse_btn)
toolbar.addWidget(self.clear_btn)
# 场景和视图
self.scene = QGraphicsScene(self)
self.view = CustomGraphicsView(self.scene, self)
self.view.setRenderHint(QPainter.Antialiasing)
self.view.setDragMode(QGraphicsView.RubberBandDrag)
self.view.setMouseTracking(True)
main_layout.addLayout(toolbar)
main_layout.addWidget(self.view)
def load_image(self):
"""加载图片到场景"""
if not self.image_path:
return
self.scene.clear()
self.pixmap_item = QGraphicsPixmapItem(QPixmap(self.image_path))
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())
def set_select_mode(self):
"""设置为选择模式"""
self.mouse_state = "select"
self.select_btn.setChecked(True)
self.rect_btn.setChecked(False)
self.ellipse_btn.setChecked(False)
self.view.setDragMode(QGraphicsView.RubberBandDrag)
def set_create_rect_mode(self):
"""设置为创建矩形模式"""
self.mouse_state = "create_rect"
self.select_btn.setChecked(False)
self.rect_btn.setChecked(True)
self.ellipse_btn.setChecked(False)
self.view.setDragMode(QGraphicsView.NoDrag)
def set_create_ellipse_mode(self):
"""设置为创建圆形模式"""
self.mouse_state = "create_ellipse"
self.select_btn.setChecked(False)
self.rect_btn.setChecked(False)
self.ellipse_btn.setChecked(True)
self.view.setDragMode(QGraphicsView.NoDrag)
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 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)