实现树状编辑
This commit is contained in:
parent
832c91454e
commit
f8f7ad1e8e
|
@ -204,3 +204,4 @@ __marimo__/
|
|||
|
||||
测试数据/
|
||||
model/
|
||||
output/
|
|
@ -1,12 +1,12 @@
|
|||
from PySide6.QtWidgets import (QMainWindow, QWidget, QGridLayout,
|
||||
from PySide6.QtWidgets import (QMainWindow, QWidget, QGridLayout, QMessageBox,
|
||||
QPushButton, QSizePolicy, QSplitter, QToolBar)
|
||||
from PySide6.QtGui import QFontDatabase
|
||||
from PySide6.QtCore import Signal, Qt
|
||||
|
||||
import os
|
||||
from info_core.defines import *
|
||||
from info_core.MyQtClass import ConfigComboBoxGroup, FolderDropWidget
|
||||
|
||||
from info_core.MyQtClass import (ConfigComboBoxGroup, FolderDropWidget,
|
||||
OutputDirSelector, FolderBrowser)
|
||||
|
||||
class ReportGeneratorUI(QMainWindow):
|
||||
send_baogao_choose_info = Signal(list[str])
|
||||
|
@ -49,8 +49,6 @@ class ReportGeneratorUI(QMainWindow):
|
|||
self.staff_group = ConfigComboBoxGroup("单次检查配置信息", is_project=False)
|
||||
# 第二行:导入图片路径、填写机组信息
|
||||
self.picture_group = FolderDropWidget()
|
||||
# self.image_analysis =
|
||||
# self.main_layout.addWidget(self.image_analysis, 1, 1)
|
||||
# 第三行:生成报告按钮(跨两列)
|
||||
self.fill_turbine_info_button()
|
||||
self.fill_btn.clicked.connect(self.on_fill_clicked)
|
||||
|
@ -79,15 +77,29 @@ class ReportGeneratorUI(QMainWindow):
|
|||
# 设置分割器初始比例
|
||||
self.splitter.setStretchFactor(0, 1)
|
||||
self.splitter.setStretchFactor(1, 4)
|
||||
|
||||
|
||||
self.init_toolbar()
|
||||
self.create_warning_box()
|
||||
self.create_info_fill_widget()
|
||||
|
||||
def init_toolbar(self):
|
||||
self.toolbar = QToolBar()
|
||||
self.addToolBar(self.toolbar)
|
||||
self.toolbar.setMovable(False)
|
||||
self.toolbar.setFloatable(False)
|
||||
new_action = self.toolbar.addAction("重置布局比例")
|
||||
|
||||
self.output_dir_selector = OutputDirSelector()
|
||||
if not os.path.exists(os.getcwd() + "/output"):
|
||||
os.mkdir(os.getcwd() + "/output")
|
||||
self.output_dir_selector.set_output_dir(os.getcwd() + "/output")
|
||||
output_dir_choose_action = self.toolbar.addAction("选择输出目录")
|
||||
|
||||
self.toolbar.addSeparator()
|
||||
|
||||
new_action.triggered.connect(self.reset_splitter)
|
||||
|
||||
output_dir_choose_action.triggered.connect(self.choose_output_dir)
|
||||
|
||||
def reset_splitter(self):
|
||||
"""重置分割器的比例"""
|
||||
total_size = sum(self.splitter.sizes()) # 获取当前总大小
|
||||
|
@ -96,12 +108,38 @@ class ReportGeneratorUI(QMainWindow):
|
|||
int(total_size * 0.8) # 第二部分占 80%
|
||||
])
|
||||
|
||||
def choose_output_dir(self):
|
||||
"""选择输出目录"""
|
||||
self.output_dir_selector.show()
|
||||
|
||||
def create_info_fill_widget(self):
|
||||
self.info_fill_widget = FolderBrowser()
|
||||
|
||||
def create_warning_box(self):
|
||||
self.warning_box = QMessageBox(self)
|
||||
self.warning_box.setWindowTitle("警告")
|
||||
self.warning_box.setIcon(QMessageBox.Warning)
|
||||
self.warning_box.setStyleSheet(WARNING_MESSAGE_BOX_STYLE + MESSAGE_BOX_BUTTON_STYLE)
|
||||
|
||||
def on_fill_clicked(self):
|
||||
"""填写信息"""
|
||||
# 读取各个配置信息
|
||||
turbine_file_list = self.picture_group.get_selected_folders()
|
||||
print(turbine_file_list)
|
||||
|
||||
if len(self.picture_group.get_selected_folders()) <= 0:
|
||||
self.warning_box.setText("请先选择机组目录")
|
||||
self.warning_box.exec()
|
||||
return
|
||||
if self.project_group.get_current_config_file() is None:
|
||||
self.warning_box.setText("请先选择/添加项目配置信息")
|
||||
self.warning_box.exec()
|
||||
return
|
||||
if self.staff_group.get_current_config_file() is None:
|
||||
self.warning_box.setText("请先选择/添加人员配置信息")
|
||||
self.warning_box.exec()
|
||||
return
|
||||
|
||||
self.info_fill_widget.set_output_path(self.get_output_path())
|
||||
self.info_fill_widget.set_initial_folders(self.get_choosen_files())
|
||||
self.info_fill_widget.show()
|
||||
# search_file_list = []
|
||||
# if self.image_analysis.check_is_waibu:
|
||||
# search_file_list.append("外汇总")
|
||||
|
@ -111,6 +149,12 @@ class ReportGeneratorUI(QMainWindow):
|
|||
# search_file_list.append("防汇总")
|
||||
# self.send_baogao_choose_info.emit(search_file_list)
|
||||
|
||||
def get_choosen_files(self):
|
||||
return self.picture_group.get_selected_folders()
|
||||
|
||||
def get_output_path(self):
|
||||
return self.output_dir_selector.get_output_dir()
|
||||
|
||||
def create_button(self, text):
|
||||
"""创建统一风格的按钮"""
|
||||
btn = QPushButton(text)
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"检查人员": "213",
|
||||
"厂家人员": "213",
|
||||
"业主人员": "213",
|
||||
"数据处理人员": "213",
|
||||
"报告编制人员": "123",
|
||||
"机组型号": "123",
|
||||
"机组厂家": "213",
|
||||
"施工日期": "开始-结束",
|
||||
"": "123",
|
||||
"外部检查": true,
|
||||
"内部检查": true,
|
||||
"防雷检查": true,
|
||||
"json路径": "/home/dtyx/桌面/yhh/ReportGenerator/config/单次检查配置信息"
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"检查人员": "张三",
|
||||
"厂家人员": "李四",
|
||||
"业主人员": "王五",
|
||||
"数据处理人员": "六麻子",
|
||||
"报告编制人员": "赵六",
|
||||
"机组型号": "model1",
|
||||
"机组厂家": "某厂",
|
||||
"叶片型号": "model--1",
|
||||
"叶片厂家": "某场",
|
||||
"施工日期": "2025/4/3-2025/4/30",
|
||||
"外部检查": true,
|
||||
"内部检查": true,
|
||||
"防雷检查": true,
|
||||
"json路径": "/home/dtyx/桌面/yhh/ReportGenerator/config/单次检查配置信息"
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"项目名称": "123",
|
||||
"风场名": "123",
|
||||
"风场地址": "123",
|
||||
"甲方公司": "123",
|
||||
"甲方负责人": "123",
|
||||
"甲方负责人电话": "123",
|
||||
"项目名称": "某集团某场外部内部防雷检测项目",
|
||||
"风场名": "某风场",
|
||||
"风场地址": "某地",
|
||||
"甲方公司": "甲",
|
||||
"甲方负责人": "甲负责人",
|
||||
"甲方负责人电话": "181xxxxxxxxx",
|
||||
"乙方公司": "武汉迪特聚能科技有限公司",
|
||||
"乙方负责人": "123",
|
||||
"乙方负责人电话": "123",
|
||||
"项目规格": "123",
|
||||
"项目工期": "123",
|
||||
"乙方负责人": "乙负责人",
|
||||
"乙方负责人电话": "181xxxxxxxx",
|
||||
"项目规格": "20台",
|
||||
"项目工期": "30天",
|
||||
"json路径": "/home/dtyx/桌面/yhh/ReportGenerator/config/项目基本信息"
|
||||
}
|
|
@ -3,11 +3,13 @@ from PySide6.QtWidgets import (QGroupBox, QVBoxLayout, QHBoxLayout, QCheckBox,
|
|||
QFileDialog, QMessageBox, QLabel, QWidget,
|
||||
QTextBrowser, QApplication, QCompleter, QFrame,
|
||||
QListWidgetItem, QListWidget, QAbstractItemView,
|
||||
QStackedWidget, QStackedLayout)
|
||||
QStackedWidget, QStackedLayout, QStyledItemDelegate,
|
||||
QTreeView, QSplitter, QFileSystemModel)
|
||||
from PySide6.QtCore import (QDateTime,QTimer,QDateTime,Signal,QSettings,
|
||||
QSortFilterProxyModel, QSize)
|
||||
QSortFilterProxyModel, QSize, QDir)
|
||||
from PySide6.QtGui import (QPixmap, QDragEnterEvent, QDropEvent, Qt, QIcon,
|
||||
QFontMetrics)
|
||||
QFontMetrics, QStandardItem, QStandardItemModel,
|
||||
QAction)
|
||||
import json, sys, os
|
||||
|
||||
from info_core.defines import *
|
||||
|
@ -144,7 +146,8 @@ class ConfigComboBoxGroup(QGroupBox):
|
|||
QMessageBox.warning(self, '警告', '没有选中任何项目。')
|
||||
def get_current_config_file(self):
|
||||
"""获取当前选中的配置文件"""
|
||||
return os.path.join(self.config_dir, self.combo_box.currentText() + ".json")
|
||||
if self.combo_box.currentText():
|
||||
return os.path.join(self.config_dir, self.combo_box.currentText() + ".json")
|
||||
|
||||
|
||||
class AddProjectDialog(QDialog):
|
||||
|
@ -1047,4 +1050,537 @@ class DraggableLine(QFrame):
|
|||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if event.button() == Qt.LeftButton:
|
||||
self.dragging = False
|
||||
self.dragging = False
|
||||
|
||||
class OutputDirSelector(QWidget):
|
||||
# 定义一个信号,当路径改变时发出
|
||||
path_changed = Signal(str)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.output_dir = ""
|
||||
self.init_ui()
|
||||
self.set_style()
|
||||
|
||||
def init_ui(self):
|
||||
# 主布局
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setSpacing(10)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
# 组框
|
||||
self.group_box = QFrame()
|
||||
self.group_box.setFrameShape(QFrame.StyledPanel)
|
||||
group_layout = QVBoxLayout(self.group_box)
|
||||
group_layout.setSpacing(GROUP_BOX_SPACING)
|
||||
group_layout.setContentsMargins(*GROUP_BOX_MARGINS)
|
||||
|
||||
# 标题标签
|
||||
self.setWindowTitle("选择输出目录")
|
||||
|
||||
# 路径显示标签
|
||||
self.path_label = QLabel("未选择文件夹")
|
||||
self.path_label.setWordWrap(True)
|
||||
self.path_label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||
|
||||
# 选择按钮
|
||||
self.select_button = QPushButton("选择输出文件夹")
|
||||
self.select_button.clicked.connect(self.select_output_dir)
|
||||
|
||||
# 添加部件到布局
|
||||
group_layout.addWidget(self.path_label)
|
||||
group_layout.addWidget(self.select_button)
|
||||
|
||||
# 添加组框到主布局
|
||||
layout.addWidget(self.group_box)
|
||||
|
||||
def set_style(self):
|
||||
# 设置组框样式
|
||||
self.group_box.setStyleSheet(GROUP_BOX_STYLE)
|
||||
|
||||
# 设置路径显示样式
|
||||
self.path_label.setStyleSheet(PATH_DISPLAY_STYLE)
|
||||
|
||||
# 设置按钮样式
|
||||
self.select_button.setStyleSheet(PRIMARY_BUTTON_STYLE)
|
||||
|
||||
# 设置最小尺寸
|
||||
self.group_box.setMinimumSize(GROUP_BOX_MIN_WIDTH, GROUP_BOX_MIN_HEIGHT)
|
||||
|
||||
def select_output_dir(self):
|
||||
"""打开文件夹选择对话框"""
|
||||
dir_path = QFileDialog.getExistingDirectory(
|
||||
self,
|
||||
"选择输出文件夹",
|
||||
"", # 默认路径为空,使用系统默认
|
||||
QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks
|
||||
)
|
||||
|
||||
if dir_path: # 如果用户选择了文件夹
|
||||
self.output_dir = dir_path
|
||||
self.path_label.setText(dir_path)
|
||||
self.path_changed.emit(dir_path) # 发出路径改变信号
|
||||
|
||||
def get_output_dir(self):
|
||||
"""获取输出路径"""
|
||||
return self.output_dir
|
||||
|
||||
def set_output_dir(self, path):
|
||||
"""设置输出路径"""
|
||||
if path:
|
||||
self.output_dir = path
|
||||
self.path_label.setText(path)
|
||||
self.path_changed.emit(path)
|
||||
|
||||
|
||||
|
||||
class JsonFileHandler:
|
||||
"""处理JSON文件的读写操作"""
|
||||
|
||||
@staticmethod
|
||||
def read_json(file_path):
|
||||
"""读取JSON文件"""
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Error reading JSON file {file_path}: {e}")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def write_json(file_path, data):
|
||||
try:
|
||||
# 检查目录是否可写
|
||||
dir_path = os.path.dirname(file_path)
|
||||
if not os.access(dir_path, os.W_OK):
|
||||
print(f"错误:目录不可写 {dir_path}")
|
||||
return False
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=4)
|
||||
print(f"成功写入文件:{file_path}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"写入文件错误:{str(e)}")
|
||||
return False
|
||||
|
||||
class TreeModelManager:
|
||||
"""管理树形模型的数据操作"""
|
||||
|
||||
@staticmethod
|
||||
def create_left_tree_model(folder_list):
|
||||
"""创建左侧文件夹树模型"""
|
||||
model = QStandardItemModel()
|
||||
model.setHorizontalHeaderLabels(["Folder Structure"])
|
||||
folder_map = {}
|
||||
|
||||
for folder_path in folder_list:
|
||||
folder_name = os.path.basename(folder_path)
|
||||
folder_map[folder_name] = folder_path
|
||||
|
||||
parent_item = QStandardItem(folder_name)
|
||||
model.appendRow(parent_item)
|
||||
|
||||
try:
|
||||
for child in os.listdir(folder_path):
|
||||
child_path = os.path.join(folder_path, child)
|
||||
if os.path.isdir(child_path):
|
||||
child_item = QStandardItem(child)
|
||||
parent_item.appendRow(child_item)
|
||||
except Exception as e:
|
||||
print(f"Error reading folder {folder_path}: {e}")
|
||||
|
||||
return model, folder_map
|
||||
|
||||
@staticmethod
|
||||
def create_right_tree_model(folder_names):
|
||||
"""创建右侧可编辑树模型"""
|
||||
model = QStandardItemModel()
|
||||
model.setHorizontalHeaderLabels(["Name", "Value"])
|
||||
|
||||
json_data = {}
|
||||
for folder_name in folder_names:
|
||||
parent_item = QStandardItem(folder_name)
|
||||
parent_item.setEditable(False)
|
||||
|
||||
# 添加机组编号
|
||||
turbine_item = QStandardItem("turbinecode")
|
||||
turbine_value = QStandardItem(folder_name.replace("机组", "").strip())
|
||||
turbine_value.setEditable(True)
|
||||
parent_item.appendRow([turbine_item, turbine_value])
|
||||
|
||||
# 添加默认的三个子部件
|
||||
for i in range(1, 4):
|
||||
part_item = QStandardItem(f"part{i}")
|
||||
part_item.setEditable(False)
|
||||
|
||||
code_item = QStandardItem("001")
|
||||
code_item.setEditable(True)
|
||||
|
||||
part_item.appendRow([QStandardItem("code"), code_item])
|
||||
parent_item.appendRow(part_item)
|
||||
|
||||
model.appendRow(parent_item)
|
||||
|
||||
# 初始化JSON数据
|
||||
json_data[folder_name] = {
|
||||
"turbinecode": folder_name.replace("机组", "").strip(),
|
||||
"part1": {"code": "001"},
|
||||
"part2": {"code": "001"},
|
||||
"part3": {"code": "001"}
|
||||
}
|
||||
|
||||
return model, json_data
|
||||
|
||||
@staticmethod
|
||||
def update_model_from_json(model, folder_name, json_data):
|
||||
"""根据JSON数据更新模型"""
|
||||
parent_item = None
|
||||
for i in range(model.rowCount()):
|
||||
item = model.item(i)
|
||||
if item and item.text() == folder_name:
|
||||
parent_item = item
|
||||
break
|
||||
|
||||
if parent_item and folder_name in json_data:
|
||||
data = json_data[folder_name]
|
||||
|
||||
# 更新机组编号
|
||||
turbine_item = parent_item.child(0)
|
||||
if turbine_item and turbine_item.child(0, 1):
|
||||
turbine_item.child(0, 1).setText(data.get("turbinecode", ""))
|
||||
|
||||
# 更新部件信息
|
||||
for j in range(1, 4):
|
||||
part_item = parent_item.child(j)
|
||||
if part_item:
|
||||
part_name = part_item.text()
|
||||
if part_name in data:
|
||||
code_item = part_item.child(0, 1)
|
||||
if code_item:
|
||||
code_item.setText(data[part_name].get("code", "001"))
|
||||
|
||||
@staticmethod
|
||||
def update_json_from_model(model, json_data):
|
||||
for i in range(model.rowCount()):
|
||||
folder_index = model.index(i, 0)
|
||||
folder_name = model.data(folder_index)
|
||||
|
||||
if not folder_name:
|
||||
continue
|
||||
|
||||
# 确保数据结构存在
|
||||
if folder_name not in json_data:
|
||||
json_data[folder_name] = {"turbinecode": "", "part1": {}, "part2": {}, "part3": {}}
|
||||
|
||||
# 获取 turbinecode 值(从第二列获取)
|
||||
turbine_index = model.index(0, 1, folder_index) # 第一行第二列
|
||||
if turbine_index.isValid():
|
||||
new_value = model.data(turbine_index)
|
||||
print(f"更新 turbinecode: {json_data[folder_name]['turbinecode']} -> {new_value}")
|
||||
json_data[folder_name]["turbinecode"] = new_value
|
||||
|
||||
# 更新parts
|
||||
for part_num in range(1, 4):
|
||||
part_index = model.index(part_num, 0, folder_index)
|
||||
if part_index.isValid():
|
||||
code_index = model.index(0, 1, part_index)
|
||||
if code_index.isValid():
|
||||
json_data[folder_name][f"part{part_num}"]["code"] = model.data(code_index, Qt.DisplayRole)
|
||||
|
||||
class DirectSaveDelegate(QStyledItemDelegate):
|
||||
"""直接保存的委托实现"""
|
||||
def __init__(self, save_callback, parent=None):
|
||||
super().__init__(parent)
|
||||
self.save_callback = save_callback
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
try:
|
||||
# 提交数据前验证索引
|
||||
if not index.isValid():
|
||||
print("⚠️ 无效的模型索引")
|
||||
return
|
||||
|
||||
old_value = index.data(Qt.DisplayRole)
|
||||
super().setModelData(editor, model, index)
|
||||
new_value = index.data(Qt.DisplayRole)
|
||||
|
||||
print(f"📝 数据变更: {old_value} → {new_value}")
|
||||
|
||||
# 获取顶层文件夹索引
|
||||
top_index = index
|
||||
while top_index.parent().isValid():
|
||||
top_index = top_index.parent()
|
||||
|
||||
if model.hasIndex(top_index.row(), top_index.column(), top_index.parent()):
|
||||
folder_name = model.data(top_index, Qt.DisplayRole)
|
||||
self.save_callback(folder_name)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 委托错误: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
class FolderBrowser(QWidget):
|
||||
"""文件夹浏览编辑主窗口"""
|
||||
data_changed = Signal(dict) # 数据变更信号
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.output_path = QDir.currentPath() # 默认输出路径
|
||||
self.json_data = {} # 存储所有JSON数据
|
||||
self.folder_map = {} # 文件夹路径映射
|
||||
self.init_ui()
|
||||
self.setup_connections()
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化用户界面"""
|
||||
main_layout = QHBoxLayout(self)
|
||||
splitter = QSplitter(Qt.Horizontal)
|
||||
|
||||
# 左侧文件夹树视图
|
||||
self.left_model = QStandardItemModel()
|
||||
self.left_model.setHorizontalHeaderLabels(["Folder Structure"])
|
||||
self.left_tree = QTreeView()
|
||||
self.left_tree.setModel(self.left_model)
|
||||
self.left_tree.setEditTriggers(QTreeView.NoEditTriggers)
|
||||
|
||||
# 添加上下文菜单(复制功能)
|
||||
self.left_tree.setContextMenuPolicy(Qt.ActionsContextMenu)
|
||||
copy_action = QAction("Copy", self.left_tree)
|
||||
copy_action.triggered.connect(self.copy_left_tree_text)
|
||||
self.left_tree.addAction(copy_action)
|
||||
|
||||
# 右侧可编辑树视图
|
||||
self.right_model = QStandardItemModel()
|
||||
self.right_model.setHorizontalHeaderLabels(["Name", "Value"])
|
||||
self.right_tree = QTreeView()
|
||||
self.right_tree.setModel(self.right_model)
|
||||
self.right_tree.setItemDelegate(
|
||||
DirectSaveDelegate(self.handle_immediate_save, self.right_tree)
|
||||
)
|
||||
|
||||
# 添加到分割器
|
||||
splitter.addWidget(self.left_tree)
|
||||
splitter.addWidget(self.right_tree)
|
||||
splitter.setSizes([300, 500])
|
||||
main_layout.addWidget(splitter)
|
||||
|
||||
def handle_immediate_save(self, folder_name):
|
||||
print(f"🔧 保存触发 [{folder_name}]")
|
||||
|
||||
try:
|
||||
# 安全获取文件夹索引
|
||||
matches = self.right_model.match(
|
||||
self.right_model.index(0, 0),
|
||||
Qt.DisplayRole,
|
||||
folder_name,
|
||||
hits=1,
|
||||
flags=Qt.MatchExactly
|
||||
)
|
||||
|
||||
if not matches:
|
||||
print(f"❌ 找不到文件夹: {folder_name}")
|
||||
return
|
||||
|
||||
folder_index = matches[0]
|
||||
|
||||
# 获取 turbinecode 值(使用标准模型访问方式)
|
||||
turbine_index = self.right_model.index(0, 1, folder_index)
|
||||
turbine_value = self.right_model.data(turbine_index, Qt.DisplayRole)
|
||||
|
||||
print(f"📊 当前值 - turbinecode: {turbine_value or '<空>'}")
|
||||
|
||||
# 更新数据
|
||||
TreeModelManager.update_json_from_model(self.right_model, self.json_data)
|
||||
self.save_to_json(folder_name)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 保存错误: {str(e)}")
|
||||
|
||||
def setup_connections(self):
|
||||
"""设置信号和槽的连接"""
|
||||
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.right_tree.expanded.connect(self.sync_left_tree_expand)
|
||||
self.right_tree.collapsed.connect(self.sync_left_tree_collapse)
|
||||
|
||||
def refresh_views(self):
|
||||
"""刷新左右视图显示"""
|
||||
self.left_tree.setModel(None) # 先重置模型
|
||||
self.left_tree.setModel(self.left_model)
|
||||
|
||||
self.right_tree.setModel(None)
|
||||
self.right_tree.setModel(self.right_model)
|
||||
|
||||
# 展开第一层节点
|
||||
for i in range(self.left_model.rowCount()):
|
||||
self.left_tree.expand(self.left_model.index(i, 0))
|
||||
self.right_tree.expand(self.right_model.index(i, 0))
|
||||
|
||||
def copy_left_tree_text(self):
|
||||
"""复制左侧树选中的文本"""
|
||||
index = self.left_tree.currentIndex()
|
||||
if index.isValid():
|
||||
text = self.left_model.data(index, Qt.DisplayRole)
|
||||
QApplication.clipboard().setText(text)
|
||||
|
||||
def sync_right_tree_expand(self, index):
|
||||
"""同步右侧树的展开状态(仅第一层)"""
|
||||
if not index.parent().isValid(): # 只处理第一层
|
||||
right_index = self.right_model.index(index.row(), 0)
|
||||
self.right_tree.expand(right_index)
|
||||
|
||||
def sync_right_tree_collapse(self, index):
|
||||
"""同步右侧树的折叠状态(仅第一层)"""
|
||||
if not index.parent().isValid(): # 只处理第一层
|
||||
right_index = self.right_model.index(index.row(), 0)
|
||||
self.right_tree.collapse(right_index)
|
||||
|
||||
def sync_left_tree_expand(self, index):
|
||||
"""同步左侧树的展开状态(仅第一层)"""
|
||||
if not index.parent().isValid(): # 只处理第一层
|
||||
left_index = self.left_model.index(index.row(), 0)
|
||||
self.left_tree.expand(left_index)
|
||||
|
||||
def sync_left_tree_collapse(self, index):
|
||||
"""同步左侧树的折叠状态(仅第一层)"""
|
||||
if not index.parent().isValid(): # 只处理第一层
|
||||
left_index = self.left_model.index(index.row(), 0)
|
||||
self.left_tree.collapse(left_index)
|
||||
|
||||
def set_output_path(self, path):
|
||||
"""设置JSON文件输出路径"""
|
||||
self.output_path = path
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
def set_initial_folders(self, folder_list):
|
||||
"""初始化文件夹结构"""
|
||||
self.left_model.clear()
|
||||
self.right_model.clear()
|
||||
self.left_model.setHorizontalHeaderLabels(["Folder Structure"])
|
||||
self.right_model.setHorizontalHeaderLabels(["Name", "Value"])
|
||||
self.json_data = {}
|
||||
self.folder_map = {}
|
||||
|
||||
if not folder_list: # 添加空列表检查
|
||||
print("Warning: folder_list is empty")
|
||||
return
|
||||
self.folder_map = {} # 新增: {文件夹名: 初始文件名}
|
||||
|
||||
for folder_path in folder_list:
|
||||
folder_name = os.path.basename(folder_path)
|
||||
# 使用初始 turbinecode 作为固定文件名
|
||||
initial_code = folder_name.replace("机组", "").strip()
|
||||
self.folder_map[folder_name] = f"{initial_code}.json" # 存储固定文件名
|
||||
|
||||
# 创建左侧树模型
|
||||
self.left_model, self.folder_map = TreeModelManager.create_left_tree_model(folder_list)
|
||||
|
||||
# 创建右侧树模型
|
||||
folder_names = [os.path.basename(f) for f in folder_list]
|
||||
self.right_model, self.json_data = TreeModelManager.create_right_tree_model(folder_names)
|
||||
|
||||
# 初始化JSON文件
|
||||
for folder_name in folder_names:
|
||||
self.initialize_json_file(folder_name)
|
||||
|
||||
self.refresh_views() # 添加视图刷新
|
||||
|
||||
def initialize_json_file(self, folder_name):
|
||||
"""初始化JSON文件"""
|
||||
if not hasattr(self, 'output_path') or not self.output_path:
|
||||
QMessageBox.warning(self, "Warning", "Output path not set!")
|
||||
return
|
||||
|
||||
json_filename = self.get_json_file_path(folder_name)
|
||||
|
||||
# 如果文件已存在,则读取现有数据
|
||||
if os.path.exists(json_filename):
|
||||
existing_data = JsonFileHandler.read_json(json_filename)
|
||||
if existing_data:
|
||||
self.json_data[folder_name].update(existing_data)
|
||||
print(f"加载已有json文件: {json_filename},{existing_data}")
|
||||
TreeModelManager.update_model_from_json(
|
||||
self.right_model, folder_name, self.json_data
|
||||
)
|
||||
else:
|
||||
# 创建新JSON文件
|
||||
self.save_to_json(folder_name)
|
||||
|
||||
def get_json_file_path(self, folder_name):
|
||||
"""
|
||||
获取JSON文件路径
|
||||
规则: 使用原始文件夹名 + .json后缀,保存到输出目录
|
||||
"""
|
||||
# 确保文件夹名是有效的文件名
|
||||
safe_name = folder_name.strip()
|
||||
# 只添加.json后缀,不做其他修改
|
||||
if not safe_name.lower().endswith('.json'):
|
||||
safe_name += '.json'
|
||||
return os.path.join(self.output_path, safe_name)
|
||||
|
||||
def on_right_data_changed(self, top_left, bottom_right):
|
||||
folder_item = self.right_model.itemFromIndex(top_left)
|
||||
while folder_item and folder_item.parent() is not None:
|
||||
folder_item = folder_item.parent()
|
||||
print(f"开始更改")
|
||||
if folder_item:
|
||||
folder_name = folder_item.text()
|
||||
print(f"变更检测到文件夹:{folder_name}")
|
||||
|
||||
# 调试:打印变更前的数据
|
||||
print("变更前数据:", self.json_data.get(folder_name))
|
||||
|
||||
# 更新数据
|
||||
TreeModelManager.update_json_from_model(self.right_model, self.json_data)
|
||||
|
||||
# 调试:打印变更后的数据
|
||||
print("变更后数据:", self.json_data.get(folder_name))
|
||||
|
||||
self.save_to_json(folder_name)
|
||||
self.data_changed.emit(self.json_data)
|
||||
|
||||
def save_to_json(self, folder_name):
|
||||
json_path = self.get_json_file_path(folder_name)
|
||||
print(f"🛠️ 准备保存到: {json_path}")
|
||||
|
||||
# 验证路径
|
||||
if os.path.isdir(json_path):
|
||||
print(f"❌ 错误:路径是目录,自动添加.json后缀")
|
||||
json_path += '.json'
|
||||
|
||||
try:
|
||||
# 确保目录存在
|
||||
os.makedirs(os.path.dirname(json_path), exist_ok=True)
|
||||
|
||||
# 写入文件
|
||||
with open(json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(self.json_data[folder_name], f, ensure_ascii=False, indent=4)
|
||||
print(f"✅ 成功保存: {json_path}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ 保存失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def batch_update_folders(self, folder_mapping):
|
||||
"""批量更新文件夹结构"""
|
||||
for folder_name, new_data in folder_mapping.items():
|
||||
if folder_name in self.json_data:
|
||||
self.json_data[folder_name].update(new_data)
|
||||
TreeModelManager.update_model_from_json(
|
||||
self.right_model, folder_name, self.json_data
|
||||
)
|
||||
self.save_to_json(folder_name)
|
||||
|
||||
self.data_changed.emit(self.json_data)
|
||||
|
||||
def get_output_path(self):
|
||||
"""获取当前输出路径"""
|
||||
return self.output_path
|
||||
|
||||
def get_current_data(self):
|
||||
"""获取当前所有数据"""
|
||||
return self.json_data
|
|
@ -204,4 +204,87 @@ SPLITTER_STYLE = """
|
|||
QSplitter::handle:hover {
|
||||
background: #808080; /* 鼠标悬停时颜色 */
|
||||
}
|
||||
"""
|
||||
|
||||
# ====================== QMessageBox 样式宏定义 ======================
|
||||
# 基础消息框样式
|
||||
MESSAGE_BOX_STYLE = f"""
|
||||
QMessageBox {{
|
||||
font-family: "{FONT_FAMILY}";
|
||||
font-size: {CONTENT_FONT_SIZE}pt;
|
||||
background-color: {LIGHT_COLOR};
|
||||
color: {TEXT_COLOR};
|
||||
}}
|
||||
QMessageBox QLabel {{
|
||||
{LABEL_STYLE}
|
||||
min-width: 300px;
|
||||
min-height: 60px;
|
||||
}}
|
||||
QMessageBox QPushButton {{
|
||||
{BUTTON_STYLE}
|
||||
min-width: 80px;
|
||||
}}
|
||||
"""
|
||||
|
||||
# 信息消息框样式
|
||||
INFO_MESSAGE_BOX_STYLE = f"""
|
||||
{MESSAGE_BOX_STYLE}
|
||||
QMessageBox {{
|
||||
border-top: 4px solid {ACCENT_COLOR};
|
||||
}}
|
||||
QMessageBox QLabel[text^="<html>"] {{
|
||||
color: {PRIMARY_COLOR};
|
||||
}}
|
||||
"""
|
||||
|
||||
# 警告消息框样式
|
||||
WARNING_MESSAGE_BOX_STYLE = f"""
|
||||
{MESSAGE_BOX_STYLE}
|
||||
QMessageBox {{
|
||||
border-top: 4px solid #f39c12;
|
||||
}}
|
||||
QMessageBox QLabel[text^="<html>"] {{
|
||||
color: #e67e22;
|
||||
}}
|
||||
"""
|
||||
|
||||
# 错误消息框样式
|
||||
CRITICAL_MESSAGE_BOX_STYLE = f"""
|
||||
{MESSAGE_BOX_STYLE}
|
||||
QMessageBox {{
|
||||
border-top: 4px solid #e74c3c;
|
||||
}}
|
||||
QMessageBox QLabel[text^="<html>"] {{
|
||||
color: #c0392b;
|
||||
}}
|
||||
"""
|
||||
|
||||
# 提问消息框样式
|
||||
QUESTION_MESSAGE_BOX_STYLE = f"""
|
||||
{MESSAGE_BOX_STYLE}
|
||||
QMessageBox {{
|
||||
border-top: 4px solid {SUCCESS_COLOR};
|
||||
}}
|
||||
QMessageBox QLabel[text^="<html>"] {{
|
||||
color: {SECONDARY_COLOR};
|
||||
}}
|
||||
"""
|
||||
|
||||
# 消息框按钮样式
|
||||
MESSAGE_BOX_BUTTON_STYLE = f"""
|
||||
QMessageBox QPushButton {{
|
||||
{PRIMARY_BUTTON_STYLE}
|
||||
min-width: 80px;
|
||||
max-width: 120px;
|
||||
}}
|
||||
QMessageBox QPushButton[text="Yes"],
|
||||
QMessageBox QPushButton[text="OK"] {{
|
||||
background-color: {SUCCESS_COLOR};
|
||||
color: {LIGHT_TEXT_COLOR};
|
||||
}}
|
||||
QMessageBox QPushButton[text="No"],
|
||||
QMessageBox QPushButton[text="Cancel"] {{
|
||||
background-color: #e74c3c;
|
||||
color: {LIGHT_TEXT_COLOR};
|
||||
}}
|
||||
"""
|
Loading…
Reference in New Issue