from PySide6.QtWidgets import (QGroupBox, QVBoxLayout, QHBoxLayout, QCheckBox, QPushButton, QDialog, QLineEdit, QFontComboBox, QFileDialog, QMessageBox, QLabel, QWidget, QTextBrowser, QApplication, QCompleter, QFrame, QListWidgetItem, QListWidget, QAbstractItemView, QStackedWidget, QStackedLayout, QStyledItemDelegate, QTreeView, QSplitter, QFileSystemModel) from PySide6.QtCore import (QDateTime,QTimer,QDateTime,Signal,QSettings, QSortFilterProxyModel, QSize, QDir) from PySide6.QtGui import (QPixmap, QDragEnterEvent, QDropEvent, Qt, QIcon, QFontMetrics, QStandardItem, QStandardItemModel, QAction) 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, '警告', '没有选中任何项目。') def get_current_config_file(self): """获取当前选中的配置文件""" if self.combo_box.currentText(): return os.path.join(self.config_dir, self.combo_box.currentText() + ".json") 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": "开始-结束"}, #{"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) # 创建复选框布局 checkbox_group = QGroupBox("检查类型") checkbox_layout = QHBoxLayout() # 添加复选框 self.checkboxes = {} for field in self.checkbox_fields: checkbox = QCheckBox(field["label"]) if self.path and field["key"] in self.data: checkbox.setChecked(bool(self.data[field["key"]])) else: checkbox.setChecked(field["default"]) self.checkboxes[field["key"]] = checkbox checkbox_layout.addWidget(checkbox) checkbox_group.setLayout(checkbox_layout) # 添加到主布局 main_layout.addWidget(checkbox_group) 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 } # 添加复选框数据 for key, checkbox in self.checkboxes.items(): personnel_info[key] = checkbox.isChecked() 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 self.parent().load_config_files() # 提示用户保存成功 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 FolderDropWidget(QWidget): selection_changed = Signal(list) def __init__(self, parent=None): super().__init__(parent) self.setAcceptDrops(True) self.current_dir = None self.selected_folders = set() self.init_ui() self.apply_styles() def init_ui(self): main_layout = QVBoxLayout() main_layout.setSpacing(5) main_layout.setContentsMargins(0, 0, 0, 0) # 顶部按钮区域 btn_layout = QHBoxLayout() self.reset_btn = QPushButton("重新选择项目文件夹") self.reset_btn.clicked.connect(self.reset_selection) self.reset_btn.setFixedSize(120, 40) self.reset_btn.setVisible(False) self.clear_btn = QPushButton("清空") self.clear_btn.clicked.connect(self.clear_selected) self.clear_btn.setFixedSize(120, 40) self.clear_btn.setVisible(False) self.full_select_btn = QPushButton("全选") self.full_select_btn.clicked.connect(self.select_all_folders) self.full_select_btn.setFixedSize(120, 40) self.full_select_btn.setVisible(False) # 添加返回按钮 self.back_btn = QPushButton("返回项目选择") self.back_btn.clicked.connect(lambda: (self.stacked_layout.setCurrentIndex(1) or self.clear_selected() or self.reset_btn.setVisible(True) or self.back_btn.setVisible(False))) self.back_btn.setVisible(False) btn_layout.addWidget(self.reset_btn) btn_layout.addWidget(self.back_btn) btn_layout.addWidget(self.full_select_btn) btn_layout.addWidget(self.clear_btn) btn_layout.addStretch() main_layout.addLayout(btn_layout) # 主内容区域 - 使用堆叠布局管理不同状态 self.stacked_layout = QStackedLayout() # 状态1: 未选择文件夹时的提示 self.prompt_container = QWidget() prompt_layout = QVBoxLayout() self.prompt_label = QLabel("拖放文件夹到此处或点击选择文件夹") self.prompt_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.prompt_label.mousePressEvent = self.select_folder prompt_layout.addWidget(self.prompt_label) self.prompt_container.setLayout(prompt_layout) self.stacked_layout.addWidget(self.prompt_container) # 状态2: 显示第一层文件夹 (项目选择) self.level1_container = QWidget() level1_main_layout = QVBoxLayout() level1_main_layout.setSpacing(0) level1_main_layout.setContentsMargins(0, 0, 0, 0) # 添加状态标签 self.level1_title = QLabel("项目选择") self.level1_title.setAlignment(Qt.AlignmentFlag.AlignCenter) level1_main_layout.addWidget(self.level1_title) self.level1_list = QListWidget() self.level1_list.setIconSize(QSize(48, 48)) self.level1_list.setViewMode(QListWidget.ViewMode.IconMode) self.level1_list.setResizeMode(QListWidget.ResizeMode.Adjust) self.level1_list.setMovement(QListWidget.Movement.Static) self.level1_list.setSpacing(10) self.level1_list.itemDoubleClicked.connect(self.show_level2_folders) level1_main_layout.addWidget(self.level1_list) self.level1_container.setLayout(level1_main_layout) self.stacked_layout.addWidget(self.level1_container) # 状态3: 显示第二层文件夹 (机组选择) self.level2_container = QWidget() level2_main_layout = QVBoxLayout() level2_main_layout.setSpacing(0) level2_main_layout.setContentsMargins(0, 0, 0, 0) # 添加状态标签 self.level2_title = QLabel("机组选择") self.level2_title.setAlignment(Qt.AlignmentFlag.AlignCenter) level2_main_layout.addWidget(self.level2_title) self.level2_list = QListWidget() self.level2_list.setIconSize(QSize(48, 48)) self.level2_list.setViewMode(QListWidget.ViewMode.IconMode) self.level2_list.setResizeMode(QListWidget.ResizeMode.Adjust) self.level2_list.setMovement(QListWidget.Movement.Static) self.level2_list.setSpacing(10) level2_main_layout.addWidget(self.level2_list) self.level2_container.setLayout(level2_main_layout) self.stacked_layout.addWidget(self.level2_container) # 将堆叠布局添加到主布局 stack_container = QWidget() stack_container.setLayout(self.stacked_layout) main_layout.addWidget(stack_container) # 已选目录列表 (始终显示在底部) self.selected_group = QGroupBox("已选机组") self.selected_group.setVisible(False) selected_layout = QVBoxLayout() self.selected_list = QListWidget() self.selected_list.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) selected_layout.addWidget(self.selected_list) self.selected_group.setLayout(selected_layout) main_layout.addWidget(self.selected_group) self.setLayout(main_layout) def select_all_folders(self): self.selected_folders.update( set(item.data(Qt.ItemDataRole.UserRole) for item in self.level2_list.findItems("", Qt.MatchFlag.MatchContains)) ) self.update_checkbox_states() self.selection_changed.emit(self.get_selected_folders()) def apply_styles(self): self.prompt_label.setStyleSheet(f""" {PATH_DISPLAY_STYLE} border: 2px dashed {SECONDARY_COLOR}; min-height: 150px; """) self.level1_list.setStyleSheet(f""" QListWidget {{ background-color: #f8f9fa; min-height: 150px; }} """) self.level2_list.setStyleSheet(f""" QListWidget {{ background-color: #f8f9fa; min-height: 150px; }} """) self.level1_title.setStyleSheet(f""" {LABEL_STYLE} font-size: {TITLE_FONT_SIZE}pt; font-weight: bold; padding: 10px; """) self.level2_title.setStyleSheet(f""" {LABEL_STYLE} font-size: {TITLE_FONT_SIZE}pt; font-weight: bold; padding: 10px; """) self.selected_group.setStyleSheet(GROUP_BOX_STYLE) self.full_select_btn.setStyleSheet(BUTTON_STYLE) self.reset_btn.setStyleSheet(BUTTON_STYLE) self.clear_btn.setStyleSheet(BUTTON_STYLE) self.back_btn.setStyleSheet(BUTTON_STYLE) def select_folder(self, event=None): folder = QFileDialog.getExistingDirectory(self, "选择文件夹") if folder: self.load_folder(folder) def reset_selection(self): self.current_dir = None self.selected_folders.clear() self.level1_list.clear() self.level2_list.clear() self.selected_list.clear() self.stacked_layout.setCurrentIndex(0) # 显示提示 self.reset_btn.setVisible(False) self.clear_btn.setVisible(False) self.back_btn.setVisible(False) self.selected_group.setVisible(False) self.selection_changed.emit([]) def clear_selected(self): self.selected_folders.clear() self.selected_list.clear() self.update_checkbox_states() self.selection_changed.emit([]) def load_folder(self, path): self.current_dir = path self.load_level1_folders(path) self.stacked_layout.setCurrentIndex(1) # 显示第一层 self.reset_btn.setVisible(True) self.back_btn.setVisible(False) self.clear_btn.setVisible(True) self.full_select_btn.setVisible(True) self.selected_group.setVisible(True) def dragEnterEvent(self, event: QDragEnterEvent): if event.mimeData().hasUrls(): event.acceptProposedAction() def dropEvent(self, event: QDropEvent): for url in event.mimeData().urls(): path = url.toLocalFile() if os.path.isdir(path): self.load_folder(path) break def load_level1_folders(self, root_path): self.level1_list.clear() self.level2_list.clear() for item in os.listdir(root_path): item_path = os.path.join(root_path, item) if os.path.isdir(item_path): item_widget = QListWidgetItem(QIcon.fromTheme("folder"), item) item_widget.setData(Qt.ItemDataRole.UserRole, item_path) item_widget.setSizeHint(QSize(80, 80)) item_widget.setTextAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignBottom) self.level1_list.addItem(item_widget) def show_level2_folders(self, item): self.level2_list.clear() self.reset_btn.setVisible(False) self.back_btn.setVisible(True) folder_path = item.data(Qt.ItemDataRole.UserRole) for sub_item in os.listdir(folder_path): sub_item_path = os.path.join(folder_path, sub_item) if os.path.isdir(sub_item_path): list_item = QListWidgetItem() list_item.setSizeHint(QSize(100, 80)) list_item.setData(Qt.ItemDataRole.UserRole, sub_item_path) widget = QWidget() layout = QVBoxLayout() layout.setAlignment(Qt.AlignmentFlag.AlignCenter) icon_label = QLabel() icon_label.setPixmap(QIcon.fromTheme("folder").pixmap(48, 48)) icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(icon_label) name_check_layout = QHBoxLayout() checkbox = QCheckBox(sub_item) checkbox.setStyleSheet(f""" {PRIMARY_CHECKBOX_STYLE} QCheckBox::indicator:checked {{ background-color: {PRIMARY_COLOR}; border: 1px solid {PRIMARY_COLOR}; }} """) checkbox.stateChanged.connect(lambda state, path=sub_item_path: self.toggle_folder_selection(path, state)) checkbox.setChecked(sub_item_path in self.selected_folders) name_check_layout.addWidget(checkbox) layout.addLayout(name_check_layout) widget.setLayout(layout) list_item.setSizeHint(widget.sizeHint()) self.level2_list.addItem(list_item) self.level2_list.setItemWidget(list_item, widget) self.stacked_layout.setCurrentIndex(2) # 显示第二层 def toggle_folder_selection(self, folder_path, state): if state == Qt.CheckState.Checked.value: self.selected_folders.add(folder_path) item = QListWidgetItem(folder_path) self.selected_list.addItem(item) else: self.selected_folders.discard(folder_path) items = self.selected_list.findItems(folder_path, Qt.MatchFlag.MatchExactly) for item in items: self.selected_list.takeItem(self.selected_list.row(item)) self.selection_changed.emit(self.get_selected_folders()) def update_checkbox_states(self): for i in range(self.level2_list.count()): item = self.level2_list.item(i) widget = self.level2_list.itemWidget(item) if widget: checkbox = widget.findChild(QCheckBox) if checkbox: path = item.data(Qt.ItemDataRole.UserRole) checkbox.setChecked(path in self.selected_folders) def get_selected_folders(self): return list(self.selected_folders) class DraggableLine(QFrame): def __init__(self, parent=None): super().__init__(parent) self.setFrameShape(QFrame.HLine) self.setFrameShadow(QFrame.Sunken) self.setLineWidth(2) self.setFixedHeight(10) self.dragging = False self.setCursor(Qt.SizeVerCursor) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.dragging = True self.start_pos = event.globalPos() def mouseMoveEvent(self, event): if self.dragging: delta = event.globalPos() - self.start_pos self.start_pos = event.globalPos() # 通知父窗口调整布局 self.parent().adjust_row_height(delta.y()) def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: 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