1085 lines
43 KiB
Python
1085 lines
43 KiB
Python
from PySide6.QtWidgets import (QGroupBox, QVBoxLayout, QHBoxLayout, QCheckBox,
|
||
QPushButton, QDialog, QLineEdit, QFontComboBox,
|
||
QFileDialog, QMessageBox, QLabel, QWidget,
|
||
QTextBrowser, QApplication, QCompleter, QFrame)
|
||
from PySide6.QtCore import QDateTime,QTimer,QDateTime,Signal,QSettings, QSortFilterProxyModel
|
||
from PySide6.QtGui import QPixmap, QDragEnterEvent, QDropEvent, Qt, QIcon
|
||
import json, sys, os
|
||
|
||
from info_core.defines import *
|
||
|
||
class ConfigComboBoxGroup(QGroupBox):
|
||
"""可复用的配置下拉框组件(带完整样式)"""
|
||
def __init__(self, title, allow_add=True, parent=None, is_project = True):
|
||
super().__init__(title, parent)
|
||
self.allow_add = allow_add
|
||
self.is_project = is_project
|
||
self.config_dir = os.path.join(os.getcwd(), "config", title)
|
||
|
||
# 确保配置目录存在
|
||
os.makedirs(self.config_dir, exist_ok=True)
|
||
|
||
# 设置组框样式
|
||
self.setStyleSheet(GROUP_BOX_STYLE)
|
||
self.setMinimumSize(GROUP_BOX_MIN_WIDTH, GROUP_BOX_MIN_HEIGHT)
|
||
|
||
# 创建UI
|
||
self.init_ui()
|
||
self.load_config_files()
|
||
|
||
def init_ui(self):
|
||
"""初始化UI组件"""
|
||
layout = QVBoxLayout()
|
||
layout.setSpacing(GROUP_BOX_SPACING)
|
||
layout.setContentsMargins(*GROUP_BOX_MARGINS)
|
||
|
||
self.combo_box_init()
|
||
# 按钮布局
|
||
btn_layout = QHBoxLayout()
|
||
btn_layout.addStretch() # 将按钮推到右侧
|
||
|
||
self.modify_btn = self.create_button("修改")
|
||
self.modify_btn.clicked.connect(self.on_modify_clicked)
|
||
btn_layout.addWidget(self.modify_btn)
|
||
|
||
if self.allow_add:
|
||
self.add_btn = self.create_button("添加")
|
||
self.add_btn.clicked.connect(self.on_add_clicked)
|
||
btn_layout.addWidget(self.add_btn)
|
||
|
||
self.delete_btn = self.create_button("删除")
|
||
self.delete_btn.clicked.connect(self.on_delete_clicked)
|
||
btn_layout.addWidget(self.delete_btn)
|
||
|
||
layout.addWidget(self.combo_box)
|
||
layout.addLayout(btn_layout)
|
||
self.setLayout(layout)
|
||
|
||
def combo_box_init(self):
|
||
# 下拉框
|
||
self.combo_box = QFontComboBox()
|
||
self.combo_box.setEditable(True)
|
||
|
||
# 代理模型(支持中文模糊匹配)
|
||
self.proxy_model = QSortFilterProxyModel()
|
||
self.proxy_model.setSourceModel(self.combo_box.model())
|
||
self.proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
|
||
|
||
# 补全器
|
||
self.completer = QCompleter(self.proxy_model, self.combo_box)
|
||
self.completer.setFilterMode(Qt.MatchContains)
|
||
self.combo_box.setCompleter(self.completer)
|
||
|
||
def create_button(self, text):
|
||
"""创建统一风格的按钮"""
|
||
btn = QPushButton(text)
|
||
btn.setStyleSheet(BUTTON_STYLE)
|
||
btn.setFixedSize(BUTTON_WIDTH, BUTTON_HEIGHT)
|
||
return btn
|
||
|
||
def load_config_files(self):
|
||
"""加载config文件夹中的JSON文件"""
|
||
self.combo_box.clear()
|
||
|
||
# 获取所有.json文件
|
||
config_files = [f for f in os.listdir(self.config_dir)
|
||
if f.endswith('.json')]
|
||
|
||
# 添加到下拉框(不带.json后缀)
|
||
for file in config_files:
|
||
self.combo_box.addItem(file[:-5])
|
||
|
||
def on_modify_clicked(self):
|
||
"""修改按钮点击事件"""
|
||
if self.is_project:
|
||
project_widget = AddProjectDialog(self.config_dir, self, os.path.join(self.config_dir, self.combo_box.currentText() + ".json"))
|
||
project_widget.exec()
|
||
else:
|
||
person_widget = AddPersonnelDialog(self.config_dir, self, os.path.join(self.config_dir, self.combo_box.currentText() + ".json"))
|
||
person_widget.exec()
|
||
self.load_config_files()
|
||
|
||
def on_add_clicked(self):
|
||
"""添加按钮点击,创建默认ConfigEditDialog"""
|
||
if self.is_project:
|
||
project_widget = AddProjectDialog(self.config_dir, self)
|
||
project_widget.exec()
|
||
else:
|
||
person_widget = AddPersonnelDialog(self.config_dir, self)
|
||
person_widget.exec()
|
||
self.load_config_files()
|
||
|
||
def on_delete_clicked(self):
|
||
"""删除选中的项目"""
|
||
# 获取当前选中的项目名称
|
||
selected_project_name = self.combo_box.currentText()
|
||
|
||
if selected_project_name:
|
||
# 确认删除操作
|
||
confirm_message = f"你确定要删除项目 '{selected_project_name}' 吗?此操作不可恢复!"
|
||
reply = QMessageBox.question(self, '警告', confirm_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
|
||
|
||
if reply == QMessageBox.Yes:
|
||
# 构建项目文件的路径
|
||
project_file_path = os.path.join(self.config_dir, selected_project_name + ".json")
|
||
|
||
# 检查文件是否存在并删除
|
||
if os.path.exists(project_file_path):
|
||
os.remove(project_file_path)
|
||
else:
|
||
QMessageBox.warning(self, '警告', f"项目文件 '{selected_project_name}.json' 不存在。")
|
||
|
||
# 从UI中移除该项目
|
||
self.combo_box.removeItem(self.combo_box.currentIndex())
|
||
|
||
# 重新加载配置文件列表
|
||
self.load_config_files()
|
||
else:
|
||
QMessageBox.information(self, '信息', '删除操作已取消。')
|
||
else:
|
||
QMessageBox.warning(self, '警告', '没有选中任何项目。')
|
||
|
||
class AddProjectDialog(QDialog):
|
||
def __init__(self, dir, parent=None, path=None, combobox=None):
|
||
super(AddProjectDialog, self).__init__(parent)
|
||
self.setWindowTitle("添加/修改项目信息")
|
||
self.resize(800, 400) # 增加宽度以适应多列布局
|
||
self.dir = dir
|
||
self.path = path
|
||
self.combobox = combobox
|
||
|
||
# 定义所有表单项的配置
|
||
self.form_fields = [
|
||
{"key": "项目名称", "label": "项目名称:", "default": ""},
|
||
{"key": "风场名", "label": "风场名:", "default": ""},
|
||
{"key": "风场地址", "label": "风场地址:", "default": ""},
|
||
{"key": "甲方公司", "label": "甲方公司:", "default": ""},
|
||
{"key": "甲方负责人", "label": "甲方负责人:", "default": ""},
|
||
{"key": "甲方负责人电话", "label": "甲方负责人电话:", "default": ""},
|
||
{"key": "乙方公司", "label": "乙方公司:", "default": "武汉迪特聚能科技有限公司"},
|
||
{"key": "乙方负责人", "label": "乙方负责人:", "default": ""},
|
||
{"key": "乙方负责人电话", "label": "乙方负责人电话:", "default": ""},
|
||
{"key": "项目规格", "label": "项目规格:", "default": ""},
|
||
{"key": "项目工期", "label": "项目工期:", "default": ""},
|
||
]
|
||
|
||
# 加载现有数据(如果有)
|
||
self.data = {}
|
||
if self.path:
|
||
self.load_existing_data()
|
||
|
||
self.init_ui()
|
||
|
||
def load_existing_data(self):
|
||
"""加载现有项目数据"""
|
||
try:
|
||
with open(self.path, 'r', encoding='utf-8') as f:
|
||
self.data = json.load(f)
|
||
except json.JSONDecodeError:
|
||
QMessageBox.critical(self, "错误", "JSON 文件格式错误!")
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "错误", f"加载文件失败: {str(e)}")
|
||
|
||
def init_ui(self):
|
||
"""初始化用户界面"""
|
||
main_layout = QVBoxLayout()
|
||
|
||
# 创建表单布局
|
||
form_widget = QWidget()
|
||
form_layout = QHBoxLayout(form_widget)
|
||
|
||
# 创建两列布局
|
||
column1 = QVBoxLayout()
|
||
column2 = QVBoxLayout()
|
||
|
||
# 将表单字段添加到列中
|
||
self.fields = {}
|
||
for i, field in enumerate(self.form_fields):
|
||
# 创建标签和输入框
|
||
label = QLabel(field["label"])
|
||
edit = QLineEdit()
|
||
|
||
# 设置默认值或从现有数据加载
|
||
if self.path and field["key"] in self.data:
|
||
edit.setText(self.data[field["key"]])
|
||
else:
|
||
edit.setText(field["default"])
|
||
|
||
# 存储输入框引用
|
||
self.fields[field["key"]] = edit
|
||
|
||
# 根据索引决定添加到哪一列
|
||
if i < len(self.form_fields) / 2:
|
||
column1.addWidget(label)
|
||
column1.addWidget(edit)
|
||
else:
|
||
column2.addWidget(label)
|
||
column2.addWidget(edit)
|
||
|
||
# 添加间距和弹性空间
|
||
column1.addStretch()
|
||
column2.addStretch()
|
||
|
||
# 将列添加到表单布局
|
||
form_layout.addLayout(column1)
|
||
form_layout.addSpacing(20) # 列间距
|
||
form_layout.addLayout(column2)
|
||
|
||
# 创建按钮
|
||
self.save_button = QPushButton("保存")
|
||
self.save_button.clicked.connect(self.on_save_button_clicked)
|
||
|
||
# 添加到主布局
|
||
main_layout.addWidget(form_widget)
|
||
main_layout.addWidget(self.save_button)
|
||
|
||
self.setLayout(main_layout)
|
||
|
||
def on_save_button_clicked(self):
|
||
"""保存按钮点击事件"""
|
||
# 收集所有字段数据
|
||
project_info = {
|
||
field["key"]: self.fields[field["key"]].text()
|
||
for field in self.form_fields
|
||
}
|
||
project_info["json路径"] = self.dir
|
||
|
||
# 验证保存目录
|
||
if not os.path.exists(self.dir) or not os.path.isdir(self.dir):
|
||
QMessageBox.warning(self, "警告", "请选择一个有效的保存项目信息的文件夹路径")
|
||
return
|
||
|
||
# 构建默认文件名
|
||
default_filename = f"{project_info['项目名称']}.json"
|
||
|
||
# 让用户选择文件名
|
||
file_path, _ = QFileDialog.getSaveFileName(
|
||
self,
|
||
"保存项目信息",
|
||
os.path.join(self.dir, default_filename),
|
||
"JSON Files (*.json)"
|
||
)
|
||
|
||
if not file_path:
|
||
QMessageBox.warning(self, "警告", "未选择文件名")
|
||
return
|
||
|
||
# 保存项目信息到文件
|
||
try:
|
||
with open(file_path, 'w', encoding='utf-8') as file:
|
||
json.dump(project_info, file, ensure_ascii=False, indent=4)
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "错误", f"保存文件失败: {str(e)}")
|
||
return
|
||
|
||
self.parent().load_config_files()
|
||
|
||
# 提示用户保存成功
|
||
QMessageBox.information(self, "信息", f"项目信息已成功保存到 {file_path}")
|
||
|
||
# 关闭对话框
|
||
self.close()
|
||
|
||
class AddPersonnelDialog(QDialog):
|
||
def __init__(self, dir, parent=None, path=None, combobox=None):
|
||
super(AddPersonnelDialog, self).__init__(parent)
|
||
self.setWindowTitle("添加/修改单次检查信息")
|
||
self.resize(800, 400) # 增加宽度以适应多列布局
|
||
self.dir = dir
|
||
self.path = path
|
||
self.combobox = combobox
|
||
|
||
# 定义所有表单项的配置
|
||
self.form_fields = [
|
||
{"key": "检查人员", "label": "检查人员:", "default": ""},
|
||
{"key": "厂家人员", "label": "厂家人员:", "default": ""},
|
||
{"key": "业主人员", "label": "业主人员:", "default": ""},
|
||
{"key": "数据处理人员", "label": "数据处理人员:", "default": ""},
|
||
{"key": "报告编制人员", "label": "报告编制人员:", "default": ""},
|
||
{"key": "机组型号", "label": "机组型号:", "default": ""},
|
||
{"key": "机组厂家", "label": "机组厂家:", "default": ""},
|
||
{"key": "施工日期", "label": "施工日期", "default": "开始-结束"},
|
||
{"key": "", "label": "", "default": ""},
|
||
#{"key": "", "label": "", "default": ""},
|
||
# 可以轻松添加更多人员字段
|
||
]
|
||
|
||
# 添加复选框字段
|
||
self.checkbox_fields = [
|
||
{"key": "外部检查", "label": "外部检查", "default": False},
|
||
{"key": "内部检查", "label": "内部检查", "default": False},
|
||
{"key": "防雷检查", "label": "防雷检查", "default": False}
|
||
]
|
||
|
||
# 加载现有数据(如果有)
|
||
self.data = {}
|
||
if self.path:
|
||
self.load_existing_data()
|
||
|
||
self.init_ui()
|
||
|
||
def load_existing_data(self):
|
||
"""加载现有人员数据"""
|
||
try:
|
||
with open(self.path, 'r', encoding='utf-8') as f:
|
||
self.data = json.load(f)
|
||
except json.JSONDecodeError:
|
||
QMessageBox.critical(self, "错误", "JSON 文件格式错误!")
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "错误", f"加载文件失败: {str(e)}")
|
||
|
||
def init_ui(self):
|
||
"""初始化用户界面"""
|
||
main_layout = QVBoxLayout()
|
||
|
||
# 创建表单布局
|
||
form_widget = QWidget()
|
||
form_layout = QHBoxLayout(form_widget)
|
||
|
||
# 创建两列布局
|
||
column1 = QVBoxLayout()
|
||
column2 = QVBoxLayout()
|
||
|
||
# 将表单字段添加到列中
|
||
self.fields = {}
|
||
for i, field in enumerate(self.form_fields):
|
||
# 创建标签和输入框
|
||
label = QLabel(field["label"])
|
||
edit = QLineEdit()
|
||
|
||
# 设置默认值或从现有数据加载
|
||
if self.path and field["key"] in self.data:
|
||
edit.setText(self.data[field["key"]])
|
||
else:
|
||
edit.setText(field["default"])
|
||
|
||
# 存储输入框引用
|
||
self.fields[field["key"]] = edit
|
||
|
||
# 根据索引决定添加到哪一列
|
||
if i < len(self.form_fields) / 2:
|
||
column1.addWidget(label)
|
||
column1.addWidget(edit)
|
||
else:
|
||
column2.addWidget(label)
|
||
column2.addWidget(edit)
|
||
|
||
# 添加间距和弹性空间
|
||
column1.addStretch()
|
||
column2.addStretch()
|
||
|
||
# 将列添加到表单布局
|
||
form_layout.addLayout(column1)
|
||
form_layout.addSpacing(20) # 列间距
|
||
form_layout.addLayout(column2)
|
||
|
||
# 创建按钮
|
||
self.save_button = QPushButton("保存")
|
||
self.save_button.clicked.connect(self.on_save_button_clicked)
|
||
|
||
# 添加到主布局
|
||
main_layout.addWidget(form_widget)
|
||
main_layout.addWidget(self.save_button)
|
||
|
||
self.setLayout(main_layout)
|
||
|
||
def on_save_button_clicked(self):
|
||
"""保存按钮点击事件"""
|
||
# 收集所有字段数据
|
||
personnel_info = {
|
||
field["key"]: self.fields[field["key"]].text()
|
||
for field in self.form_fields
|
||
}
|
||
personnel_info["json路径"] = self.dir
|
||
|
||
# 验证保存目录
|
||
if not os.path.exists(self.dir) or not os.path.isdir(self.dir):
|
||
QMessageBox.warning(self, "警告", "请选择一个有效的保存人员信息的文件夹路径")
|
||
return
|
||
|
||
# 构建默认文件名
|
||
default_filename = "人员信息.json"
|
||
|
||
# 让用户选择文件名
|
||
file_path, _ = QFileDialog.getSaveFileName(
|
||
self,
|
||
"保存人员信息",
|
||
os.path.join(self.dir, default_filename),
|
||
"JSON Files (*.json)"
|
||
)
|
||
|
||
if not file_path:
|
||
QMessageBox.warning(self, "警告", "未选择文件名")
|
||
return
|
||
|
||
# 保存人员信息到文件
|
||
try:
|
||
with open(file_path, 'w', encoding='utf-8') as file:
|
||
json.dump(personnel_info, file, ensure_ascii=False, indent=4)
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "错误", f"保存文件失败: {str(e)}")
|
||
return
|
||
|
||
# 更新QComboBox
|
||
if self.combobox is None:
|
||
self.parent().ui.personnel.addItem(os.path.basename(file_path))
|
||
else:
|
||
self.combobox.load_config_files(os.path.basename(self.dir))
|
||
|
||
# 提示用户保存成功
|
||
QMessageBox.information(self, "信息", f"人员信息已成功保存到 {file_path}")
|
||
|
||
# 关闭对话框
|
||
self.close()
|
||
|
||
class SmartDropdown(QWidget):
|
||
"""
|
||
支持动态同步的智能下拉框
|
||
-全局可保存共享选择池
|
||
-可编辑共享池
|
||
存储形式:
|
||
|
||
"""
|
||
shared_pool = {} # 类变量存储共享选项
|
||
|
||
class MyFontComboBox(QFontComboBox):
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
# 获取内部的QLineEdit部件
|
||
self.line_edit = self.lineEdit()
|
||
# 当获得焦点时全选文本
|
||
self.line_edit.selectionChanged.connect(self.select_all_text)
|
||
|
||
def select_all_text(self):
|
||
if self.line_edit.hasFocus():
|
||
self.line_edit.selectAll()
|
||
|
||
@classmethod
|
||
def update_shared_pool(cls, dropdown_type, options):
|
||
"""更新指定类别的共享选项"""
|
||
if not options: # 如果选项为空,添加默认选项
|
||
options = ["未选中"]
|
||
|
||
cls.shared_pool[dropdown_type] = list(options)
|
||
cls.save_shared_pool()
|
||
|
||
# 自动刷新所有该类型的下拉框
|
||
for widget in QApplication.allWidgets():
|
||
if isinstance(widget, SmartDropdown) and widget.dropdown_type == dropdown_type:
|
||
widget._load_options()
|
||
|
||
@classmethod
|
||
def save_shared_pool(cls):
|
||
"""保存共享池到QSettings"""
|
||
settings = QSettings()
|
||
settings.setValue("SmartDropdown/shared_pool", cls.shared_pool)
|
||
|
||
@classmethod
|
||
def load_shared_pool(cls):
|
||
"""从QSettings加载共享池"""
|
||
settings = QSettings()
|
||
pool = settings.value("SmartDropdown/shared_pool", {})
|
||
cls.shared_pool = {k: v for k, v in pool.items()} if pool else {}
|
||
# 确保每个类别至少有一个默认选项
|
||
for dropdown_type in cls.shared_pool:
|
||
if not cls.shared_pool[dropdown_type]:
|
||
cls.shared_pool[dropdown_type] = ["未选中"]
|
||
|
||
def __init__(self, dropdown_type, messagebrowser, label_text=None, parent=None):
|
||
super().__init__(parent)
|
||
self.dropdown_type = dropdown_type
|
||
self.label_text = label_text
|
||
self.messagebrowser = messagebrowser
|
||
|
||
# 初始化共享池
|
||
if not self.shared_pool:
|
||
self.load_shared_pool()
|
||
|
||
# 确保当前类型在共享池中存在
|
||
if self.dropdown_type not in self.shared_pool:
|
||
self.shared_pool[self.dropdown_type] = ["未选中"]
|
||
self.save_shared_pool()
|
||
|
||
self._setup_ui()
|
||
self._load_options()
|
||
self._initialize_defect_type()
|
||
self.messagebrowser.append(f"初始化下拉框: {dropdown_type}")
|
||
|
||
@property
|
||
def current_index(self):
|
||
"""当前选中索引(0表示未选中)"""
|
||
return self.combo_box.currentIndex()
|
||
|
||
@current_index.setter
|
||
def current_index(self, index):
|
||
if self.combo_box.count() > 0:
|
||
clamped_index = max(0, min(index, self.combo_box.count()-1))
|
||
self.combo_box.setCurrentIndex(clamped_index)
|
||
|
||
def _initialize_defect_type(self):
|
||
"""初始化缺陷类型选项,如果选项不足则自动重置"""
|
||
if self.dropdown_type == "缺陷类型":
|
||
# 检查当前选项数量是否不足
|
||
if len(self.shared_pool.get(self.dropdown_type, [])) <= 1:
|
||
default_options = [
|
||
"未选中...", "前缘涂层损伤(风损)", "表面裂纹", "腻子脱落", "漆面脱落",
|
||
"油污","布层破损(深层、不规则面状)", "裂纹(浅层线状)", "开裂(深层线带窄面状)",
|
||
"贯穿损伤", "鼓包", "凹陷", "雷击45d", "雷击zzw", "雷击ck",
|
||
"合模缝开裂", "透光", "雷击碳化", "褶皱", "脱胶", "缺胶",
|
||
"防雷线断裂", "开裂"
|
||
]
|
||
|
||
self.update_shared_pool(self.dropdown_type, default_options)
|
||
self.messagebrowser.append(
|
||
f"初始化提示: 检测到{self.dropdown_type}选项不足,已自动重置为默认选项集"
|
||
f"(原因: 第一次初始化或选项被清空)"
|
||
)
|
||
|
||
def _setup_ui(self):
|
||
"""初始化UI组件"""
|
||
self.combo_box = QFontComboBox()
|
||
self.combo_box.setEditable(True)
|
||
|
||
# 操作按钮
|
||
self.add_btn = QPushButton("+添加输入的类别")
|
||
self.del_btn = QPushButton("-删除选中类别")
|
||
self.refresh_btn = QPushButton()
|
||
self.refresh_btn.setIcon(QIcon.fromTheme("view-refresh")) # 使用系统刷新图标
|
||
self.reset_btn = QPushButton("重置")
|
||
|
||
# 设置按钮大小
|
||
for btn in [self.add_btn, self.del_btn, self.reset_btn]:
|
||
btn.setFixedWidth(150)
|
||
self.refresh_btn.setFixedWidth(30)
|
||
# 设置工具提示
|
||
self.add_btn.setToolTip("添加选项到共享池")
|
||
self.del_btn.setToolTip("从共享池删除当前选项")
|
||
self.refresh_btn.setToolTip("同步共享池选项")
|
||
self.reset_btn.setToolTip("重置为默认选项")
|
||
|
||
# 按钮布局
|
||
btn_layout = QHBoxLayout()
|
||
btn_layout.addWidget(self.add_btn)
|
||
btn_layout.addWidget(self.del_btn)
|
||
btn_layout.addWidget(self.reset_btn)
|
||
btn_layout.addWidget(self.refresh_btn)
|
||
btn_layout.setSpacing(2)
|
||
btn_layout.setContentsMargins(0, 0, 0, 0)
|
||
|
||
# 主布局
|
||
main_layout = QVBoxLayout()
|
||
main_layout.setSpacing(5)
|
||
if self.label_text:
|
||
main_layout.addWidget(QLabel(self.label_text))
|
||
main_layout.addWidget(self.combo_box)
|
||
main_layout.addLayout(btn_layout)
|
||
|
||
self.setLayout(main_layout)
|
||
|
||
# 信号连接
|
||
self.add_btn.clicked.connect(self._add_option)
|
||
self.del_btn.clicked.connect(self._del_option)
|
||
self.refresh_btn.clicked.connect(self._refresh_options)
|
||
self.reset_btn.clicked.connect(self._reset_options)
|
||
self.combo_box.currentIndexChanged.connect(self._update_buttons)
|
||
|
||
def _reset_options(self):
|
||
"""重置选项为默认值"""
|
||
if self.dropdown_type == "缺陷类型":
|
||
default_options = [
|
||
"未选中...", "前缘涂层损伤(风损)", "表面裂纹", "腻子脱落", "漆面脱落",
|
||
"油污","布层破损(深层、不规则面状)", "裂纹(浅层线状)", "开裂(深层线带窄面状)",
|
||
"贯穿损伤", "鼓包", "凹陷", "雷击45d", "雷击zzw", "雷击ck",
|
||
"合模缝开裂", "透光", "雷击碳化", "褶皱", "脱胶", "缺胶",
|
||
"防雷线断裂", "开裂"
|
||
]
|
||
|
||
reply = QMessageBox.question(
|
||
self, '确认重置',
|
||
'确定要重置缺陷类型为默认选项吗?此操作不可撤销!',
|
||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||
QMessageBox.StandardButton.No)
|
||
|
||
if reply == QMessageBox.StandardButton.Yes:
|
||
self.update_shared_pool(self.dropdown_type, default_options)
|
||
self.messagebrowser.append(f"已重置 {self.dropdown_type} 为默认选项")
|
||
else:
|
||
QMessageBox.information(
|
||
self, '提示',
|
||
f'当前类型 {self.dropdown_type} 没有预定义的重置选项')
|
||
|
||
def _load_options(self):
|
||
"""加载选项(确保只有一个未选中选项)"""
|
||
current_text = self.combo_box.currentText() if self.combo_box.count() > 0 else ""
|
||
|
||
self.combo_box.blockSignals(True) # 防止触发信号
|
||
self.combo_box.clear()
|
||
|
||
# 确保共享池中有当前类型的选项
|
||
if (self.dropdown_type not in self.shared_pool or
|
||
not self.shared_pool[self.dropdown_type]):
|
||
self.shared_pool[self.dropdown_type] = ["未选中"]
|
||
self.save_shared_pool()
|
||
|
||
# 直接从共享池加载选项
|
||
self.combo_box.addItems(self.shared_pool[self.dropdown_type])
|
||
|
||
# 恢复之前的选中项(如果有)
|
||
if current_text:
|
||
idx = self.combo_box.findText(current_text)
|
||
if idx >= 0:
|
||
self.combo_box.setCurrentIndex(idx)
|
||
|
||
self.combo_box.blockSignals(False)
|
||
self._update_buttons()
|
||
|
||
self.messagebrowser.append(f"加载选项: {self.dropdown_type} - {self.shared_pool[self.dropdown_type]}")
|
||
|
||
def _refresh_options(self):
|
||
"""手动刷新选项"""
|
||
self.messagebrowser.append(f"手动刷新选项: {self.dropdown_type}")
|
||
self._load_options()
|
||
|
||
def _update_buttons(self):
|
||
"""根据当前状态更新按钮可用性"""
|
||
has_options = len(self.shared_pool.get(self.dropdown_type, [])) > 0
|
||
has_selection = self.current_index >= 0
|
||
|
||
self.del_btn.setEnabled(has_options and has_selection)
|
||
self.add_btn.setEnabled(True)
|
||
self.refresh_btn.setEnabled(True)
|
||
self.reset_btn.setEnabled(True)
|
||
|
||
def _add_option(self):
|
||
"""添加新选项到共享池"""
|
||
new_option = self.combo_box.currentText().strip()
|
||
if not new_option:
|
||
return
|
||
|
||
if new_option not in self.shared_pool[self.dropdown_type]:
|
||
if QMessageBox.Yes == QMessageBox.question(
|
||
self, "确认添加",
|
||
f"添加 '{new_option}' 到 '{self.dropdown_type}' 选项池?",
|
||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No):
|
||
|
||
if self.dropdown_type not in self.shared_pool:
|
||
self.shared_pool[self.dropdown_type] = ["未选中"]
|
||
|
||
self.shared_pool[self.dropdown_type].append(new_option)
|
||
self.messagebrowser.append(f"添加选项: {new_option} 到 {self.dropdown_type}")
|
||
self.update_shared_pool(self.dropdown_type, self.shared_pool[self.dropdown_type])
|
||
self.combo_box.setCurrentIndex(self.combo_box.findText(new_option))
|
||
else:
|
||
self.messagebrowser.append(f"添加失败: 选项 {new_option} 已存在于 {self.dropdown_type} 选项池")
|
||
|
||
def _del_option(self):
|
||
"""从共享池删除当前选项"""
|
||
if self.current_index < 0: # 无效选择
|
||
return
|
||
|
||
current_text = self.combo_box.currentText()
|
||
|
||
if current_text == "未选中":
|
||
self.messagebrowser.append("不能删除默认的'未选中'选项")
|
||
return
|
||
|
||
if QMessageBox.Yes == QMessageBox.question(
|
||
self, "确认删除",
|
||
f"从 '{self.dropdown_type}' 删除 '{current_text}'?",
|
||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No):
|
||
|
||
try:
|
||
self.shared_pool[self.dropdown_type].remove(current_text)
|
||
|
||
# 如果删除后选项为空,恢复默认选项
|
||
if not self.shared_pool[self.dropdown_type]:
|
||
self.shared_pool[self.dropdown_type] = ["未选中"]
|
||
|
||
self.messagebrowser.append(f"删除选项: {current_text} 从 {self.dropdown_type}")
|
||
self.update_shared_pool(self.dropdown_type, self.shared_pool[self.dropdown_type])
|
||
except ValueError:
|
||
self.messagebrowser.append(f"删除失败: 选项 {current_text} 不存在")
|
||
|
||
class FolderImportWidget(QGroupBox):
|
||
"""支持双路径独立导入的组件"""
|
||
main_folder_selected = Signal(str) # 仅总图片路径变化时发射
|
||
selected_folder_selected = Signal(str) # 选中路径变化时发射
|
||
get_baogao_choose_info = Signal(str) # 获取报告选择
|
||
|
||
def __init__(self, title: str = "图片文件夹导入", parent=None):
|
||
super().__init__(title, parent)
|
||
self.main_folder = None
|
||
self.selected_folder = None
|
||
self.setup_ui()
|
||
self.apply_styles()
|
||
|
||
def setup_ui(self):
|
||
"""设置UI布局"""
|
||
self.setMinimumSize(GROUP_BOX_MIN_WIDTH, GROUP_BOX_MIN_HEIGHT)
|
||
|
||
main_layout = QVBoxLayout()
|
||
main_layout.setSpacing(GROUP_BOX_SPACING)
|
||
main_layout.setContentsMargins(*GROUP_BOX_MARGINS)
|
||
|
||
# ================= 总图片路径区域 =================
|
||
self.main_group = DropGroupBox("总图片路径(自动解析数量,生成略缩图)")
|
||
main_group_layout = QVBoxLayout()
|
||
|
||
# 拖拽区域
|
||
self.main_drag_label = QLabel("拖拽文件夹到此处或点击按钮选择")
|
||
self.main_drag_label.setProperty("class", "waiting-label")
|
||
|
||
# 路径显示
|
||
self.main_path_label = QLabel("未选择")
|
||
self.main_path_label.setProperty("class", "info-display")
|
||
|
||
# 选择按钮
|
||
self.main_select_btn = QPushButton("选择总图片文件夹")
|
||
self.main_select_btn.setProperty("class", "normal-button")
|
||
|
||
main_group_layout.addWidget(self.main_drag_label)
|
||
main_group_layout.addWidget(self.main_path_label)
|
||
main_group_layout.addWidget(self.main_select_btn, 0, Qt.AlignmentFlag.AlignHCenter)
|
||
self.main_group.setLayout(main_group_layout)
|
||
self.main_group.setAcceptDrops(True)
|
||
|
||
# ================= 选中图片路径区域 =================
|
||
self.selected_group = DropGroupBox("选中图片路径(获取典型部位图片,自动读取图片名做描述)")
|
||
selected_group_layout = QVBoxLayout()
|
||
|
||
# 拖拽区域
|
||
self.selected_drag_label = QLabel("拖拽文件夹到此处或点击按钮选择")
|
||
self.selected_drag_label.setProperty("class", "waiting-label")
|
||
|
||
# 路径显示
|
||
self.selected_path_label = QLabel("未选择")
|
||
self.selected_path_label.setProperty("class", "info-display")
|
||
|
||
# 选择按钮
|
||
self.selected_select_btn = QPushButton("选择图片文件夹")
|
||
self.selected_select_btn.setProperty("class", "normal-button")
|
||
|
||
self.modify_layout = QHBoxLayout()
|
||
self.modify_picture_quexian_btn = QPushButton("修改图片缺陷信息")
|
||
self.modify_picture_quexian_btn.setProperty("class", "normal-button")
|
||
self.modify_picture_quexian_btn.setEnabled(False)
|
||
self.modify_picture_quexian_btn.clicked.connect(self.on_modify_picture_quexian_btn_clicked)
|
||
self.modify_picture_huizong_btn = QPushButton("修改图片汇总信息")
|
||
self.modify_picture_huizong_btn.setProperty("class", "normal-button")
|
||
self.modify_picture_huizong_btn.setEnabled(False)
|
||
self.modify_picture_huizong_btn.clicked.connect(self.on_modify_picture_huizong_btn_clicked)
|
||
self.modify_group = QGroupBox("修改图片信息")
|
||
self.modify_layout.addWidget(self.modify_picture_quexian_btn, 0, Qt.AlignmentFlag.AlignHCenter)
|
||
self.modify_layout.addWidget(self.modify_picture_huizong_btn, 0, Qt.AlignmentFlag.AlignHCenter)
|
||
self.modify_layout.addStretch(1)
|
||
self.modify_group.setLayout(self.modify_layout)
|
||
|
||
|
||
selected_group_layout.addWidget(self.selected_drag_label)
|
||
selected_group_layout.addWidget(self.selected_path_label)
|
||
selected_group_layout.addWidget(self.selected_select_btn, 0, Qt.AlignmentFlag.AlignHCenter)
|
||
selected_group_layout.addWidget(self.modify_group)
|
||
self.selected_group.setLayout(selected_group_layout)
|
||
self.selected_group.setAcceptDrops(True)
|
||
|
||
# ================= 添加到主布局 =================
|
||
main_layout.addWidget(self.main_group)
|
||
main_layout.addWidget(self.selected_group)
|
||
self.setLayout(main_layout)
|
||
|
||
# 连接信号
|
||
self.main_select_btn.clicked.connect(lambda: self.select_folder("main"))
|
||
self.selected_select_btn.clicked.connect(lambda: self.select_folder("selected"))
|
||
|
||
self.search_file_list = []
|
||
|
||
def apply_styles(self):
|
||
"""应用样式"""
|
||
self.setStyleSheet(f"""
|
||
{GROUP_BOX_STYLE}
|
||
/* 内部GroupBox样式 */
|
||
QGroupBox QGroupBox {{
|
||
font-size: {TITLE_FONT_SIZE - 1}pt;
|
||
border: 1px solid #ced4da;
|
||
margin-top: 16px;
|
||
}}
|
||
QLabel[class="waiting-label"] {{
|
||
{WAITING_LABEL_STYLE}
|
||
font-size: {CONTENT_FONT_SIZE}pt;
|
||
}}
|
||
QLabel[class="info-display"] {{
|
||
{INFO_DISPLAY_STYLE}
|
||
min-height: 60px;
|
||
}}
|
||
QPushButton[class="normal-button"] {{
|
||
{BUTTON_STYLE}
|
||
min-width: 160px;
|
||
}}
|
||
""")
|
||
|
||
def select_folder(self, folder_type: str):
|
||
"""选择文件夹"""
|
||
dialog_title = "选择总图片文件夹" if folder_type == "main" else "选择图片文件夹"
|
||
folder = QFileDialog.getExistingDirectory(
|
||
self,
|
||
dialog_title,
|
||
os.path.expanduser("~"),
|
||
QFileDialog.Option.ShowDirsOnly | QFileDialog.Option.DontResolveSymlinks
|
||
)
|
||
if folder:
|
||
self.set_folder_path(folder, folder_type)
|
||
|
||
def set_folder_path(self, folder_path: str, folder_type: str):
|
||
"""设置文件夹路径并更新UI"""
|
||
if folder_type == "main":
|
||
self.main_folder = folder_path
|
||
self.main_path_label.setText(folder_path)
|
||
self.main_drag_label.hide()
|
||
self.main_folder_selected.emit(folder_path) # 触发解析
|
||
else:
|
||
self.selected_folder = folder_path
|
||
self.selected_path_label.setText(folder_path)
|
||
self.selected_drag_label.hide()
|
||
self.modify_picture_quexian_btn.setEnabled(True)
|
||
self.modify_picture_huizong_btn.setEnabled(True)
|
||
self.selected_folder_selected.emit(folder_path)
|
||
|
||
def dragEnterEvent(self, event: QDragEnterEvent):
|
||
"""总区域的拖拽事件(转发到对应子区域)"""
|
||
source = event.source()
|
||
if isinstance(source, QGroupBox):
|
||
# 如果是来自子区域的拖拽,交给子区域处理
|
||
return
|
||
|
||
# 否则检查是否是有效的文件夹拖拽
|
||
if event.mimeData().hasUrls():
|
||
urls = event.mimeData().urls()
|
||
if len(urls) == 1 and urls[0].isLocalFile():
|
||
file_info = urls[0].toLocalFile()
|
||
if os.path.isdir(file_info):
|
||
event.acceptProposedAction()
|
||
|
||
def dropEvent(self, event: QDropEvent):
|
||
"""总区域的放下事件(自动分配到第一个区域)"""
|
||
urls = event.mimeData().urls()
|
||
if urls and urls[0].isLocalFile():
|
||
folder_path = urls[0].toLocalFile()
|
||
if os.path.isdir(folder_path):
|
||
self.set_folder_path(folder_path, "main")
|
||
|
||
def get_paths(self) -> tuple:
|
||
"""获取两个路径(总路径,选中路径)"""
|
||
return self.main_folder, self.selected_folder
|
||
|
||
def get_baogao_choose(self):
|
||
self.get_baogao_choose_info.emit()
|
||
|
||
def update_baogao_choose_info(self, info: list[str]):
|
||
self.search_file_list = info
|
||
|
||
def on_modify_picture_quexian_btn_clicked(self):
|
||
"""修改图片缺陷类型"""
|
||
# self.get_baogao_choose()
|
||
# if self.search_file_list:
|
||
# Y1_num, Y1 =
|
||
|
||
def on_modify_picture_huizong_btn_clicked(self):
|
||
"""修改图片汇总信息"""
|
||
self.get_baogao_choose()
|
||
|
||
|
||
# 子区域GroupBox需要单独处理拖拽事件
|
||
class DropGroupBox(QGroupBox):
|
||
"""支持拖拽的子区域GroupBox"""
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.setAcceptDrops(True)
|
||
|
||
def dragEnterEvent(self, event: QDragEnterEvent):
|
||
if event.mimeData().hasUrls():
|
||
urls = event.mimeData().urls()
|
||
if len(urls) == 1 and urls[0].isLocalFile():
|
||
file_info = urls[0].toLocalFile()
|
||
if os.path.isdir(file_info):
|
||
event.acceptProposedAction()
|
||
return
|
||
event.ignore()
|
||
|
||
def dropEvent(self, event: QDropEvent):
|
||
urls = event.mimeData().urls()
|
||
if urls and urls[0].isLocalFile():
|
||
folder_path = urls[0].toLocalFile()
|
||
if os.path.isdir(folder_path):
|
||
# 通过parent()调用主组件的方法
|
||
parent = self.parent()
|
||
while parent and not isinstance(parent, FolderImportWidget):
|
||
parent = parent.parent()
|
||
|
||
if parent:
|
||
folder_type = "main" if self == parent.main_group else "selected"
|
||
parent.set_folder_path(folder_path, folder_type)
|
||
|
||
class ImageAnalysisWidget(QGroupBox):
|
||
"""完全使用宏定义样式的图片解析组件"""
|
||
generate_path_selected = Signal(str)
|
||
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.setTitle("图片解析")
|
||
self.image_folder_path = None
|
||
self.generate_path = None
|
||
self.setup_ui()
|
||
self.apply_styles()
|
||
|
||
def setup_ui(self):
|
||
"""设置UI布局"""
|
||
self.setMinimumSize(GROUP_BOX_MIN_WIDTH, GROUP_BOX_MIN_HEIGHT)
|
||
|
||
main_layout = QVBoxLayout()
|
||
main_layout.setSpacing(GROUP_BOX_SPACING)
|
||
main_layout.setContentsMargins(*GROUP_BOX_MARGINS)
|
||
|
||
# 初始状态 - 等待导入
|
||
self.waiting_label = QLabel("请先导入图片文件夹以开始解析")
|
||
self.waiting_label.setProperty("class", "waiting-label")
|
||
|
||
# 解析结果区域
|
||
self.result_frame = QFrame()
|
||
result_layout = QVBoxLayout()
|
||
result_layout.setSpacing(15)
|
||
|
||
# 图片文件夹信息
|
||
folder_info_layout = QHBoxLayout()
|
||
folder_label = QLabel("图片文件夹:")
|
||
folder_label.setProperty("class", "info-label")
|
||
self.folder_path_label = QLabel("未选择")
|
||
self.folder_path_label.setProperty("class", "info-display")
|
||
folder_info_layout.addWidget(folder_label)
|
||
folder_info_layout.addWidget(self.folder_path_label)
|
||
|
||
# 图片数量信息
|
||
count_info_layout = QHBoxLayout()
|
||
count_label = QLabel("图片数量:")
|
||
count_label.setProperty("class", "info-label")
|
||
self.image_count_label = QLabel("-")
|
||
self.image_count_label.setProperty("class", "info-display")
|
||
count_info_layout.addWidget(count_label)
|
||
count_info_layout.addWidget(self.image_count_label)
|
||
|
||
# 分隔线
|
||
separator = QFrame()
|
||
separator.setFrameShape(QFrame.Shape.HLine)
|
||
separator.setProperty("class", "separator")
|
||
|
||
# 生成路径区域
|
||
path_info_layout = QHBoxLayout()
|
||
path_label = QLabel("生成路径:")
|
||
path_label.setProperty("class", "info-label")
|
||
self.generate_path_label = QLabel("未选择")
|
||
self.generate_path_label.setProperty("class", "info-display")
|
||
path_info_layout.addWidget(path_label)
|
||
path_info_layout.addWidget(self.generate_path_label)
|
||
|
||
# 选择按钮
|
||
self.select_path_button = QPushButton("选择生成路径")
|
||
self.select_path_button.setProperty("class", "normal-button")
|
||
self.select_path_button.clicked.connect(self.select_generate_path)
|
||
self.select_path_button.setEnabled(False)
|
||
|
||
# 生成报告选项
|
||
check_box_layout = QHBoxLayout()
|
||
self.check_is_waibu = QCheckBox("外部")
|
||
self.check_is_neibu = QCheckBox("内部")
|
||
self.check_is_fanglei = QCheckBox("防雷")
|
||
self.check_is_neibu.setStyleSheet(CHECKBOX_STYLE)
|
||
self.check_is_waibu.setStyleSheet(CHECKBOX_STYLE)
|
||
self.check_is_fanglei.setStyleSheet(CHECKBOX_STYLE)
|
||
check_box_layout.addWidget(self.check_is_waibu)
|
||
check_box_layout.addWidget(self.check_is_neibu)
|
||
check_box_layout.addWidget(self.check_is_fanglei)
|
||
|
||
# 添加到结果布局
|
||
result_layout.addLayout(folder_info_layout)
|
||
result_layout.addLayout(count_info_layout)
|
||
result_layout.addWidget(separator)
|
||
result_layout.addLayout(path_info_layout)
|
||
result_layout.addWidget(self.select_path_button, 0, Qt.AlignmentFlag.AlignHCenter)
|
||
result_layout.addLayout(check_box_layout)
|
||
result_layout.addStretch()
|
||
|
||
self.result_frame.setLayout(result_layout)
|
||
self.result_frame.hide()
|
||
|
||
# 添加到主布局
|
||
main_layout.addWidget(self.waiting_label)
|
||
main_layout.addWidget(self.result_frame)
|
||
self.setLayout(main_layout)
|
||
|
||
def apply_styles(self):
|
||
"""完全使用宏定义应用样式"""
|
||
self.setStyleSheet(GROUP_BOX_STYLE)
|
||
|
||
# 通过QSS类选择器应用样式
|
||
self.setStyleSheet(f"""
|
||
{self.styleSheet()}
|
||
/* 等待提示 */
|
||
QLabel[class="waiting-label"] {{
|
||
{WAITING_LABEL_STYLE}
|
||
}}
|
||
|
||
/* 信息标签 */
|
||
QLabel[class="info-label"] {{
|
||
{INFO_LABEL_STYLE}
|
||
}}
|
||
|
||
/* 信息显示 */
|
||
QLabel[class="info-display"] {{
|
||
{INFO_DISPLAY_STYLE}
|
||
}}
|
||
|
||
/* 分隔线 */
|
||
QFrame[class="separator"] {{
|
||
{SEPARATOR_STYLE}
|
||
}}
|
||
|
||
/* 普通按钮 */
|
||
QPushButton[class="normal-button"] {{
|
||
{BUTTON_STYLE}
|
||
}}
|
||
""")
|
||
|
||
def set_image_folder(self, folder_path: str):
|
||
"""设置图片文件夹路径并开始解析"""
|
||
self.image_folder_path = folder_path
|
||
self.folder_path_label.setText(folder_path)
|
||
self.start_analysis()
|
||
|
||
def start_analysis(self):
|
||
"""开始解析"""
|
||
self.total_picture_count = 0
|
||
for root, dirs, files in os.walk(self.image_folder_path):
|
||
for file in files:
|
||
# 检查文件扩展名是否为图片格式
|
||
if file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff')):
|
||
self.total_picture_count += 1
|
||
self.waiting_label.hide()
|
||
self.image_count_label.setText(str(self.total_picture_count)) # 模拟解析结果
|
||
self.select_path_button.setEnabled(True)
|
||
self.result_frame.show()
|
||
|
||
def select_generate_path(self):
|
||
"""选择生成路径"""
|
||
path = QFileDialog.getExistingDirectory(
|
||
self,
|
||
"选择生成路径",
|
||
os.path.expanduser("~") or self.image_folder_path,
|
||
QFileDialog.Option.ShowDirsOnly | QFileDialog.Option.DontResolveSymlinks
|
||
)
|
||
if path:
|
||
self.set_generate_path(path)
|
||
|
||
def set_generate_path(self, path: str):
|
||
"""设置并显示生成路径"""
|
||
self.generate_path = path
|
||
self.generate_path_label.setText(path)
|
||
self.generate_path_selected.emit(path) |