增加缺陷填写框,右侧图片列表能够接收图片放入并同步树模型和、json文件
This commit is contained in:
parent
81d6711fac
commit
cc460cfbcd
|
@ -0,0 +1,374 @@
|
|||
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)
|
|
@ -6,7 +6,8 @@ from PySide6.QtWidgets import (QGroupBox, QVBoxLayout, QHBoxLayout, QCheckBox,
|
|||
QStackedWidget, QStackedLayout, QStyledItemDelegate,
|
||||
QTreeView, QSplitter, QFileSystemModel, QScrollArea,
|
||||
QToolTip, QGridLayout, QSizePolicy, QProgressDialog,
|
||||
QButtonGroup, QInputDialog, QMenu)
|
||||
QButtonGroup, QInputDialog, QMenu, QComboBox, QDialogButtonBox,
|
||||
QTextEdit, QStyle)
|
||||
from PySide6.QtCore import (QDateTime,QTimer,QDateTime,Signal,QSettings, QPoint, QEvent,
|
||||
QSortFilterProxyModel, QSize, QDir, QMimeData, QRunnable, QObject,
|
||||
QThreadPool, QPoint, QRect, QUrl)
|
||||
|
@ -14,8 +15,9 @@ from PySide6.QtGui import (QPixmap, QDragEnterEvent, QDropEvent, Qt, QIcon,
|
|||
QFontMetrics, QStandardItem, QStandardItemModel,
|
||||
QAction, QColor, QImageReader, QDrag, QCursor,
|
||||
QPainter)
|
||||
import json, sys, os
|
||||
import json, sys, os, re, time
|
||||
|
||||
from info_core.Img_edit import DefectMarkEditor
|
||||
from info_core.defines import *
|
||||
|
||||
class ConfigComboBoxGroup(QGroupBox):
|
||||
|
@ -1514,34 +1516,69 @@ class FolderBrowser(QWidget):
|
|||
self.right_model.dataChanged.connect(self.on_right_data_changed)
|
||||
self.left_tree.expanded.connect(self.sync_right_tree_expand)
|
||||
self.left_tree.collapsed.connect(self.sync_right_tree_collapse)
|
||||
self.left_tree.clicked.connect(self.on_left_tree_clicked)
|
||||
self.folder_selected.connect(self.image_browser.show_images)
|
||||
|
||||
self.right_tree.expanded.connect(self.sync_left_tree_expand)
|
||||
self.right_tree.collapsed.connect(self.sync_left_tree_collapse)
|
||||
self.right_tree.clicked.connect(self.on_right_tree_clicked)
|
||||
self.left_tree.clicked.connect(self.on_left_tree_clicked)
|
||||
self.folder_selected.connect(self.image_browser.show_images)
|
||||
self.folder_selected.connect(self.update_blade_name)
|
||||
|
||||
self.dictionary_browser.dictionary_updated.connect(self.handle_dictionary_update)
|
||||
|
||||
def handle_dictionary_update(self, folder_name, part_name, dict_type_en, dict_data):
|
||||
"""处理字典更新"""
|
||||
print(f"🔧 字典更新 {self.json_data}:\n[{folder_name}][{part_name}][{dict_type_en}] -> \n{dict_data}")
|
||||
# 1. 更新JSON数据
|
||||
if folder_name in self.json_data and part_name in self.json_data[folder_name]:
|
||||
self.json_data[folder_name][part_name][dict_type_en] = dict_data
|
||||
|
||||
# 2. 更新树模型
|
||||
self.update_right_tree(folder_name)
|
||||
|
||||
# 3. 保存到文件
|
||||
self.save_to_json(folder_name)
|
||||
|
||||
def update_right_tree(self, folder_name):
|
||||
"""更新右侧树视图"""
|
||||
# 找到对应的文件夹项
|
||||
for row in range(self.right_model.rowCount()):
|
||||
folder_item = self.right_model.item(row, 0)
|
||||
if folder_item.text() == folder_name:
|
||||
# 更新整个子树
|
||||
TreeModelManager.update_model_from_json(
|
||||
self.right_model,
|
||||
folder_name,
|
||||
self.json_data
|
||||
)
|
||||
# 展开该节点
|
||||
self.right_tree.expand(folder_item.index())
|
||||
break
|
||||
|
||||
def on_right_tree_clicked(self, index):
|
||||
"""处理右侧树视图点击事件"""
|
||||
if not index.isValid():
|
||||
return
|
||||
|
||||
item = self.right_model.itemFromIndex(index)
|
||||
path = self.get_item_path(item)
|
||||
print(f"点击了右侧树节点: {item.text()}")
|
||||
|
||||
# 检查是否点击了字典节点
|
||||
if "dictionaries" in path:
|
||||
parts = path.split("/dictionaries/")
|
||||
if len(parts) == 2:
|
||||
blade_name = parts[0]
|
||||
dict_type = parts[1]
|
||||
|
||||
# 获取字典数据
|
||||
dict_data = self.json_data.get(blade_name, {}).get("dictionaries", {}).get(dict_type, {})
|
||||
|
||||
# 更新字典浏览器
|
||||
self.dictionary_browser.show_dictionary(blade_name, dict_type, dict_data)
|
||||
|
||||
text = item.text() # 获取显示文本(中文)
|
||||
print(f"点击了右侧树节点: {text}")
|
||||
# 检查是否点击了字典节点(中文)
|
||||
if text in ["缺陷图字典", "典型图字典"]:
|
||||
# 获取part名称和原文件夹名
|
||||
part_item = item.parent()
|
||||
folder_item = part_item.parent() if part_item else None
|
||||
|
||||
if part_item and folder_item:
|
||||
folder_name = folder_item.text()
|
||||
part_name = part_item.text()
|
||||
# 转换为英文键名获取数据
|
||||
dict_type = DictionaryBrowser.DICT_TYPE_MAPPING.get(text, "")
|
||||
if dict_type:
|
||||
dict_data = self.json_data.get(folder_name, {}).get(DictionaryBrowser.DICT_TYPE_MAPPING.get(part_name, "")).get(dict_type, {})
|
||||
|
||||
# 传递中文显示名称
|
||||
self.dictionary_browser.show_dictionary(folder_name, part_name, text, dict_data)
|
||||
|
||||
def get_item_path(self, item):
|
||||
"""获取树节点路径"""
|
||||
path = []
|
||||
|
@ -1555,22 +1592,6 @@ class FolderBrowser(QWidget):
|
|||
# 假设叶片名称是文件夹路径的最后一部分
|
||||
blade_name = os.path.basename(folder_path.rstrip('/\\'))
|
||||
self.dictionary_browser.set_current_blade(blade_name)
|
||||
|
||||
def save_dictionary_data(self, dict_type, blade_name, data):
|
||||
"""保存字典数据到JSON"""
|
||||
# 找到对应的文件夹项
|
||||
for folder_name in self.json_data:
|
||||
if blade_name in folder_name: # 简单匹配逻辑,可根据实际情况调整
|
||||
# 在JSON数据中创建或更新字典数据
|
||||
if "dictionaries" not in self.json_data[folder_name]:
|
||||
self.json_data[folder_name]["dictionaries"] = {}
|
||||
|
||||
if dict_type not in self.json_data[folder_name]["dictionaries"]:
|
||||
self.json_data[folder_name]["dictionaries"][dict_type] = {}
|
||||
|
||||
self.json_data[folder_name]["dictionaries"][dict_type] = data
|
||||
self.save_to_json(folder_name)
|
||||
break
|
||||
|
||||
def on_left_tree_clicked(self, index):
|
||||
"""处理左侧树视图选择变化事件"""
|
||||
|
@ -1744,6 +1765,11 @@ class FolderBrowser(QWidget):
|
|||
self.data_changed.emit(self.json_data)
|
||||
|
||||
def save_to_json(self, folder_name):
|
||||
"""将类中现在的self.json_data保存到JSON文件
|
||||
|
||||
Args:
|
||||
folder_name (str): 文件夹名称(要保存的json文件名,文件名为机组号)
|
||||
"""
|
||||
json_path = self.get_json_file_path(folder_name)
|
||||
print(f"🛠️ 准备保存到: {json_path}")
|
||||
|
||||
|
@ -2278,20 +2304,59 @@ class ImageBrowser(QWidget):
|
|||
except Exception as e:
|
||||
print(f"拖动失败: {str(e)}")
|
||||
|
||||
|
||||
class DictionaryBrowser(QWidget):
|
||||
"""右侧叶片典型/缺陷字典浏览窗口"""
|
||||
dictionary_updated = Signal(str, str, dict) # 字典类型, 叶片名称, 字典数据
|
||||
|
||||
class OverlayLabel(QLabel):
|
||||
"""支持叠加图标的标签控件"""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.current_blade = "" # 当前叶片名称
|
||||
self.current_dict_type = "" # 当前字典类型(典型/缺陷)
|
||||
self.dictionaries = {} # 存储所有字典数据
|
||||
self.init_ui()
|
||||
self._overlay_icon = None
|
||||
|
||||
def set_overlay_icon(self, standard_pixmap):
|
||||
"""设置叠加图标"""
|
||||
self._overlay_icon = standard_pixmap
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
"""重绘事件,绘制图标叠加"""
|
||||
super().paintEvent(event)
|
||||
|
||||
if self._overlay_icon and self.pixmap():
|
||||
# 获取系统标准图标
|
||||
icon = self.style().standardIcon(self._overlay_icon)
|
||||
pixmap = icon.pixmap(24, 24) # 图标大小
|
||||
|
||||
# 在右下角绘制图标
|
||||
painter = QPainter(self)
|
||||
margin = 5
|
||||
x = self.width() - pixmap.width() - margin
|
||||
y = self.height() - pixmap.height() - margin
|
||||
painter.drawPixmap(x, y, pixmap)
|
||||
painter.end()
|
||||
|
||||
class DictionaryBrowser(QWidget):
|
||||
"""字典浏览编辑窗口"""
|
||||
dictionary_updated = Signal(str, str, str, dict) # 原文件夹名, part名称, 字典类型(英文), 字典数据
|
||||
|
||||
# 中英文映射字典
|
||||
DICT_TYPE_MAPPING = {
|
||||
"缺陷图字典": "defect_picture_dict",
|
||||
"典型图字典": "typical_picture_dict",
|
||||
"叶片1" : "part1",
|
||||
"叶片2" : "part2",
|
||||
"叶片3" : "part3",
|
||||
}
|
||||
|
||||
REVERSE_DICT_MAPPING = {v: k for k, v in DICT_TYPE_MAPPING.items()}
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.original_folder_name = "" # 原文件夹名
|
||||
self.current_part = "" # 当前part名称(part1/part2/part3)
|
||||
self.current_dict_type = "" # 当前字典类型(英文)
|
||||
self.current_dict_data = {} # 当前字典数据
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化用户界面"""
|
||||
"""初始化界面"""
|
||||
self.setMinimumWidth(300)
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
@ -2305,106 +2370,112 @@ class DictionaryBrowser(QWidget):
|
|||
# 图片浏览区域
|
||||
self.scroll_area = QScrollArea()
|
||||
self.scroll_area.setWidgetResizable(True)
|
||||
|
||||
# 使用网格布局
|
||||
self.image_container = QWidget()
|
||||
self.image_layout = QGridLayout(self.image_container)
|
||||
self.image_layout.setContentsMargins(5, 5, 5, 5)
|
||||
self.image_layout.setSpacing(10)
|
||||
self.image_layout.setAlignment(Qt.AlignTop)
|
||||
|
||||
self.scroll_area.setWidget(self.image_container)
|
||||
layout.addWidget(self.scroll_area)
|
||||
|
||||
# 初始状态提示
|
||||
self.placeholder_label = QLabel("请点击右侧JSON树中的字典节点")
|
||||
self.placeholder_label.setAlignment(Qt.AlignCenter)
|
||||
self.placeholder_label.setStyleSheet("color: gray; font-size: 14px;")
|
||||
self.scroll_area.setWidget(self.placeholder_label)
|
||||
self.set_placeholder("请点击右侧JSON树中的字典节点")
|
||||
|
||||
# 设置拖放接收
|
||||
self.setAcceptDrops(True)
|
||||
|
||||
def show_dictionary(self, blade_name, dict_type, dict_data):
|
||||
"""显示指定字典内容"""
|
||||
self.current_blade = blade_name
|
||||
self.current_dict_type = dict_type
|
||||
self.dictionaries = dict_data
|
||||
def set_placeholder(self, text):
|
||||
"""设置占位文本"""
|
||||
label = QLabel(text)
|
||||
label.setAlignment(Qt.AlignCenter)
|
||||
label.setStyleSheet("color: gray; font-size: 14px;")
|
||||
self.scroll_area.setWidget(label)
|
||||
|
||||
def show_dictionary(self, folder_name, part_name, dict_type_display, dict_data):
|
||||
"""显示指定字典内容
|
||||
:param dict_type_display: 显示用的字典类型名称(中文)
|
||||
"""
|
||||
self.original_folder_name = folder_name
|
||||
self.current_part = self.DICT_TYPE_MAPPING.get(part_name, "")
|
||||
# 转换为英文键名存储
|
||||
self.current_dict_type = self.DICT_TYPE_MAPPING.get(dict_type_display, "")
|
||||
self.current_dict_data = dict_data or {}
|
||||
|
||||
self.update_title()
|
||||
self.refresh_dictionary_view()
|
||||
|
||||
def setup_connections(self):
|
||||
"""设置信号和槽的连接"""
|
||||
self.dict_type_group.buttonClicked.connect(self.on_dict_type_changed)
|
||||
|
||||
def on_dict_type_changed(self, button):
|
||||
"""处理字典类型选择变化"""
|
||||
# 直接比较按钮对象来确定类型
|
||||
if button == self.typical_btn:
|
||||
self.current_dict_type = "典型图"
|
||||
else:
|
||||
self.current_dict_type = "缺陷图"
|
||||
self.update_title()
|
||||
self.refresh_dictionary_view()
|
||||
|
||||
def set_current_blade(self, blade_name):
|
||||
"""设置当前叶片名称"""
|
||||
self.current_blade = blade_name
|
||||
self.update_title()
|
||||
self.refresh_dictionary_view()
|
||||
self.refresh_view()
|
||||
|
||||
def update_title(self):
|
||||
"""更新标题显示"""
|
||||
if not self.current_blade or not self.current_dict_type:
|
||||
"""更新标题显示(使用中文)"""
|
||||
if not all([self.original_folder_name, self.current_part, self.current_dict_type]):
|
||||
self.title_label.setText("请选择字典节点")
|
||||
else:
|
||||
self.title_label.setText(f"{self.current_blade} - {self.current_dict_type}")
|
||||
# 转换为中文显示
|
||||
dict_name = self.REVERSE_DICT_MAPPING.get(self.current_dict_type, self.current_dict_type)
|
||||
self.title_label.setText(f"{self.original_folder_name} - {self.current_part} - {dict_name}")
|
||||
|
||||
def refresh_dictionary_view(self):
|
||||
"""刷新字典视图"""
|
||||
if not self.current_dict_type or not self.current_blade:
|
||||
self.scroll_area.setWidget(self.placeholder_label)
|
||||
def refresh_view(self):
|
||||
"""刷新视图"""
|
||||
# 安全清除旧内容
|
||||
old_widget = self.scroll_area.takeWidget()
|
||||
if old_widget:
|
||||
old_widget.deleteLater()
|
||||
|
||||
if not all([self.original_folder_name, self.current_part, self.current_dict_type]):
|
||||
self.set_placeholder("请选择有效的字典节点")
|
||||
return
|
||||
|
||||
# 获取当前字典数据
|
||||
dict_data = self.dictionaries.get(self.current_dict_type, {})
|
||||
|
||||
if not dict_data:
|
||||
no_data_label = QLabel(f"该字典没有数据")
|
||||
no_data_label.setAlignment(Qt.AlignCenter)
|
||||
no_data_label.setStyleSheet("color: gray; font-size: 14px;")
|
||||
self.scroll_area.setWidget(no_data_label)
|
||||
if not self.current_dict_data:
|
||||
self.set_placeholder("该字典没有数据")
|
||||
return
|
||||
|
||||
# 创建图片容器
|
||||
self.image_container = QWidget()
|
||||
self.image_layout = QGridLayout(self.image_container)
|
||||
self.image_layout.setContentsMargins(5, 5, 5, 5)
|
||||
self.image_layout.setSpacing(5)
|
||||
self.image_layout.setAlignment(Qt.AlignTop)
|
||||
# 创建新的容器
|
||||
container = QWidget()
|
||||
layout = QVBoxLayout(container) # 改为垂直布局
|
||||
layout.setContentsMargins(5, 5, 5, 5)
|
||||
layout.setSpacing(5)
|
||||
|
||||
self.scroll_area.setWidget(self.image_container)
|
||||
# 创建水平流式布局的容器
|
||||
flow_widget = QWidget()
|
||||
flow_layout = QHBoxLayout(flow_widget)
|
||||
flow_layout.setContentsMargins(0, 0, 0, 0)
|
||||
flow_layout.setSpacing(5)
|
||||
flow_layout.setAlignment(Qt.AlignLeft | Qt.AlignTop) # 左对齐且顶部对齐
|
||||
|
||||
# 添加图片项
|
||||
for i, (desc, img_path) in enumerate(dict_data.items()):
|
||||
self.add_image_item(img_path, desc, i)
|
||||
|
||||
def add_image_item(self, img_path, description, index):
|
||||
"""添加单个图片项到布局"""
|
||||
thumbnail_size = 150 # 缩略图大小
|
||||
for key, img_path in self.current_dict_data.items():
|
||||
self._add_image_item(flow_layout, img_path, key)
|
||||
|
||||
# 添加流式布局到主布局
|
||||
layout.addWidget(flow_widget)
|
||||
layout.addStretch(1) # 添加拉伸因子使内容保持在顶部
|
||||
|
||||
self.scroll_area.setWidget(container)
|
||||
|
||||
def _check_description_format(self, key):
|
||||
"""检查描述是否符合标准格式"""
|
||||
if not key or "_" not in key:
|
||||
return False
|
||||
|
||||
parts = key.split("_")
|
||||
# 检查是否有足够的字段(序号+6个标准字段+建议)
|
||||
return len(parts) >= 8
|
||||
|
||||
def _add_image_item(self, parent_layout, img_path, key):
|
||||
"""添加单个图片项 - 只显示图片,符合格式的显示对勾"""
|
||||
thumbnail_size = 150
|
||||
|
||||
# 创建容器widget
|
||||
item_widget = QWidget()
|
||||
|
||||
# 设置悬停提示
|
||||
hover_text = f"图片路径: {img_path}\n\n描述详情:\n{self.get_description_display(key)}"
|
||||
item_widget.setToolTip(hover_text)
|
||||
|
||||
item_layout = QVBoxLayout(item_widget)
|
||||
item_layout.setContentsMargins(5, 5, 5, 5)
|
||||
item_layout.setSpacing(3)
|
||||
item_layout.setSpacing(0)
|
||||
|
||||
# 图片标签
|
||||
img_label = QLabel()
|
||||
# 图片标签 - 使用自定义的OverlayLabel类
|
||||
img_label = OverlayLabel()
|
||||
img_label.setFixedSize(thumbnail_size, thumbnail_size)
|
||||
img_label.setAlignment(Qt.AlignCenter)
|
||||
img_label.setStyleSheet("border: 1px solid #ddd; background: white;")
|
||||
|
||||
# 检查描述是否符合格式
|
||||
is_valid_format = self._check_description_format(key)
|
||||
|
||||
# 加载图片
|
||||
if os.path.exists(img_path):
|
||||
|
@ -2414,140 +2485,398 @@ class DictionaryBrowser(QWidget):
|
|||
thumbnail_size, thumbnail_size,
|
||||
Qt.KeepAspectRatio, Qt.SmoothTransformation
|
||||
))
|
||||
# 如果格式正确,显示对勾
|
||||
if is_valid_format:
|
||||
img_label.set_overlay_icon(QStyle.SP_DialogApplyButton)
|
||||
else:
|
||||
img_label.setText("无效图片")
|
||||
img_label.setStyleSheet("color: red;")
|
||||
else:
|
||||
img_label.setText("图片不存在")
|
||||
img_label.setStyleSheet("color: red;")
|
||||
|
||||
# 描述标签
|
||||
desc_label = QLabel(description)
|
||||
desc_label.setAlignment(Qt.AlignCenter)
|
||||
desc_label.setStyleSheet("font-size: 11px;")
|
||||
desc_label.setWordWrap(True)
|
||||
|
||||
# 添加到布局
|
||||
item_layout.addWidget(img_label)
|
||||
item_layout.addWidget(desc_label)
|
||||
|
||||
# 添加上下文菜单
|
||||
item_widget.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
item_widget.customContextMenuRequested.connect(
|
||||
lambda pos, path=img_path, desc=description: self.show_context_menu(pos, path, desc)
|
||||
lambda pos, path=img_path, k=key: self.show_context_menu(pos, path, k))
|
||||
|
||||
parent_layout.addWidget(item_widget)
|
||||
|
||||
def get_description_display(self, key):
|
||||
"""获取描述信息的显示文本 - 用于悬停提示"""
|
||||
if not key or "_" not in key:
|
||||
return "⚠ 无描述信息"
|
||||
|
||||
parts = key.split("_")
|
||||
if len(parts) < 8:
|
||||
return f"⚠ 描述格式不完整\n当前描述: {key}"
|
||||
|
||||
# 格式化显示
|
||||
description = (
|
||||
f"✔ 已编辑描述\n"
|
||||
f"缺陷类型: {parts[1]}\n"
|
||||
f"位置: {parts[2]}\n"
|
||||
f"尺寸: {parts[3]}\n"
|
||||
f"可见程度: {parts[4]}\n"
|
||||
f"紧急程度: {parts[5]}\n"
|
||||
f"危重等级: {parts[6]}\n"
|
||||
f"维修建议: {'_'.join(parts[7:])}"
|
||||
)
|
||||
|
||||
# 计算位置
|
||||
cols_per_row = max(1, self.width() // (thumbnail_size + 20))
|
||||
row = index // cols_per_row
|
||||
col = index % cols_per_row
|
||||
|
||||
self.image_layout.addWidget(item_widget, row, col)
|
||||
return description
|
||||
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
"""拖拽进入事件"""
|
||||
if event.mimeData().hasUrls():
|
||||
event.acceptProposedAction()
|
||||
|
||||
def show_context_menu(self, pos, img_path, description):
|
||||
def dropEvent(self, event):
|
||||
"""处理拖放事件"""
|
||||
if not all([self.original_folder_name, self.current_part, self.current_dict_type]):
|
||||
QMessageBox.warning(self, "警告", "请先选择有效的字典节点")
|
||||
return
|
||||
|
||||
urls = event.mimeData().urls()
|
||||
if urls:
|
||||
img_path = urls[0].toLocalFile()
|
||||
if os.path.isfile(img_path):
|
||||
# 生成新键(使用当前最大数字+1)
|
||||
keys = [int(k) for k in self.current_dict_data.keys() if k.isdigit()]
|
||||
max_key = max(keys) if keys else -1
|
||||
new_key = str(max_key + 1)
|
||||
|
||||
# 更新字典数据
|
||||
self.current_dict_data[new_key] = img_path
|
||||
|
||||
# 刷新显示
|
||||
self.refresh_view()
|
||||
|
||||
# 发射信号通知更新 (包含英文字典类型)
|
||||
self.dictionary_updated.emit(
|
||||
self.original_folder_name,
|
||||
self.current_part,
|
||||
self.current_dict_type, # 英文键名
|
||||
self.current_dict_data
|
||||
)
|
||||
|
||||
def show_context_menu(self, pos, img_path, key):
|
||||
"""显示上下文菜单"""
|
||||
menu = QMenu(self)
|
||||
|
||||
# 编辑描述动作
|
||||
edit_action = QAction("编辑描述", self)
|
||||
edit_action.triggered.connect(lambda: self.edit_description(img_path, description))
|
||||
|
||||
# 删除图片动作
|
||||
delete_action = QAction("删除图片", self)
|
||||
delete_action.triggered.connect(lambda: self.delete_image(img_path))
|
||||
delete_action.triggered.connect(lambda: self.delete_image(key))
|
||||
|
||||
# 添加编辑描述动作 (仅当是缺陷图字典时显示)
|
||||
edit_desc_action = None
|
||||
if self.current_dict_type == "defect_picture_dict":
|
||||
edit_desc_action = QAction("编辑缺陷描述", self)
|
||||
edit_desc_action.triggered.connect(lambda: self.edit_defect_description(key, img_path))
|
||||
menu.addAction(edit_desc_action)
|
||||
|
||||
menu.addAction(edit_action)
|
||||
menu.addAction(delete_action)
|
||||
menu.exec_(QCursor.pos())
|
||||
|
||||
def edit_description(self, img_path, old_desc):
|
||||
"""编辑图片描述"""
|
||||
new_desc, ok = QInputDialog.getText(
|
||||
self, "编辑描述", "请输入新的描述:",
|
||||
QLineEdit.Normal, old_desc
|
||||
)
|
||||
|
||||
def edit_defect_description(self, key, img_path):
|
||||
"""编辑缺陷图描述"""
|
||||
# 获取当前描述 (如果没有则使用默认格式)
|
||||
current_desc = self.current_dict_data.get(key, "") if key in self.current_dict_data else ""
|
||||
|
||||
if ok and new_desc and new_desc != old_desc:
|
||||
# 更新字典数据
|
||||
dict_data = self.dictionaries[self.current_dict_type][self.current_blade]
|
||||
if img_path in dict_data.values():
|
||||
# 找到对应的条目并更新
|
||||
for desc, path in dict_data.items():
|
||||
if path == img_path:
|
||||
dict_data.pop(desc)
|
||||
dict_data[new_desc] = img_path
|
||||
break
|
||||
|
||||
# 刷新视图并发射信号
|
||||
self.refresh_dictionary_view()
|
||||
self.dictionary_updated.emit(
|
||||
self.current_dict_type,
|
||||
self.current_blade,
|
||||
dict_data
|
||||
)
|
||||
|
||||
def delete_image(self, img_path):
|
||||
"""删除图片"""
|
||||
# 创建并显示编辑对话框
|
||||
editor = DefectDescriptionEditor(self, current_description=current_desc)
|
||||
if editor.exec_() == QDialog.Accepted:
|
||||
# 获取新描述
|
||||
new_desc = editor.get_description()
|
||||
|
||||
# 更新字典数据 - 删除旧键并添加新键
|
||||
if key in self.current_dict_data:
|
||||
# 保留图片路径,只更新键名
|
||||
img_path = self.current_dict_data.pop(key)
|
||||
self.current_dict_data[new_desc] = img_path
|
||||
|
||||
# 刷新显示
|
||||
self.refresh_view()
|
||||
|
||||
# 发射信号通知更新
|
||||
self.dictionary_updated.emit(
|
||||
self.original_folder_name,
|
||||
self.current_part,
|
||||
self.current_dict_type,
|
||||
self.current_dict_data
|
||||
)
|
||||
|
||||
|
||||
def delete_image(self, key):
|
||||
"""删除指定图片"""
|
||||
reply = QMessageBox.question(
|
||||
self, "确认删除",
|
||||
"确定要从字典中删除这张图片吗?",
|
||||
f"确定要从字典中删除Key为 {key} 的图片吗?",
|
||||
QMessageBox.Yes | QMessageBox.No
|
||||
)
|
||||
|
||||
if reply == QMessageBox.Yes:
|
||||
# 从字典中删除
|
||||
dict_data = self.dictionaries[self.current_dict_type][self.current_blade]
|
||||
for desc, path in list(dict_data.items()):
|
||||
if path == img_path:
|
||||
dict_data.pop(desc)
|
||||
break
|
||||
self.current_dict_data.pop(key, None)
|
||||
|
||||
# 刷新视图并发射信号
|
||||
self.refresh_dictionary_view()
|
||||
# 刷新显示
|
||||
self.refresh_view()
|
||||
|
||||
# 发射信号通知更新
|
||||
self.dictionary_updated.emit(
|
||||
self.current_dict_type,
|
||||
self.current_blade,
|
||||
dict_data
|
||||
self.original_folder_name,
|
||||
self.current_part,
|
||||
self.current_dict_type,
|
||||
self.current_dict_data
|
||||
)
|
||||
|
||||
def add_image_to_dictionary(self, img_path):
|
||||
"""添加图片到当前字典"""
|
||||
if not self.current_dict_type or not self.current_blade:
|
||||
QMessageBox.warning(self, "警告", "请先选择叶片和字典类型")
|
||||
return
|
||||
|
||||
class DefectDescriptionEditor(QDialog):
|
||||
def __init__(self, parent=None, current_description="", img_path=""):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("编辑缺陷图描述")
|
||||
self.setMinimumSize(1000, 700) # 增大窗口尺寸以适应两个编辑器
|
||||
self.setStyleSheet(DEFECT_EDIT_WINDOW_STYLE)
|
||||
|
||||
# 获取或创建当前字典
|
||||
if self.current_blade not in self.dictionaries[self.current_dict_type]:
|
||||
self.dictionaries[self.current_dict_type][self.current_blade] = {}
|
||||
self.current_description = current_description
|
||||
self.img_path = img_path
|
||||
self.init_ui()
|
||||
|
||||
dict_data = self.dictionaries[self.current_dict_type][self.current_blade]
|
||||
def init_ui(self):
|
||||
main_layout = QHBoxLayout(self)
|
||||
main_layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
||||
# 生成默认描述(序号)
|
||||
default_desc = f"图片_{len(dict_data) + 1}"
|
||||
# 左侧 - 图片编辑器 (使用DefectMarkEditor)
|
||||
self.image_editor = DefectMarkEditor(self, self.img_path)
|
||||
self.image_editor.setMinimumSize(600, 600)
|
||||
main_layout.addWidget(self.image_editor, stretch=2)
|
||||
|
||||
# 添加到字典
|
||||
dict_data[default_desc] = img_path
|
||||
# 右侧 - 描述编辑器
|
||||
self.init_description_editor()
|
||||
main_layout.addWidget(self.desc_editor_widget, stretch=1)
|
||||
|
||||
# 刷新视图并发射信号
|
||||
self.refresh_dictionary_view()
|
||||
self.dictionary_updated.emit(
|
||||
self.current_dict_type,
|
||||
self.current_blade,
|
||||
dict_data
|
||||
def init_description_editor(self):
|
||||
self.desc_editor_widget = QWidget()
|
||||
layout = QVBoxLayout(self.desc_editor_widget)
|
||||
layout.setContentsMargins(5, 0, 0, 0)
|
||||
|
||||
# 创建一个简单的 QTextEdit 作为消息浏览器
|
||||
self.message_browser = QTextEdit()
|
||||
self.message_browser.setReadOnly(True)
|
||||
layout.addWidget(self.message_browser)
|
||||
|
||||
# 解析当前描述
|
||||
parts = self.parse_current_description()
|
||||
|
||||
# 1. 缺陷类型
|
||||
type_group = QGroupBox("缺陷类型")
|
||||
type_group.setStyleSheet(GROUP_BOX_STYLE)
|
||||
type_layout = QHBoxLayout(type_group)
|
||||
|
||||
# 使用 SmartDropdown 作为缺陷类型选择器
|
||||
self.defect_type_dropdown = SmartDropdown(
|
||||
dropdown_type="缺陷类型",
|
||||
messagebrowser=self.message_browser,
|
||||
parent=self
|
||||
)
|
||||
if "type" in parts:
|
||||
self.defect_type_dropdown.combo_box.setCurrentText(parts["type"])
|
||||
type_layout.addWidget(self.defect_type_dropdown)
|
||||
|
||||
layout.addWidget(type_group)
|
||||
|
||||
# 2. 缺陷位置
|
||||
location_group = QGroupBox("缺陷位置")
|
||||
location_group.setStyleSheet(GROUP_BOX_STYLE)
|
||||
location_layout = QVBoxLayout(location_group)
|
||||
|
||||
self.location_edit = QLineEdit()
|
||||
self.location_edit.setStyleSheet(LINE_EDIT_STYLE)
|
||||
self.location_edit.setText(parts.get("location", ""))
|
||||
location_layout.addWidget(self.location_edit)
|
||||
|
||||
layout.addWidget(location_group)
|
||||
|
||||
# 3. 缺陷尺寸
|
||||
size_group = QGroupBox("缺陷尺寸")
|
||||
size_group.setStyleSheet(GROUP_BOX_STYLE)
|
||||
size_layout = QVBoxLayout(size_group)
|
||||
|
||||
# 轴向尺寸
|
||||
axial_container = QWidget()
|
||||
axial_container.setStyleSheet(DIMENSION_CONTAINER_STYLE)
|
||||
axial_layout = QHBoxLayout(axial_container)
|
||||
axial_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
axial_label = QLabel("轴向尺寸:")
|
||||
axial_label.setStyleSheet(DIMENSION_LABEL_STYLE)
|
||||
axial_layout.addWidget(axial_label)
|
||||
|
||||
self.axial_edit = QLineEdit()
|
||||
self.axial_edit.setStyleSheet(DIMENSION_EDIT_STYLE)
|
||||
self.axial_edit.setText(parts.get("axial_size", ""))
|
||||
axial_layout.addWidget(self.axial_edit)
|
||||
|
||||
# 添加单位标签
|
||||
axial_unit = QLabel("mm")
|
||||
axial_unit.setStyleSheet(LABEL_STYLE)
|
||||
axial_layout.addWidget(axial_unit)
|
||||
|
||||
axial_layout.addStretch()
|
||||
size_layout.addWidget(axial_container)
|
||||
|
||||
# 弦向尺寸
|
||||
chord_container = QWidget()
|
||||
chord_container.setStyleSheet(DIMENSION_CONTAINER_STYLE)
|
||||
chord_layout = QHBoxLayout(chord_container)
|
||||
chord_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
chord_label = QLabel("弦向尺寸:")
|
||||
chord_label.setStyleSheet(DIMENSION_LABEL_STYLE)
|
||||
chord_layout.addWidget(chord_label)
|
||||
|
||||
self.chord_edit = QLineEdit()
|
||||
self.chord_edit.setStyleSheet(DIMENSION_EDIT_STYLE)
|
||||
self.chord_edit.setText(parts.get("chord_size", ""))
|
||||
chord_layout.addWidget(self.chord_edit)
|
||||
|
||||
# 添加单位标签
|
||||
chord_unit = QLabel("mm")
|
||||
chord_unit.setStyleSheet(LABEL_STYLE)
|
||||
chord_layout.addWidget(chord_unit)
|
||||
|
||||
chord_layout.addStretch()
|
||||
size_layout.addWidget(chord_container)
|
||||
|
||||
layout.addWidget(size_group)
|
||||
|
||||
# 4. 其他属性
|
||||
attr_group = QGroupBox("其他属性")
|
||||
attr_group.setStyleSheet(GROUP_BOX_STYLE)
|
||||
attr_layout = QGridLayout(attr_group)
|
||||
|
||||
# 可见程度
|
||||
visibility_label = QLabel("可见程度:")
|
||||
visibility_label.setStyleSheet(LABEL_STYLE)
|
||||
self.visibility_combo = QComboBox()
|
||||
self.visibility_combo.setStyleSheet(COMBO_BOX_STYLE)
|
||||
self.visibility_combo.addItems(["轻微", "一般", "重要", "严重"])
|
||||
if "visibility" in parts:
|
||||
self.visibility_combo.setCurrentText(parts["visibility"])
|
||||
attr_layout.addWidget(visibility_label, 0, 0)
|
||||
attr_layout.addWidget(self.visibility_combo, 0, 1)
|
||||
|
||||
# 紧急程度
|
||||
urgency_label = QLabel("紧急程度:")
|
||||
urgency_label.setStyleSheet(LABEL_STYLE)
|
||||
self.urgency_combo = QComboBox()
|
||||
self.urgency_combo.setStyleSheet(COMBO_BOX_STYLE)
|
||||
self.urgency_combo.addItems(["不紧急", "一般", "紧急", "非常紧急"])
|
||||
if "urgency" in parts:
|
||||
self.urgency_combo.setCurrentText(parts["urgency"])
|
||||
attr_layout.addWidget(urgency_label, 1, 0)
|
||||
attr_layout.addWidget(self.urgency_combo, 1, 1)
|
||||
|
||||
# 危重等级
|
||||
severity_label = QLabel("危重等级:")
|
||||
severity_label.setStyleSheet(LABEL_STYLE)
|
||||
self.severity_combo = QComboBox()
|
||||
self.severity_combo.setStyleSheet(COMBO_BOX_STYLE)
|
||||
self.severity_combo.addItems(["轻微", "一般", "重要", "严重"])
|
||||
if "severity" in parts:
|
||||
self.severity_combo.setCurrentText(parts["severity"])
|
||||
attr_layout.addWidget(severity_label, 2, 0)
|
||||
attr_layout.addWidget(self.severity_combo, 2, 1)
|
||||
|
||||
layout.addWidget(attr_group)
|
||||
|
||||
# 5. 维修建议
|
||||
suggestion_group = QGroupBox("维修建议")
|
||||
suggestion_group.setStyleSheet(GROUP_BOX_STYLE)
|
||||
suggestion_layout = QVBoxLayout(suggestion_group)
|
||||
|
||||
self.suggestion_edit = QLineEdit()
|
||||
self.suggestion_edit.setStyleSheet(LINE_EDIT_STYLE)
|
||||
self.suggestion_edit.setText(parts.get("suggestion", ""))
|
||||
suggestion_layout.addWidget(self.suggestion_edit)
|
||||
|
||||
layout.addWidget(suggestion_group)
|
||||
|
||||
# 按钮
|
||||
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
button_box.setStyleSheet(PRIMARY_BUTTON_STYLE)
|
||||
button_box.accepted.connect(self.accept)
|
||||
button_box.rejected.connect(self.reject)
|
||||
layout.addWidget(button_box)
|
||||
|
||||
def set_dictionary_data(self, dict_type, blade_name, data):
|
||||
"""设置字典数据"""
|
||||
if dict_type in self.dictionaries:
|
||||
if data:
|
||||
self.dictionaries[dict_type][blade_name] = data
|
||||
elif blade_name in self.dictionaries[dict_type]:
|
||||
del self.dictionaries[dict_type][blade_name]
|
||||
def parse_current_description(self):
|
||||
"""解析当前描述字符串"""
|
||||
parts = {
|
||||
"type": "",
|
||||
"location": "",
|
||||
"axial_size": "",
|
||||
"chord_size": "",
|
||||
"visibility": "一般",
|
||||
"urgency": "一般",
|
||||
"severity": "一般",
|
||||
"suggestion": ""
|
||||
}
|
||||
|
||||
if not self.current_description:
|
||||
return parts
|
||||
|
||||
# 如果当前显示的是这个字典,则刷新视图
|
||||
if self.current_dict_type == dict_type and self.current_blade == blade_name:
|
||||
self.refresh_dictionary_view()
|
||||
# 尝试解析格式: 1_缺陷类型_缺陷位置_缺陷尺寸_可见程度_紧急程度_危重等级_维修建议
|
||||
desc_parts = self.current_description.split("_")
|
||||
if len(desc_parts) >= 8:
|
||||
parts["type"] = desc_parts[1]
|
||||
parts["location"] = desc_parts[2]
|
||||
|
||||
# 解析尺寸 (格式: 轴向XXmm弦向XXmm)
|
||||
size_str = desc_parts[3]
|
||||
axial_match = re.search(r"轴向(\d+)mm", size_str)
|
||||
chord_match = re.search(r"弦向(\d+)mm", size_str)
|
||||
if axial_match:
|
||||
parts["axial_size"] = axial_match.group(1)
|
||||
if chord_match:
|
||||
parts["chord_size"] = chord_match.group(1)
|
||||
|
||||
if len(desc_parts) > 4:
|
||||
parts["visibility"] = desc_parts[4]
|
||||
if len(desc_parts) > 5:
|
||||
parts["urgency"] = desc_parts[5]
|
||||
if len(desc_parts) > 6:
|
||||
parts["severity"] = desc_parts[6]
|
||||
if len(desc_parts) > 7:
|
||||
parts["suggestion"] = "_".join(desc_parts[7:])
|
||||
|
||||
return parts
|
||||
|
||||
def resizeEvent(self, event):
|
||||
"""窗口大小改变时重新计算布局"""
|
||||
super().resizeEvent(event)
|
||||
if hasattr(self, 'current_dict_type') and hasattr(self, 'current_blade'):
|
||||
QTimer.singleShot(100, self.refresh_dictionary_view)
|
||||
def get_description(self):
|
||||
"""生成描述字符串"""
|
||||
# 生成序号 (使用当前时间戳的最后4位确保唯一性)
|
||||
seq = str(int(time.time()) % 10000)
|
||||
|
||||
# 获取各字段值
|
||||
defect_type = self.defect_type_dropdown.combo_box.currentText()
|
||||
location = self.location_edit.text().strip()
|
||||
axial_size = self.axial_edit.text().strip()
|
||||
chord_size = self.chord_edit.text().strip()
|
||||
visibility = self.visibility_combo.currentText()
|
||||
urgency = self.urgency_combo.currentText()
|
||||
severity = self.severity_combo.currentText()
|
||||
suggestion = self.suggestion_edit.text().strip()
|
||||
|
||||
# 构建尺寸字符串
|
||||
size_str = ""
|
||||
if axial_size or chord_size:
|
||||
size_parts = []
|
||||
if axial_size:
|
||||
size_parts.append(f"轴向{axial_size}mm")
|
||||
if chord_size:
|
||||
size_parts.append(f"弦向{chord_size}mm")
|
||||
size_str = "".join(size_parts)
|
||||
|
||||
# 构建完整描述
|
||||
description = f"{seq}_{defect_type}_{location}_{size_str}_{visibility}_{urgency}_{severity}_{suggestion}"
|
||||
return description
|
|
@ -287,4 +287,47 @@ MESSAGE_BOX_BUTTON_STYLE = f"""
|
|||
background-color: #e74c3c;
|
||||
color: {LIGHT_TEXT_COLOR};
|
||||
}}
|
||||
"""
|
||||
"""
|
||||
|
||||
# ====================== 缺陷描述编辑相关样式 ======================
|
||||
# 缺陷描述编辑窗口样式
|
||||
DEFECT_EDIT_WINDOW_STYLE = f"""
|
||||
QWidget {{
|
||||
background-color: {LIGHT_COLOR};
|
||||
}}
|
||||
"""
|
||||
|
||||
# 输入框样式
|
||||
LINE_EDIT_STYLE = f"""
|
||||
QLineEdit {{
|
||||
font-family: "{FONT_FAMILY}";
|
||||
font-size: {CONTENT_FONT_SIZE}pt;
|
||||
min-height: {COMBO_BOX_HEIGHT + 5}px;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid #bdc3c7;
|
||||
border-radius: 4px;
|
||||
}}
|
||||
"""
|
||||
|
||||
# 尺寸输入框样式
|
||||
DIMENSION_EDIT_STYLE = f"""
|
||||
QLineEdit {{
|
||||
{LINE_EDIT_STYLE}
|
||||
min-width: 60px;
|
||||
}}
|
||||
"""
|
||||
|
||||
# 尺寸标签样式
|
||||
DIMENSION_LABEL_STYLE = f"""
|
||||
QLabel {{
|
||||
{LABEL_STYLE}
|
||||
min-width: 60px;
|
||||
}}
|
||||
"""
|
||||
|
||||
# 尺寸控件容器样式
|
||||
DIMENSION_CONTAINER_STYLE = f"""
|
||||
QWidget {{
|
||||
background-color: transparent;
|
||||
}}
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
filelock==3.18.0
|
||||
fsspec==2025.7.0
|
||||
Jinja2==3.1.6
|
||||
lxml==6.0.0
|
||||
MarkupSafe==3.0.2
|
||||
mpmath==1.3.0
|
||||
networkx==3.4.2
|
||||
numpy==2.2.6
|
||||
nvidia-cublas-cu12==12.6.4.1
|
||||
nvidia-cuda-cupti-cu12==12.6.80
|
||||
nvidia-cuda-nvrtc-cu12==12.6.77
|
||||
nvidia-cuda-runtime-cu12==12.6.77
|
||||
nvidia-cudnn-cu12==9.5.1.17
|
||||
nvidia-cufft-cu12==11.3.0.4
|
||||
nvidia-cufile-cu12==1.11.1.6
|
||||
nvidia-curand-cu12==10.3.7.77
|
||||
nvidia-cusolver-cu12==11.7.1.2
|
||||
nvidia-cusparse-cu12==12.5.4.2
|
||||
nvidia-cusparselt-cu12==0.6.3
|
||||
nvidia-nccl-cu12==2.26.2
|
||||
nvidia-nvjitlink-cu12==12.6.85
|
||||
nvidia-nvtx-cu12==12.6.77
|
||||
opencv-python==4.12.0.88
|
||||
piexif==1.1.3
|
||||
pillow==11.3.0
|
||||
PySide6==6.9.1
|
||||
PySide6_Addons==6.9.1
|
||||
PySide6_Essentials==6.9.1
|
||||
python-docx==1.2.0
|
||||
shiboken6==6.9.1
|
||||
sympy==1.14.0
|
||||
torch==2.7.1
|
||||
torchaudio==2.7.1
|
||||
torchvision==0.22.1
|
||||
triton==3.3.1
|
||||
typing_extensions==4.14.1
|
Loading…
Reference in New Issue