diff --git a/config/单次检查配置信息/1.json b/config/单次检查配置信息/1.json new file mode 100644 index 0000000..58e9535 --- /dev/null +++ b/config/单次检查配置信息/1.json @@ -0,0 +1,15 @@ +{ + "检查人员": "213", + "厂家人员": "213", + "业主人员": "213", + "数据处理人员": "213", + "报告编制人员": "123", + "机组型号": "123", + "机组厂家": "213", + "施工日期": "开始-结束", + "": "123", + "外部检查": true, + "内部检查": true, + "防雷检查": true, + "json路径": "/home/dtyx/桌面/yhh/ReportGenerator/config/单次检查配置信息" +} \ No newline at end of file diff --git a/config/项目基本信息/1234.json b/config/项目基本信息/1234.json deleted file mode 100644 index a1c959d..0000000 --- a/config/项目基本信息/1234.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "项目名称": "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/font/SourceHanSerifCN-Medium.ttf b/font/SourceHanSerifCN-Medium.ttf new file mode 100644 index 0000000..02d7172 Binary files /dev/null and b/font/SourceHanSerifCN-Medium.ttf differ diff --git a/info_core/MyQtClass.py b/info_core/MyQtClass.py index c6c7d8e..b4ab4d7 100644 --- a/info_core/MyQtClass.py +++ b/info_core/MyQtClass.py @@ -1,9 +1,13 @@ 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 + QTextBrowser, QApplication, QCompleter, QFrame, + QListWidgetItem, QListWidget, QAbstractItemView, + QStackedWidget, QStackedLayout) +from PySide6.QtCore import (QDateTime,QTimer,QDateTime,Signal,QSettings, + QSortFilterProxyModel, QSize) +from PySide6.QtGui import (QPixmap, QDragEnterEvent, QDropEvent, Qt, QIcon, + QFontMetrics) import json, sys, os from info_core.defines import * @@ -138,6 +142,10 @@ class ConfigComboBoxGroup(QGroupBox): QMessageBox.information(self, '信息', '删除操作已取消。') else: QMessageBox.warning(self, '警告', '没有选中任何项目。') + def get_current_config_file(self): + """获取当前选中的配置文件""" + 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): @@ -298,8 +306,9 @@ class AddPersonnelDialog(QDialog): {"key": "报告编制人员", "label": "报告编制人员:", "default": ""}, {"key": "机组型号", "label": "机组型号:", "default": ""}, {"key": "机组厂家", "label": "机组厂家:", "default": ""}, + {"key": "叶片型号", "label": "叶片型号", "default": ""}, + {"key": "叶片厂家", "label": "叶片厂家:", "default": ""}, {"key": "施工日期", "label": "施工日期", "default": "开始-结束"}, - {"key": "", "label": "", "default": ""}, #{"key": "", "label": "", "default": ""}, # 可以轻松添加更多人员字段 ] @@ -377,7 +386,25 @@ class AddPersonnelDialog(QDialog): 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) @@ -390,6 +417,10 @@ class AddPersonnelDialog(QDialog): 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 # 验证保存目录 @@ -420,11 +451,7 @@ class AddPersonnelDialog(QDialog): 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)) + self.parent().load_config_files() # 提示用户保存成功 QMessageBox.information(self, "信息", f"人员信息已成功保存到 {file_path}") @@ -700,386 +727,300 @@ class SmartDropdown(QWidget): 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) # 获取报告选择 +class FolderDropWidget(QWidget): + selection_changed = Signal(list) - 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.current_dir = None + self.selected_folders = set() + self.init_ui() self.apply_styles() - - def setup_ui(self): - """设置UI布局""" - self.setMinimumSize(GROUP_BOX_MIN_WIDTH, GROUP_BOX_MIN_HEIGHT) - + + def init_ui(self): main_layout = QVBoxLayout() - main_layout.setSpacing(GROUP_BOX_SPACING) - main_layout.setContentsMargins(*GROUP_BOX_MARGINS) + main_layout.setSpacing(15) + main_layout.setContentsMargins(15, 15, 15, 15) + + # 顶部按钮区域 + 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.waiting_label = QLabel("请先导入图片文件夹以开始解析") - self.waiting_label.setProperty("class", "waiting-label") + self.clear_btn = QPushButton("清空选中机组列表") + self.clear_btn.clicked.connect(self.clear_selected) + self.clear_btn.setFixedSize(120, 40) + self.clear_btn.setVisible(False) - # 解析结果区域 - self.result_frame = QFrame() - result_layout = QVBoxLayout() - result_layout.setSpacing(15) + btn_layout.addWidget(self.reset_btn) + btn_layout.addWidget(self.clear_btn) + btn_layout.addStretch() + main_layout.addLayout(btn_layout) + + # 主内容区域 - 使用堆叠布局管理不同状态 + self.stacked_layout = QStackedLayout() - # 图片文件夹信息 - 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) + # 状态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() - # 图片数量信息 - 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) + # 添加状态标签 + self.level1_title = QLabel("项目选择") + self.level1_title.setAlignment(Qt.AlignmentFlag.AlignCenter) + level1_main_layout.addWidget(self.level1_title) - # 分隔线 - separator = QFrame() - separator.setFrameShape(QFrame.Shape.HLine) - separator.setProperty("class", "separator") + 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) - # 生成路径区域 - 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.level1_container.setLayout(level1_main_layout) + self.stacked_layout.addWidget(self.level1_container) + + # 状态3: 显示第二层文件夹 (机组选择) + self.level2_container = QWidget() + level2_main_layout = QVBoxLayout() - # 选择按钮 - 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) + # 添加状态标签 + self.level2_title = QLabel("机组选择") + self.level2_title.setAlignment(Qt.AlignmentFlag.AlignCenter) + level2_main_layout.addWidget(self.level2_title) - # 生成报告选项 - 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) + 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) - # 添加到结果布局 - 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.back_btn = QPushButton("返回项目选择") + self.back_btn.clicked.connect(lambda: self.stacked_layout.setCurrentIndex(1)) + level2_main_layout.addWidget(self.back_btn) - self.result_frame.setLayout(result_layout) - self.result_frame.hide() - - # 添加到主布局 - main_layout.addWidget(self.waiting_label) - main_layout.addWidget(self.result_frame) + 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 apply_styles(self): - """完全使用宏定义应用样式""" - self.setStyleSheet(GROUP_BOX_STYLE) + self.prompt_label.setStyleSheet(f""" + {PATH_DISPLAY_STYLE} + border: 2px dashed {SECONDARY_COLOR}; + min-height: 150px; + """) - # 通过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} + 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.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.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.clear_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() + 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) - 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 +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 \ No newline at end of file diff --git a/info_core/__pycache__/MyQtClass.cpython-310.pyc b/info_core/__pycache__/MyQtClass.cpython-310.pyc index 30dbda1..333ba40 100644 Binary files a/info_core/__pycache__/MyQtClass.cpython-310.pyc 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 index fdd2c14..490597b 100644 Binary files a/info_core/__pycache__/defines.cpython-310.pyc and b/info_core/__pycache__/defines.cpython-310.pyc differ diff --git a/info_core/defines.py b/info_core/defines.py index 449a1b9..5485e6b 100644 --- a/info_core/defines.py +++ b/info_core/defines.py @@ -18,8 +18,8 @@ TEXT_COLOR = "#2c3e50" LIGHT_TEXT_COLOR = "#ecf0f1" # 按钮样式 -BUTTON_WIDTH = 100 -BUTTON_HEIGHT = 34 +BUTTON_WIDTH = 200 +BUTTON_HEIGHT = 20 BUTTON_RADIUS = 4 BUTTON_FONT_WEIGHT = QFont.Weight.Medium @@ -64,7 +64,7 @@ PRIMARY_BUTTON_STYLE = f""" """ # 下拉框样式 -COMBO_BOX_HEIGHT = 34 +COMBO_BOX_HEIGHT = 10 COMBO_BOX_STYLE = f""" QComboBox {{ font-family: "{FONT_FAMILY}"; @@ -81,10 +81,10 @@ COMBO_BOX_STYLE = f""" """ # 组框样式 -GROUP_BOX_MIN_WIDTH = 380 -GROUP_BOX_MIN_HEIGHT = 200 -GROUP_BOX_SPACING = 15 -GROUP_BOX_MARGINS = (15, 15, 15, 15) +GROUP_BOX_MIN_WIDTH = 300 +GROUP_BOX_MIN_HEIGHT = 120 +GROUP_BOX_SPACING = 5 +GROUP_BOX_MARGINS = (5, 5, 5, 5) GROUP_BOX_STYLE = f""" QGroupBox {{ font-family: "{FONT_FAMILY}"; @@ -127,8 +127,8 @@ PATH_DISPLAY_STYLE = f""" # 主窗口样式 WINDOW_MIN_WIDTH = 1000 WINDOW_MIN_HEIGHT = 720 -MAIN_LAYOUT_SPACING = 20 -MAIN_LAYOUT_MARGINS = (25, 25, 25, 25) +MAIN_LAYOUT_SPACING = 10 +MAIN_LAYOUT_MARGINS = (5, 5, 5, 5) # 信息标签样式 (左侧固定标签) INFO_LABEL_STYLE = f""" @@ -167,7 +167,7 @@ WAITING_LABEL_STYLE = f""" """ CHECKBOX_SIZE = 16 # 复选框大小 -CHECKBOX_MARGIN = 4 # 边距 +CHECKBOX_MARGIN = 1 # 边距 # 基础CheckBox样式 CHECKBOX_STYLE = f""" @@ -193,6 +193,15 @@ PRIMARY_CHECKBOX_STYLE = f""" }} QCheckBox::indicator:checked {{ background-color: {PRIMARY_COLOR}; - image: url(:/icons/check_white.svg); }} +""" + +SPLITTER_STYLE = """ + QSplitter::handle { + background: #a0a0a0; /* 分割线颜色 */ + height: 4px; /* 水平分割线高度(垂直分割线用 width) */ + } + QSplitter::handle:hover { + background: #808080; /* 鼠标悬停时颜色 */ + } """ \ No newline at end of file diff --git a/main.py b/main.py index 72d2a76..cd4011c 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,11 @@ from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QGridLayout, - QPushButton, QSizePolicy) + QPushButton, QSizePolicy, QSplitter, QToolBar) from PySide6.QtGui import QFontDatabase -from PySide6.QtCore import Signal +from PySide6.QtCore import Signal, Qt + import os from info_core.defines import * -from info_core.MyQtClass import ConfigComboBoxGroup, FolderImportWidget, ImageAnalysisWidget +from info_core.MyQtClass import ConfigComboBoxGroup, FolderDropWidget, DraggableLine class ReportGeneratorUI(QMainWindow): @@ -46,46 +47,66 @@ class ReportGeneratorUI(QMainWindow): # 第一行:项目信息和人员配置 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.picture_group = FolderDropWidget() + # self.image_analysis = + # self.main_layout.addWidget(self.image_analysis, 1, 1) # 第三行:生成报告按钮(跨两列) 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) # 第三行不拉伸(固定高度) + # 创建一个垂直分割器 + self.splitter = QSplitter(Qt.Vertical) + self.splitter.setStyleSheet(SPLITTER_STYLE) + # 创建顶部和底部容器 + top_container = QWidget() + top_container.setLayout(QGridLayout()) + top_container.layout().addWidget(self.project_group, 0, 0) + top_container.layout().addWidget(self.staff_group, 0, 1) + + middle_container = QWidget() + middle_container.setLayout(QGridLayout()) + middle_container.layout().addWidget(self.picture_group, 0, 0) + + # 添加部件到分割器 + self.splitter.addWidget(top_container) + self.splitter.addWidget(middle_container) + + # 设置主布局 + self.main_layout.addWidget(self.splitter, 0, 0, 2, 2) # 占据前两行两列 + self.main_layout.addWidget(self.generate_btn, 2, 0, 1, 2) + + # 设置分割器初始比例 + self.splitter.setStretchFactor(0, 1) + self.splitter.setStretchFactor(1, 4) + + self.toolbar = QToolBar() + self.addToolBar(self.toolbar) + self.toolbar.setMovable(False) + self.toolbar.setFloatable(False) + new_action = self.toolbar.addAction("重置布局比例") + self.toolbar.addSeparator() + new_action.triggered.connect(self.reset_splitter) + def reset_splitter(self): + """重置分割器的比例""" + total_size = sum(self.splitter.sizes()) # 获取当前总大小 + self.splitter.setSizes([ + int(total_size * 0.2), # 第一部分占 20%(比例 1:4) + int(total_size * 0.8) # 第二部分占 80% + ]) + 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) + # 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): """创建统一风格的按钮"""