commit c28642bdec35e636524606121433c7dad73b29cb Author: Voge1imkafig <2390349500@qq.com> Date: Mon Aug 4 18:08:53 2025 +0800 新建仓库 diff --git a/README.md b/README.md new file mode 100644 index 0000000..3e51c71 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# 包含一整套流程的报告生成工具,包括: +- 数据获取:文件获取数据 +- 数据预处理:阴暗处亮度增加,细节增强。 +- 数据报告生成:基于模板批量生成报告。 + + +![项目架构](工具流程.png) \ No newline at end of file diff --git a/config/项目基本信息/123.json b/config/项目基本信息/123.json new file mode 100644 index 0000000..3169c4a --- /dev/null +++ b/config/项目基本信息/123.json @@ -0,0 +1,14 @@ +{ + "项目名称": "123", + "风场名": "123", + "风场地址": "123", + "甲方公司": "123", + "甲方负责人": "123", + "甲方负责人电话": "123", + "乙方公司": "武汉迪特聚能科技有限公司", + "乙方负责人": "123", + "乙方负责人电话": "123", + "项目规格": "123", + "项目工期": "123", + "json路径": "/home/dtyx/桌面/yhh/ReportGenerator/config/项目基本信息" +} \ No newline at end of file diff --git a/config/项目基本信息/1234.json b/config/项目基本信息/1234.json new file mode 100644 index 0000000..a1c959d --- /dev/null +++ b/config/项目基本信息/1234.json @@ -0,0 +1,14 @@ +{ + "项目名称": "123", + "风场名": "123", + "风场地址": "213", + "甲方公司": "123", + "甲方负责人": "123", + "甲方负责人电话": "123", + "乙方公司": "武汉迪特聚能科技有限公司", + "乙方负责人": "123", + "乙方负责人电话": "123", + "项目规格": "213", + "项目工期": "123", + "json路径": "/home/dtyx/桌面/yhh/ReportGenerator/config/项目基本信息" +} \ No newline at end of file diff --git a/info_core/MyQtClass.py b/info_core/MyQtClass.py new file mode 100644 index 0000000..c6c7d8e --- /dev/null +++ b/info_core/MyQtClass.py @@ -0,0 +1,1085 @@ +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) \ No newline at end of file diff --git a/info_core/__pycache__/MyQtClass.cpython-310.pyc b/info_core/__pycache__/MyQtClass.cpython-310.pyc new file mode 100644 index 0000000..30dbda1 Binary files /dev/null and b/info_core/__pycache__/MyQtClass.cpython-310.pyc differ diff --git a/info_core/__pycache__/defines.cpython-310.pyc b/info_core/__pycache__/defines.cpython-310.pyc new file mode 100644 index 0000000..fdd2c14 Binary files /dev/null and b/info_core/__pycache__/defines.cpython-310.pyc differ diff --git a/info_core/defines.py b/info_core/defines.py new file mode 100644 index 0000000..449a1b9 --- /dev/null +++ b/info_core/defines.py @@ -0,0 +1,198 @@ +from PySide6.QtGui import QFont +# ====================== 样式宏定义 ====================== +# 字体设置 +FONT_PATH = "font/SourceHanSerifCN-Medium.ttf" # 指定字体文件路径 +FONT_FAMILY = "Source Han Serif CN" # 字体家族名称 +TITLE_FONT_SIZE = 12 +CONTENT_FONT_SIZE = 11 +BUTTON_FONT_SIZE = 11 + +# 颜色定义 +PRIMARY_COLOR = "#2c3e50" +SECONDARY_COLOR = "#34495e" +ACCENT_COLOR = "#3498db" +SUCCESS_COLOR = "#2ecc71" +LIGHT_COLOR = "#ecf0f1" +DARK_COLOR = "#2c3e50" +TEXT_COLOR = "#2c3e50" +LIGHT_TEXT_COLOR = "#ecf0f1" + +# 按钮样式 +BUTTON_WIDTH = 100 +BUTTON_HEIGHT = 34 +BUTTON_RADIUS = 4 +BUTTON_FONT_WEIGHT = QFont.Weight.Medium + +BUTTON_STYLE = f""" + QPushButton {{ + font-family: "{FONT_FAMILY}"; + font-size: {BUTTON_FONT_SIZE}pt; + font-weight: {BUTTON_FONT_WEIGHT}; + color: {TEXT_COLOR}; + background-color: {LIGHT_COLOR}; + border: 1px solid {SECONDARY_COLOR}; + border-radius: {BUTTON_RADIUS}px; + padding: 6px 12px; + min-width: {BUTTON_WIDTH}px; + min-height: {BUTTON_HEIGHT}px; + }} + QPushButton:hover {{ + background-color: #dfe6ec; + }} + QPushButton:pressed {{ + background-color: #d0d7dd; + }} +""" + +PRIMARY_BUTTON_STYLE = f""" + QPushButton {{ + font-family: "{FONT_FAMILY}"; + font-size: {BUTTON_FONT_SIZE + 1}pt; + font-weight: {BUTTON_FONT_WEIGHT}; + color: {LIGHT_TEXT_COLOR}; + background-color: {PRIMARY_COLOR}; + border: 1px solid {DARK_COLOR}; + border-radius: {BUTTON_RADIUS}px; + padding: 8px 16px; + }} + QPushButton:hover {{ + background-color: #34495e; + }} + QPushButton:pressed {{ + background-color: #2c3e50; + }} +""" + +# 下拉框样式 +COMBO_BOX_HEIGHT = 34 +COMBO_BOX_STYLE = f""" + QComboBox {{ + font-family: "{FONT_FAMILY}"; + font-size: {CONTENT_FONT_SIZE}pt; + min-height: {COMBO_BOX_HEIGHT}px; + padding: 5px 10px; + border: 1px solid #bdc3c7; + border-radius: 4px; + }} + QComboBox::drop-down {{ + width: 30px; + border-left: 1px solid #bdc3c7; + }} +""" + +# 组框样式 +GROUP_BOX_MIN_WIDTH = 380 +GROUP_BOX_MIN_HEIGHT = 200 +GROUP_BOX_SPACING = 15 +GROUP_BOX_MARGINS = (15, 15, 15, 15) +GROUP_BOX_STYLE = f""" + QGroupBox {{ + font-family: "{FONT_FAMILY}"; + font-size: {TITLE_FONT_SIZE}pt; + font-weight: bold; + color: {PRIMARY_COLOR}; + border: 1px solid #bdc3c7; + border-radius: 5px; + margin-top: 10px; + }} + QGroupBox::title {{ + subcontrol-origin: margin; + left: 10px; + padding: 0 3px; + }} +""" + +# 标签样式 +LABEL_STYLE = f""" + QLabel {{ + font-family: "{FONT_FAMILY}"; + font-size: {CONTENT_FONT_SIZE}pt; + color: {TEXT_COLOR}; + }} +""" + +PATH_DISPLAY_STYLE = f""" + QLabel {{ + font-family: "{FONT_FAMILY}"; + font-size: {CONTENT_FONT_SIZE}pt; + background-color: {LIGHT_COLOR}; + border: 1px solid #dee2e6; + border-radius: 4px; + padding: 10px; + min-height: 80px; + color: {TEXT_COLOR}; + }} +""" + +# 主窗口样式 +WINDOW_MIN_WIDTH = 1000 +WINDOW_MIN_HEIGHT = 720 +MAIN_LAYOUT_SPACING = 20 +MAIN_LAYOUT_MARGINS = (25, 25, 25, 25) + +# 信息标签样式 (左侧固定标签) +INFO_LABEL_STYLE = f""" + QLabel {{ + {LABEL_STYLE} + min-height: {COMBO_BOX_HEIGHT}px; + padding: 5px; + min-width: 100px; + }} +""" + +# 信息显示样式 (右侧内容) +INFO_DISPLAY_STYLE = f""" + QLabel {{ + {PATH_DISPLAY_STYLE} + min-height: {COMBO_BOX_HEIGHT}px; + border-radius: 4px; + }} +""" + +# 分隔线样式 +SEPARATOR_STYLE = """ + QFrame { + border: 1px solid #dee2e6; + margin: 10px 0; + } +""" + +# 等待提示样式 +WAITING_LABEL_STYLE = f""" + QLabel {{ + {LABEL_STYLE} + font-size: {TITLE_FONT_SIZE}pt; + qproperty-alignment: AlignCenter; + }} +""" + +CHECKBOX_SIZE = 16 # 复选框大小 +CHECKBOX_MARGIN = 4 # 边距 + +# 基础CheckBox样式 +CHECKBOX_STYLE = f""" + QCheckBox {{ + font-family: "{FONT_FAMILY}"; + font-size: {CONTENT_FONT_SIZE}pt; + color: {TEXT_COLOR}; + spacing: 8px; + padding: {CHECKBOX_MARGIN}px; + }} + QCheckBox::indicator {{ + width: {CHECKBOX_SIZE}px; + height: {CHECKBOX_SIZE}px; + }} +""" + +# 主色调CheckBox +PRIMARY_CHECKBOX_STYLE = f""" + {CHECKBOX_STYLE} + QCheckBox::indicator {{ + border: 1px solid {PRIMARY_COLOR}; + border-radius: 3px; + }} + QCheckBox::indicator:checked {{ + background-color: {PRIMARY_COLOR}; + image: url(:/icons/check_white.svg); + }} +""" \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..72d2a76 --- /dev/null +++ b/main.py @@ -0,0 +1,109 @@ +from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QGridLayout, + QPushButton, QSizePolicy) +from PySide6.QtGui import QFontDatabase +from PySide6.QtCore import Signal +import os +from info_core.defines import * +from info_core.MyQtClass import ConfigComboBoxGroup, FolderImportWidget, ImageAnalysisWidget + + +class ReportGeneratorUI(QMainWindow): + send_baogao_choose_info = Signal(list[str]) + + def __init__(self): + super().__init__() + # 加载字体 + self.load_font() + + # 设置窗口属性 + self.setWindowTitle("报告生成器") + self.setMinimumSize(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT) + + # 主窗口部件 + self.central_widget = QWidget() + self.setCentralWidget(self.central_widget) + + # 主布局 + self.main_layout = QGridLayout(self.central_widget) + self.main_layout.setSpacing(MAIN_LAYOUT_SPACING) + self.main_layout.setContentsMargins(*MAIN_LAYOUT_MARGINS) + + # 初始化UI + self.init_ui() + + + def load_font(self): + """加载自定义字体""" + if os.path.exists(FONT_PATH): + font_id = QFontDatabase.addApplicationFont(FONT_PATH) + if font_id == -1: + print("字体加载失败,将使用系统默认字体") + else: + print(f"字体文件未找到: {FONT_PATH},将使用系统默认字体") + + def init_ui(self): + """初始化所有UI组件""" + # 第一行:项目信息和人员配置 + self.project_group = ConfigComboBoxGroup("项目基本信息") + self.staff_group = ConfigComboBoxGroup("单次检查配置信息", is_project=False) + self.main_layout.addWidget(self.project_group, 0, 0) + self.main_layout.addWidget(self.staff_group, 0, 1) + + # 第二行:导入图片路径、填写机组信息 + self.picture_group = FolderImportWidget("导入图片路径") + self.main_layout.addWidget(self.picture_group, 1, 0) + self.image_analysis = ImageAnalysisWidget("填写机组信息") + self.main_layout.addWidget(self.image_analysis, 1, 1) + # 连接信号 + self.picture_group.main_folder_selected.connect(self.image_analysis.set_image_folder) + self.image_analysis.generate_path_selected.connect(self.on_generate_path_selected) + + # 获取请求信号,调用获取函数,发送更新信号 + self.picture_group.get_baogao_choose_info.connect(self.get_baogao_choose_info) + self.send_baogao_choose_info.connect(self.picture_group.update_baogao_choose_info) + + # 第三行:生成报告按钮(跨两列) + self.create_generate_button() + self.generate_btn.setEnabled(False) + self.main_layout.addWidget(self.generate_btn, 2, 0, 1, 2) + + # 设置列和行的拉伸比例 + self.main_layout.setColumnStretch(0, 1) # 第一列拉伸比例 + self.main_layout.setColumnStretch(1, 1) # 第二列拉伸比例 + self.main_layout.setRowStretch(0, 1) # 第一行拉伸比例为1 + self.main_layout.setRowStretch(1, 4) # 第二行拉伸比例为4 + self.main_layout.setRowStretch(2, 0) # 第三行不拉伸(固定高度) + + def on_generate_path_selected(self, path): + self.generate_btn.setEnabled(True) + + def get_baogao_choose_info(self): + search_file_list = [] + if self.image_analysis.check_is_waibu: + search_file_list.append("外汇总") + if self.image_analysis.check_is_neibu: + search_file_list.append("内汇总") + if self.image_analysis.check_is_fanglei: + search_file_list.append("防汇总") + self.send_baogao_choose_info.emit(search_file_list) + + def create_button(self, text): + """创建统一风格的按钮""" + btn = QPushButton(text) + btn.setStyleSheet(BUTTON_STYLE) + btn.setFixedSize(BUTTON_WIDTH, BUTTON_HEIGHT) + return btn + + def create_generate_button(self): + """创建生成报告按钮""" + self.generate_btn = QPushButton("生成报告") + self.generate_btn.setStyleSheet(PRIMARY_BUTTON_STYLE) + self.generate_btn.setFixedHeight(50) + self.generate_btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) + + +if __name__ == "__main__": + app = QApplication([]) + window = ReportGeneratorUI() + window.show() + app.exec() \ No newline at end of file diff --git a/工具流程.png b/工具流程.png new file mode 100644 index 0000000..df1600d Binary files /dev/null and b/工具流程.png differ