From f73123ad4ef505b800b36e9dcafa824d3ba5f2b3 Mon Sep 17 00:00:00 2001 From: ChengQiqian Date: Thu, 17 Jul 2025 10:50:07 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=20/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jizu_upload_2.py | 294 ++++++++++++++++ part_upload_2.py | 247 ++++++++++++++ picture_upload_2.py | 791 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1332 insertions(+) create mode 100644 jizu_upload_2.py create mode 100644 part_upload_2.py create mode 100644 picture_upload_2.py diff --git a/jizu_upload_2.py b/jizu_upload_2.py new file mode 100644 index 0000000..425c86d --- /dev/null +++ b/jizu_upload_2.py @@ -0,0 +1,294 @@ +import sys +import requests +import json +from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QLabel, QLineEdit, QPushButton, QTextEdit, QMessageBox, + QFileDialog, QComboBox, QFormLayout, QGroupBox) +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QFont + + +class TurbineAddWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("新增机组") + self.setGeometry(100, 100, 600, 600) + self.setStyleSheet(""" + QMainWindow { + background-color: #f5f5f5; + } + QGroupBox { + border: 1px solid #ccc; + border-radius: 5px; + margin-top: 10px; + padding-top: 15px; + } + QGroupBox::title { + subcontrol-origin: margin; + left: 10px; + padding: 0 3px; + } + QLabel { + font-weight: bold; + min-width: 80px; + } + QPushButton { + background-color: #4CAF50; + color: white; + border: none; + padding: 5px 10px; + border-radius: 3px; + } + QPushButton:hover { + background-color: #45a049; + } + QComboBox, QLineEdit, QTextEdit { + padding: 5px; + border: 1px solid #ddd; + border-radius: 3px; + } + """) + + # 存储项目数据的字典 {项目显示名称: 项目ID} + self.project_data = {} + + self.init_ui() + self.load_project_list() + + def init_ui(self): + # 主窗口部件 + central_widget = QWidget() + self.setCentralWidget(central_widget) + main_layout = QVBoxLayout() + + # 项目查询组 + project_group = QGroupBox("项目信息") + project_layout = QFormLayout() + + # 项目选择下拉框 + self.project_combo = QComboBox() + project_layout.addRow(QLabel("项目名称*:"), self.project_combo) + + # 刷新项目列表按钮 + self.refresh_btn = QPushButton("刷新项目列表") + self.refresh_btn.clicked.connect(self.load_project_list) + project_layout.addRow(self.refresh_btn) + + project_group.setLayout(project_layout) + main_layout.addWidget(project_group) + + # 机组信息组 + turbine_group = QGroupBox("机组信息") + turbine_layout = QFormLayout() + + # 机组名称 + self.turbine_name_input = QLineEdit() + turbine_layout.addRow(QLabel("机组名称:"), self.turbine_name_input) + + # 机组编码 + self.turbine_code_input = QLineEdit() + turbine_layout.addRow(QLabel("机组编码:"), self.turbine_code_input) + + # 机组描述 + self.turbine_desc_input = QTextEdit() + self.turbine_desc_input.setMaximumHeight(80) + turbine_layout.addRow(QLabel("机组描述:"), self.turbine_desc_input) + + # 机组厂商 + self.turbine_manufacturer_input = QLineEdit() + turbine_layout.addRow(QLabel("机组厂商:"), self.turbine_manufacturer_input) + + # 机组型号 + self.turbine_model_input = QLineEdit() + turbine_layout.addRow(QLabel("机组型号:"), self.turbine_model_input) + + # 机组封面图 + self.cover_image_path = "" + self.cover_image_label = QLabel("未选择图片") + self.cover_image_label.setStyleSheet("color: #666; font-style: italic;") + self.select_image_btn = QPushButton("选择图片") + self.select_image_btn.clicked.connect(self.select_image) + + image_layout = QHBoxLayout() + image_layout.addWidget(self.cover_image_label) + image_layout.addWidget(self.select_image_btn) + turbine_layout.addRow(QLabel("机组封面图:"), image_layout) + + turbine_group.setLayout(turbine_layout) + main_layout.addWidget(turbine_group) + + # 按钮区域 + button_layout = QHBoxLayout() + button_layout.addStretch() + + # 清空按钮 + self.clear_btn = QPushButton("清空表单") + self.clear_btn.setFixedWidth(100) + self.clear_btn.clicked.connect(self.clear_form) + button_layout.addWidget(self.clear_btn) + + # 提交按钮 + self.submit_btn = QPushButton("提交") + self.submit_btn.setFixedWidth(100) + self.submit_btn.clicked.connect(self.submit_data) + button_layout.addWidget(self.submit_btn) + + main_layout.addLayout(button_layout) + central_widget.setLayout(main_layout) + + def load_project_list(self): + """加载项目列表""" + url = "http://pms.dtyx.net:9158/project/list" + headers = { + "Authorization": "null", + "Content-Type": "application/x-www-form-urlencoded" + } + + try: + response = requests.get(url, headers=headers) + response.raise_for_status() + + result = response.json() + if result.get("success") and "data" in result: + self.project_data = {} + self.project_combo.clear() + + # 获取并排序项目数据 + projects = result["data"] + sorted_projects = sorted(projects, key=lambda x: x.get("projectName", "")) + + # 填充项目数据 + for project in sorted_projects: + display_name = f"{project.get('projectName', '')} (ID: {project.get('projectId', '')})" + self.project_data[display_name] = project.get("projectId", "") + self.project_combo.addItem(display_name) + + if self.project_data: + QMessageBox.information(self, "成功", f"成功加载{len(self.project_data)}个项目") + else: + QMessageBox.warning(self, "警告", "没有找到项目数据") + else: + QMessageBox.warning(self, "警告", f"获取项目列表失败: {result.get('msg', '未知错误')}") + + except Exception as e: + QMessageBox.critical(self, "错误", f"获取项目列表时出错:\n{str(e)}") + + def select_image(self): + options = QFileDialog.Options() + file_path, _ = QFileDialog.getOpenFileName( + self, "选择机组封面图", "", + "图片文件 (*.jpg *.jpeg *.png *.gif);;所有文件 (*.*)", + options=options + ) + + if file_path: + self.cover_image_path = file_path + self.cover_image_label.setText(file_path.split('/')[-1]) + self.cover_image_label.setStyleSheet("color: #333; font-style: normal;") + + def validate_inputs(self): + if not self.project_combo.currentText(): + QMessageBox.warning(self, "输入错误", "请选择项目!") + return False + return True + + def submit_data(self): + if not self.validate_inputs(): + return + + # 获取选择的项目ID + selected_project = self.project_combo.currentText() + project_id = self.project_data.get(selected_project, "") + + # 构造请求数据 + turbine_data = { + "projectId": project_id, + "turbineName": self.turbine_name_input.text().strip(), + "turbineCode": self.turbine_code_input.text().strip(), + "turbineDesc": self.turbine_desc_input.toPlainText().strip(), + "turbineManufacturer": self.turbine_manufacturer_input.text().strip(), + "turbineModel": self.turbine_model_input.text().strip(), + "turbineCoverUrl": self.cover_image_path if self.cover_image_path else "" + } + + # 确认对话框 + reply = QMessageBox.question( + self, '确认提交', + '确定要提交这些信息吗?', + QMessageBox.Yes | QMessageBox.No, QMessageBox.No + ) + + if reply == QMessageBox.No: + return + + # 提交数据 + url = "http://pms.dtyx.net:9158/turbine" + headers = { + "Authorization": "null", + "Content-Type": "application/json" + } + + try: + response = requests.post(url, headers=headers, data=json.dumps(turbine_data)) + response.raise_for_status() + + result = response.json() + + # 创建自定义消息框显示大尺寸结果 + msg_box = QMessageBox(self) + msg_box.setWindowTitle("提交成功") + msg_box.setIcon(QMessageBox.Information) + msg_box.setText("机组信息提交成功!") + + text_edit = QTextEdit() + text_edit.setReadOnly(True) + text_edit.setPlainText(json.dumps(result, indent=2)) + text_edit.setMinimumSize(500, 300) + text_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + + msg_box.layout().addWidget(text_edit, 1, 0, 1, msg_box.layout().columnCount()) + msg_box.setStyleSheet("QLabel{min-width: 400px;}") + msg_box.exec_() + + except Exception as e: + error_box = QMessageBox(self) + error_box.setWindowTitle("提交失败") + error_box.setIcon(QMessageBox.Critical) + error_box.setText("机组信息提交失败!") + + error_text = QTextEdit() + error_text.setReadOnly(True) + error_text.setPlainText(str(e)) + error_text.setMinimumSize(500, 150) + error_text.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + + error_box.layout().addWidget(error_text, 1, 0, 1, error_box.layout().columnCount()) + error_box.exec_() + + def clear_form(self): + """手动清空表单的方法""" + reply = QMessageBox.question( + self, '确认清空', + '确定要清空所有已填写的信息吗?', + QMessageBox.Yes | QMessageBox.No, QMessageBox.No + ) + + if reply == QMessageBox.Yes: + self.turbine_name_input.clear() + self.turbine_code_input.clear() + self.turbine_desc_input.clear() + self.turbine_manufacturer_input.clear() + self.turbine_model_input.clear() + self.cover_image_path = "" + self.cover_image_label.setText("未选择图片") + self.cover_image_label.setStyleSheet("color: #666; font-style: italic;") + + +if __name__ == "__main__": + app = QApplication(sys.argv) + font = QFont("Microsoft YaHei", 10) + app.setFont(font) + + window = TurbineAddWindow() + window.show() + sys.exit(app.exec_()) \ No newline at end of file diff --git a/part_upload_2.py b/part_upload_2.py new file mode 100644 index 0000000..b2bc1f6 --- /dev/null +++ b/part_upload_2.py @@ -0,0 +1,247 @@ +import sys +import requests +import json +from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QLabel, QLineEdit, QPushButton, QTextEdit, QMessageBox, + QComboBox, QFormLayout, QGroupBox, QSizePolicy) +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QFont + + +class PartAddWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("新增部件") + self.setGeometry(100, 100, 600, 600) + self.setStyleSheet(""" + QMainWindow { + background-color: #f5f5f5; + } + QGroupBox { + border: 1px solid #ccc; + border-radius: 5px; + margin-top: 10px; + padding-top: 15px; + } + QGroupBox::title { + subcontrol-origin: margin; + left: 10px; + padding: 0 3px; + } + QLabel { + font-weight: bold; + min-width: 80px; + } + QPushButton { + background-color: #4CAF50; + color: white; + border: none; + padding: 5px 10px; + border-radius: 3px; + } + QPushButton:hover { + background-color: #45a049; + } + QComboBox, QLineEdit, QTextEdit { + padding: 5px; + border: 1px solid #ddd; + border-radius: 3px; + } + """) + + self.turbine_data = {} + self.init_ui() + + def init_ui(self): + central_widget = QWidget() + self.setCentralWidget(central_widget) + main_layout = QVBoxLayout() + + # 项目查询组 + project_group = QGroupBox("项目查询") + project_layout = QFormLayout() + + self.project_id_input = QLineEdit() + self.project_id_input.setPlaceholderText("请输入项目ID") + project_layout.addRow(QLabel("项目ID*:"), self.project_id_input) + + self.query_btn = QPushButton("查询机组列表") + self.query_btn.clicked.connect(self.load_turbine_list) + project_layout.addRow(self.query_btn) + + project_group.setLayout(project_layout) + main_layout.addWidget(project_group) + + # 部件信息组 + part_group = QGroupBox("部件信息") + part_layout = QFormLayout() + + self.turbine_combo = QComboBox() + part_layout.addRow(QLabel("机组名称*:"), self.turbine_combo) + + self.part_name_combo = QComboBox() + self.part_name_combo.addItems(["", "叶片1", "叶片2", "叶片3"]) + part_layout.addRow(QLabel("部件名称*:"), self.part_name_combo) + + self.part_code_input = QLineEdit() + part_layout.addRow(QLabel("部件编号:"), self.part_code_input) + + self.part_type_combo = QComboBox() + self.part_type_combo.addItems(["", "VANE-1", "VANE-2", "VANE-3"]) + part_layout.addRow(QLabel("部件类型:"), self.part_type_combo) + + self.part_desc_input = QTextEdit() + self.part_desc_input.setMaximumHeight(80) + part_layout.addRow(QLabel("部件描述:"), self.part_desc_input) + + self.part_manufacturer_input = QLineEdit() + part_layout.addRow(QLabel("部件厂商:"), self.part_manufacturer_input) + + self.part_model_input = QLineEdit() + part_layout.addRow(QLabel("部件型号:"), self.part_model_input) + + part_group.setLayout(part_layout) + main_layout.addWidget(part_group) + + # 按钮区域 + button_layout = QHBoxLayout() + button_layout.addStretch() + + self.submit_btn = QPushButton("提交") + self.submit_btn.setFixedWidth(100) + self.submit_btn.clicked.connect(self.submit_data) + button_layout.addWidget(self.submit_btn) + + main_layout.addLayout(button_layout) + central_widget.setLayout(main_layout) + + def load_turbine_list(self): + project_id = self.project_id_input.text().strip() + if not project_id: + QMessageBox.warning(self, "输入错误", "请输入项目ID!") + return + + url = "http://pms.dtyx.net:9158/turbine/list" + headers = { + "Authorization": "null", + "Content-Type": "application/x-www-form-urlencoded" + } + + try: + params = {"projectId": project_id} + response = requests.get(url, headers=headers, params=params) + response.raise_for_status() + + result = response.json() + if result.get("success") and "data" in result: + self.turbine_data = {} + self.turbine_combo.clear() + + turbines = result["data"] + sorted_turbines = sorted(turbines, key=lambda x: x.get("turbineName", "")) + + for turbine in sorted_turbines: + display_name = f"{turbine.get('turbineName', '')} (ID: {turbine.get('turbineId', '')})" + self.turbine_data[display_name] = turbine.get("turbineId", "") + self.turbine_combo.addItem(display_name) + + if self.turbine_data: + QMessageBox.information(self, "成功", f"成功加载{len(self.turbine_data)}个机组") + else: + QMessageBox.warning(self, "警告", "没有找到机组数据") + else: + QMessageBox.warning(self, "警告", f"获取机组列表失败: {result.get('msg', '未知错误')}") + + except Exception as e: + QMessageBox.critical(self, "错误", f"获取机组列表时出错:\n{str(e)}") + + def validate_inputs(self): + if not self.project_id_input.text().strip(): + QMessageBox.warning(self, "输入错误", "请输入项目ID!") + return False + if not self.turbine_combo.currentText(): + QMessageBox.warning(self, "输入错误", "请选择机组!") + return False + if not self.part_name_combo.currentText(): + QMessageBox.warning(self, "输入错误", "请选择部件名称!") + return False + return True + + def submit_data(self): + if not self.validate_inputs(): + return + + selected_turbine = self.turbine_combo.currentText() + turbine_id = self.turbine_data.get(selected_turbine, "") + + part_data = { + "turbineId": turbine_id, + "partName": self.part_name_combo.currentText(), + "partCode": self.part_code_input.text().strip(), + "partType": self.part_type_combo.currentText(), + "partDesc": self.part_desc_input.toPlainText().strip(), + "partManufacturer": self.part_manufacturer_input.text().strip(), + "partModel": self.part_model_input.text().strip() + } + + reply = QMessageBox.question( + self, '确认提交', + '确定要提交这些部件信息吗?', + QMessageBox.Yes | QMessageBox.No, QMessageBox.No + ) + + if reply == QMessageBox.No: + return + + url = "http://pms.dtyx.net:9158/part" + headers = { + "Authorization": "null", + "Content-Type": "application/json" + } + + try: + response = requests.post(url, headers=headers, data=json.dumps(part_data)) + response.raise_for_status() + + result = response.json() + + # 创建自定义消息框显示大尺寸结果 + msg_box = QMessageBox(self) + msg_box.setWindowTitle("提交成功") + msg_box.setIcon(QMessageBox.Information) + msg_box.setText("部件信息提交成功!") + + text_edit = QTextEdit() + text_edit.setReadOnly(True) + text_edit.setPlainText(json.dumps(result, indent=2)) + text_edit.setMinimumSize(500, 300) + text_edit.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + + msg_box.layout().addWidget(text_edit, 1, 0, 1, msg_box.layout().columnCount()) + msg_box.setStyleSheet("QLabel{min-width: 400px;}") + msg_box.exec_() + + except Exception as e: + error_box = QMessageBox(self) + error_box.setWindowTitle("提交失败") + error_box.setIcon(QMessageBox.Critical) + error_box.setText("部件信息提交失败!") + + error_text = QTextEdit() + error_text.setReadOnly(True) + error_text.setPlainText(str(e)) + error_text.setMinimumSize(500, 150) + error_text.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + + error_box.layout().addWidget(error_text, 1, 0, 1, error_box.layout().columnCount()) + error_box.exec_() + + +if __name__ == "__main__": + app = QApplication(sys.argv) + font = QFont("Microsoft YaHei", 10) + app.setFont(font) + + window = PartAddWindow() + window.show() + sys.exit(app.exec_()) \ No newline at end of file diff --git a/picture_upload_2.py b/picture_upload_2.py new file mode 100644 index 0000000..360bdb7 --- /dev/null +++ b/picture_upload_2.py @@ -0,0 +1,791 @@ +import os +import sys +import requests +import json +from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, + QLabel, QLineEdit, QComboBox, QPushButton, QFileDialog, + QGroupBox, QDateTimeEdit, QDoubleSpinBox, QSpinBox, QTextEdit, + QTabWidget, QScrollArea, QMessageBox, QCalendarWidget, QGridLayout,QFormLayout) +from PyQt5.QtCore import Qt, QDateTime, QTimer +from PyQt5.QtGui import QFont, QIcon + + +class NoWheelComboBox(QComboBox): + def wheelEvent(self, event): + event.ignore() + + +class NoWheelSpinBox(QSpinBox): + def wheelEvent(self, event): + event.ignore() + + +class NoWheelDoubleSpinBox(QDoubleSpinBox): + def wheelEvent(self, event): + event.ignore() + + +class ImageUploaderApp(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("图片批量上传工具") + self.setWindowIcon(QIcon("upload_icon.png")) + self.setGeometry(100, 100, 900, 700) + + # 初始化参数 + self.base_url = "http://pms.dtyx.net:9158/image" + self.file_paths = [] + self.upload_in_progress = False + self.cancel_requested = False + self.batch_size = 20 # 每批次上传的图片数量 + + # 存储项目、机组、部件数据 + self.project_data = {} # {项目显示名称: 项目ID} + self.turbine_data = {} # {机组显示名称: 机组ID} + self.part_data = {} # {部件显示名称: 部件ID} + + # 当前选择的项目ID和机组ID + self.current_project_id = "" + self.current_turbine_id = "" + + # 初始化UI + self.init_ui() + self.load_project_list() + + def init_ui(self): + # 创建主窗口部件 + main_widget = QWidget() + self.setCentralWidget(main_widget) + + # 主布局 + main_layout = QVBoxLayout() + main_widget.setLayout(main_layout) + + # 创建标签页 + tab_widget = QTabWidget() + main_layout.addWidget(tab_widget) + + # 项目选择标签页 + project_tab = QWidget() + tab_widget.addTab(project_tab, "项目选择") + self.setup_project_tab(project_tab) + + # 图片参数标签页 + param_tab = QWidget() + tab_widget.addTab(param_tab, "图片参数") + self.setup_param_tab(param_tab) + + # 高级参数标签页 + advanced_tab = QWidget() + tab_widget.addTab(advanced_tab, "高级参数") + self.setup_advanced_tab(advanced_tab) + + # 底部按钮区域 + button_layout = QHBoxLayout() + + self.upload_btn = QPushButton("开始上传") + self.upload_btn.setFont(QFont("Arial", 12, QFont.Bold)) + self.upload_btn.setStyleSheet("background-color: #4CAF50; color: white; padding: 10px;") + self.upload_btn.clicked.connect(self.start_upload) + + self.clear_btn = QPushButton("清空选择") + self.clear_btn.setFont(QFont("Arial", 12)) + self.clear_btn.setStyleSheet("background-color: #f44336; color: white; padding: 10px;") + self.clear_btn.clicked.connect(self.clear_selection) + + button_layout.addWidget(self.upload_btn) + button_layout.addWidget(self.clear_btn) + main_layout.addLayout(button_layout) + + # 日志输出 + self.log_text = QTextEdit() + self.log_text.setReadOnly(True) + self.log_text.setFont(QFont("Consolas", 10)) + self.log_text.setStyleSheet("background-color: #f5f5f5;") + main_layout.addWidget(self.log_text) + + def setup_project_tab(self, tab): + layout = QVBoxLayout() + tab.setLayout(layout) + + # 项目选择 + project_group = QGroupBox("项目选择") + project_layout = QFormLayout() + + self.project_combo = NoWheelComboBox() + self.project_combo.currentIndexChanged.connect(self.on_project_selected) + project_layout.addRow(QLabel("项目名称*:"), self.project_combo) + + # 刷新项目按钮 + self.refresh_project_btn = QPushButton("刷新项目列表") + self.refresh_project_btn.clicked.connect(self.load_project_list) + project_layout.addRow(self.refresh_project_btn) + + project_group.setLayout(project_layout) + layout.addWidget(project_group) + + # 机组选择 + turbine_group = QGroupBox("机组选择") + turbine_layout = QFormLayout() + + self.turbine_combo = NoWheelComboBox() + self.turbine_combo.currentIndexChanged.connect(self.on_turbine_selected) + turbine_layout.addRow(QLabel("机组名称*:"), self.turbine_combo) + + # 刷新机组按钮 + self.refresh_turbine_btn = QPushButton("刷新机组列表") + self.refresh_turbine_btn.clicked.connect(lambda: self.load_turbine_list(self.current_project_id)) + turbine_layout.addRow(self.refresh_turbine_btn) + + turbine_group.setLayout(turbine_layout) + layout.addWidget(turbine_group) + + # 部件选择 + part_group = QGroupBox("部件选择") + part_layout = QFormLayout() + + self.part_combo = NoWheelComboBox() + part_layout.addRow(QLabel("部件名称*:"), self.part_combo) + + # 刷新部件按钮 + self.refresh_part_btn = QPushButton("刷新部件列表") + self.refresh_part_btn.clicked.connect( + lambda: self.load_part_list(self.current_project_id, self.current_turbine_id)) + part_layout.addRow(self.refresh_part_btn) + + part_group.setLayout(part_layout) + layout.addWidget(part_group) + + # 文件选择区域 + file_group = QGroupBox("图片选择") + file_layout = QVBoxLayout() + + self.select_files_btn = QPushButton("选择图片文件") + self.select_files_btn.setFont(QFont("Arial", 12)) + self.select_files_btn.setStyleSheet("background-color: #2196F3; color: white; padding: 8px;") + self.select_files_btn.clicked.connect(self.select_files) + file_layout.addWidget(self.select_files_btn) + + self.file_list_label = QLabel("已选择 0 个文件") + self.file_list_label.setFont(QFont("Arial", 10)) + file_layout.addWidget(self.file_list_label) + + file_group.setLayout(file_layout) + layout.addWidget(file_group) + + # 添加弹性空间 + layout.addStretch() + + def setup_param_tab(self, tab): + layout = QVBoxLayout() + tab.setLayout(layout) + + # 图片源选择 + source_group = QGroupBox("图片源设置") + source_layout = QVBoxLayout() + + self.source_combo = NoWheelComboBox() + self.source_combo.addItems(["图像采集", "外部工作", "内部工作", "防雷工作"]) + self.source_combo.setCurrentText("内部工作") + source_layout.addWidget(QLabel("图片源类型:")) + source_layout.addWidget(self.source_combo) + + source_group.setLayout(source_layout) + layout.addWidget(source_group) + + # 图片类型选择 + type_group = QGroupBox("图片类型设置") + type_layout = QVBoxLayout() + + self.type_combo = NoWheelComboBox() + self.type_combo.addItems(["", "缺陷影像", "典型影像", "其他影像"]) + type_layout.addWidget(QLabel("图片类型:")) + type_layout.addWidget(self.type_combo) + + type_group.setLayout(type_layout) + layout.addWidget(type_group) + + # 拍摄方式选择 + method_group = QGroupBox("拍摄方式设置") + method_layout = QVBoxLayout() + + self.method_combo = NoWheelComboBox() + self.method_combo.addItems(["", "无人机航拍", "手持相机拍摄"]) + method_layout.addWidget(QLabel("拍摄方式:")) + method_layout.addWidget(self.method_combo) + + method_group.setLayout(method_layout) + layout.addWidget(method_group) + + # 天气选择 + weather_group = QGroupBox("天气设置") + weather_layout = QVBoxLayout() + + self.weather_combo = NoWheelComboBox() + weather_options = [ + "", "晴天", "多云", "阴天", "小雨", "中雨", "大雨", "暴雨", + "阵雨", "雷阵雨", "雷电", "冰雹", "轻雾", "雾", "浓雾", + "霾", "雨夹雪", "小雪", "中雪", "大雪", "暴雪", "冻雨" + ] + self.weather_combo.addItems(weather_options) + weather_layout.addWidget(QLabel("天气情况:")) + weather_layout.addWidget(self.weather_combo) + + weather_group.setLayout(weather_layout) + layout.addWidget(weather_group) + + def setup_advanced_tab(self, tab): + scroll = QScrollArea() + scroll.setWidgetResizable(True) + tab.layout = QVBoxLayout(tab) + tab.layout.addWidget(scroll) + + container = QWidget() + scroll.setWidget(container) + + layout = QVBoxLayout() + container.setLayout(layout) + + # 采集员信息 + collector_group = QGroupBox("采集员信息") + collector_layout = QVBoxLayout() + + self.collector_id_input = QLineEdit() + self.collector_name_input = QLineEdit() + collector_layout.addWidget(QLabel("采集员ID:")) + collector_layout.addWidget(self.collector_id_input) + collector_layout.addWidget(QLabel("采集员姓名:")) + collector_layout.addWidget(self.collector_name_input) + + collector_group.setLayout(collector_layout) + layout.addWidget(collector_group) + + # 拍摄时间 + time_group = QGroupBox("拍摄时间") + time_layout = QVBoxLayout() + + # 开始时间 + start_time_group = QGroupBox("开始时间") + start_time_layout = QVBoxLayout() + + self.start_calendar = QCalendarWidget() + self.start_time_edit = QDateTimeEdit() + self.start_time_edit.setDisplayFormat("HH:mm:ss") + self.start_time_edit.setTime(QDateTime.currentDateTime().time()) + + start_time_layout.addWidget(self.start_calendar) + start_time_layout.addWidget(self.start_time_edit) + start_time_group.setLayout(start_time_layout) + + # 结束时间 + end_time_group = QGroupBox("结束时间") + end_time_layout = QVBoxLayout() + + self.end_calendar = QCalendarWidget() + self.end_time_edit = QDateTimeEdit() + self.end_time_edit.setDisplayFormat("HH:mm:ss") + self.end_time_edit.setTime(QDateTime.currentDateTime().time()) + + end_time_layout.addWidget(self.end_calendar) + end_time_layout.addWidget(self.end_time_edit) + end_time_group.setLayout(end_time_layout) + + time_layout.addWidget(start_time_group) + time_layout.addWidget(end_time_group) + time_group.setLayout(time_layout) + layout.addWidget(time_group) + + # 环境参数 + env_group = QGroupBox("环境参数") + env_layout = QGridLayout() + + self.humidity_spin = NoWheelSpinBox() + self.humidity_spin.setRange(0, 100) + self.humidity_spin.setSpecialValueText("未设置") + self.humidity_spin.setValue(0) + + self.temp_min_spin = NoWheelDoubleSpinBox() + self.temp_min_spin.setRange(-50, 50) + self.temp_min_spin.setSpecialValueText("未设置") + self.temp_min_spin.setValue(-50) + + self.temp_max_spin = NoWheelDoubleSpinBox() + self.temp_max_spin.setRange(-50, 50) + self.temp_max_spin.setSpecialValueText("未设置") + self.temp_max_spin.setValue(-50) + + self.wind_level_spin = NoWheelSpinBox() + self.wind_level_spin.setRange(0, 12) + self.wind_level_spin.setSpecialValueText("未设置") + self.wind_level_spin.setValue(0) + + self.distance_spin = NoWheelSpinBox() + self.distance_spin.setRange(0, 1000) + self.distance_spin.setSpecialValueText("未设置") + self.distance_spin.setValue(0) + + env_layout.addWidget(QLabel("湿度(%):"), 0, 0) + env_layout.addWidget(self.humidity_spin, 0, 1) + env_layout.addWidget(QLabel("最低温度(℃):"), 1, 0) + env_layout.addWidget(self.temp_min_spin, 1, 1) + env_layout.addWidget(QLabel("最高温度(℃):"), 2, 0) + env_layout.addWidget(self.temp_max_spin, 2, 1) + env_layout.addWidget(QLabel("风力等级:"), 3, 0) + env_layout.addWidget(self.wind_level_spin, 3, 1) + env_layout.addWidget(QLabel("拍摄距离(m):"), 4, 0) + env_layout.addWidget(self.distance_spin, 4, 1) + + env_group.setLayout(env_layout) + layout.addWidget(env_group) + + # 添加弹性空间 + layout.addStretch() + + def load_project_list(self): + """加载项目列表""" + url = "http://pms.dtyx.net:9158/project/list" + headers = { + "Authorization": "null", + "Content-Type": "application/x-www-form-urlencoded" + } + + try: + response = requests.get(url, headers=headers) + response.raise_for_status() + + result = response.json() + if result.get("success") and "data" in result: + self.project_data = {} + self.project_combo.clear() + + # 获取并排序项目数据 + projects = result["data"] + sorted_projects = sorted(projects, key=lambda x: x.get("projectName", "")) + + # 填充项目数据 + for project in sorted_projects: + display_name = f"{project.get('projectName', '')} (ID: {project.get('projectId', '')})" + self.project_data[display_name] = project.get("projectId", "") + self.project_combo.addItem(display_name) + + if self.project_data: + self.log_message(f"成功加载{len(self.project_data)}个项目") + else: + self.log_message("没有找到项目数据") + QMessageBox.warning(self, "警告", "没有找到项目数据") + else: + self.log_message(f"获取项目列表失败: {result.get('msg', '未知错误')}") + QMessageBox.warning(self, "警告", f"获取项目列表失败: {result.get('msg', '未知错误')}") + + except Exception as e: + self.log_message(f"获取项目列表时出错:\n{str(e)}") + QMessageBox.critical(self, "错误", f"获取项目列表时出错:\n{str(e)}") + + def load_turbine_list(self, project_id): + """加载机组列表""" + if not project_id: + QMessageBox.warning(self, "警告", "请先选择项目!") + return + + url = "http://pms.dtyx.net:9158/turbine/list" + headers = { + "Authorization": "null", + "Content-Type": "application/x-www-form-urlencoded" + } + + try: + params = {"projectId": project_id} + response = requests.get(url, headers=headers, params=params) + response.raise_for_status() + + result = response.json() + if result.get("success") and "data" in result: + self.turbine_data = {} + self.turbine_combo.clear() + + # 获取并排序机组数据 + turbines = result["data"] + sorted_turbines = sorted(turbines, key=lambda x: x.get("turbineName", "")) + + # 填充机组数据 + for turbine in sorted_turbines: + display_name = f"{turbine.get('turbineName', '')} (ID: {turbine.get('turbineId', '')})" + self.turbine_data[display_name] = turbine.get("turbineId", "") + self.turbine_combo.addItem(display_name) + + if self.turbine_data: + self.log_message(f"成功加载{len(self.turbine_data)}个机组") + else: + self.log_message("没有找到机组数据") + QMessageBox.warning(self, "警告", "没有找到机组数据") + else: + self.log_message(f"获取机组列表失败: {result.get('msg', '未知错误')}") + QMessageBox.warning(self, "警告", f"获取机组列表失败: {result.get('msg', '未知错误')}") + + except Exception as e: + self.log_message(f"获取机组列表时出错:\n{str(e)}") + QMessageBox.critical(self, "错误", f"获取机组列表时出错:\n{str(e)}") + + def load_part_list(self, project_id, turbine_id): + """加载部件列表""" + if not project_id or not turbine_id: + QMessageBox.warning(self, "警告", "请先选择项目和机组!") + return + + url = "http://pms.dtyx.net:9158/part/list" + headers = { + "Authorization": "null", + "Content-Type": "application/x-www-form-urlencoded" + } + + try: + params = { + "projectId": project_id, + "turbineId": turbine_id + } + response = requests.get(url, headers=headers, params=params) + response.raise_for_status() + + result = response.json() + if result.get("success") and "data" in result: + self.part_data = {} + self.part_combo.clear() + + # 获取并排序部件数据 + parts = result["data"] + sorted_parts = sorted(parts, key=lambda x: x.get("partName", "")) + + # 填充部件数据 + for part in sorted_parts: + display_name = f"{part.get('partName', '')} (ID: {part.get('partId', '')})" + self.part_data[display_name] = part.get("partId", "") + self.part_combo.addItem(display_name) + + if self.part_data: + self.log_message(f"成功加载{len(self.part_data)}个部件") + else: + self.log_message("没有找到部件数据") + QMessageBox.warning(self, "警告", "没有找到部件数据") + else: + self.log_message(f"获取部件列表失败: {result.get('msg', '未知错误')}") + QMessageBox.warning(self, "警告", f"获取部件列表失败: {result.get('msg', '未知错误')}") + + except Exception as e: + self.log_message(f"获取部件列表时出错:\n{str(e)}") + QMessageBox.critical(self, "错误", f"获取部件列表时出错:\n{str(e)}") + + def on_project_selected(self, index): + """项目选择变化事件""" + if index >= 0: + selected_project = self.project_combo.currentText() + self.current_project_id = self.project_data.get(selected_project, "") + self.log_message(f"已选择项目: {selected_project}") + self.load_turbine_list(self.current_project_id) + else: + self.current_project_id = "" + self.turbine_combo.clear() + self.part_combo.clear() + + def on_turbine_selected(self, index): + """机组选择变化事件""" + if index >= 0: + selected_turbine = self.turbine_combo.currentText() + self.current_turbine_id = self.turbine_data.get(selected_turbine, "") + self.log_message(f"已选择机组: {selected_turbine}") + self.load_part_list(self.current_project_id, self.current_turbine_id) + else: + self.current_turbine_id = "" + self.part_combo.clear() + + def select_files(self): + options = QFileDialog.Options() + files, _ = QFileDialog.getOpenFileNames( + self, "选择图片文件", "", + "图片文件 (*.jpg *.jpeg *.png *.bmp *.gif)", + options=options + ) + + if files: + self.file_paths = files + self.update_file_count() + self.log_message(f"已选择 {len(files)} 个图片文件") + + def clear_selection(self): + self.file_paths = [] + self.update_file_count() + self.log_message("已清空文件选择") + + def update_file_count(self): + """更新文件计数显示""" + self.file_list_label.setText(f"已选择 {len(self.file_paths)} 个文件") + + def log_message(self, message): + """记录日志信息""" + self.log_text.append(f"[{QDateTime.currentDateTime().toString('yyyy-MM-dd hh:mm:ss')}] {message}") + + def validate_inputs(self): + """验证输入是否完整""" + if not self.current_project_id: + QMessageBox.warning(self, "警告", "请选择项目!") + return False + if not self.current_turbine_id: + QMessageBox.warning(self, "警告", "请选择机组!") + return False + if not self.part_combo.currentText(): + QMessageBox.warning(self, "警告", "请选择部件!") + return False + if not self.file_paths: + QMessageBox.warning(self, "警告", "请选择要上传的图片文件!") + return False + return True + + def start_upload(self): + if self.upload_in_progress: + QMessageBox.warning(self, "警告", "已有上传任务在进行中!") + return + + if not self.validate_inputs(): + return + + self.upload_in_progress = True + self.cancel_requested = False + self.upload_btn.setEnabled(False) + self.clear_btn.setEnabled(False) + + # 使用QTimer在事件循环中启动上传,避免界面卡住 + QTimer.singleShot(100, self.upload_images) + + def upload_images(self): + try: + # 获取选择的部件ID + selected_part = self.part_combo.currentText() + part_id = self.part_data.get(selected_part, "") + if not part_id: + raise ValueError("无法获取部件ID") + + # 从日历和时间控件获取时间 + start_date = self.start_calendar.selectedDate() + start_time = self.start_time_edit.time() + start_datetime = QDateTime(start_date, start_time) + + end_date = self.end_calendar.selectedDate() + end_time = self.end_time_edit.time() + end_datetime = QDateTime(end_date, end_time) + + # 构建参数字典,只包含有值的参数 + params = { + "imageSource": ["collect", "out-work", "in-work", "lightning-protection-work"][ + self.source_combo.currentIndex()], + } + + # 添加可选参数 + if self.type_combo.currentIndex() > 0: + params["imageType"] = ["", "DEFECT", "TYPICAL", "OTHER"][self.type_combo.currentIndex()] + + if self.method_combo.currentIndex() > 0: + params["shootingMethod"] = ["", "UAV", "HANDHELD_CAMERA"][self.method_combo.currentIndex()] + + if self.weather_combo.currentIndex() > 0: + weather_options = [ + "", "SUNNY", "CLOUDY", "OVERCAST", "LIGHT_RAIN", "MODERATE_RAIN", "HEAVY_RAIN", + "CLOUDBURST", "SHOWER", "THUNDERSHOWER", "THUNDER", "HAIL", "LIGHT_FOG", + "FOG", "THICK_FOG", "HAZE", "SLEET", "LIGHT_SNOW", "MODERATE_SNOW", + "HEAVY_SNOW", "BLIZZARD", "FREEZING_RAIN" + ] + params["weather"] = weather_options[self.weather_combo.currentIndex()] + + collector_id = self.collector_id_input.text().strip() + if collector_id: + params["collectorId"] = collector_id + + collector_name = self.collector_name_input.text().strip() + if collector_name: + params["collectorName"] = collector_name + + params["shootingTimeBegin"] = start_datetime.toString("yyyy-MM-dd hh:mm:ss") + params["shootingTimeEnd"] = end_datetime.toString("yyyy-MM-dd hh:mm:ss") + + # 环境参数 + if self.humidity_spin.value() > 0: + params["humidness"] = self.humidity_spin.value() + + if self.temp_min_spin.value() > -50: + params["temperatureMin"] = self.temp_min_spin.value() + + if self.temp_max_spin.value() > -50: + params["temperatureMax"] = self.temp_max_spin.value() + + if self.wind_level_spin.value() > 0: + params["windLevel"] = self.wind_level_spin.value() + + if self.distance_spin.value() > 0: + params["shootingDistance"] = self.distance_spin.value() + + # 构建URL + url = f"{self.base_url}/{params['imageSource']}/upload-batch/{part_id}" + + # 计算总批次数 + total_files = len(self.file_paths) + total_batches = (total_files + self.batch_size - 1) // self.batch_size + + self.log_message(f"开始上传 {total_files} 张图片,分为 {total_batches} 批,每批 {self.batch_size} 张...") + self.log_message(f"请求URL: {url}") + self.log_message(f"请求参数: {params}") + + # 显示上传进度对话框 + self.progress = QMessageBox(self) + self.progress.setWindowTitle("上传中") + self.progress.setText(f"正在上传 {total_files} 张图片 (0/{total_batches} 批),请稍候...") + self.progress.setStandardButtons(QMessageBox.Cancel) + self.progress.buttonClicked.connect(self.cancel_upload) + self.progress.show() + + # 确保UI更新 + QApplication.processEvents() + + # 分批上传 + success_count = 0 + fail_count = 0 + + for batch_num in range(total_batches): + if self.cancel_requested: + self.log_message("用户取消了上传操作") + break + + # 获取当前批次的文件 + start_idx = batch_num * self.batch_size + end_idx = min((batch_num + 1) * self.batch_size, total_files) + batch_files = self.file_paths[start_idx:end_idx] + + # 准备图片列表参数 + batch_params = params.copy() + for i, file_path in enumerate(batch_files): + file_name = os.path.basename(file_path) + batch_params[f"imageList[{i}].imageName"] = file_name + batch_params[f"imageList[{i}].imagePath"] = file_path + + if "imageType" in batch_params: + batch_params[f"imageList[{i}].imageType"] = batch_params["imageType"] + if "shootingMethod" in batch_params: + batch_params[f"imageList[{i}].shootingMethod"] = batch_params["shootingMethod"] + if "weather" in batch_params: + batch_params[f"imageList[{i}].weather"] = batch_params["weather"] + + # 准备文件数据 + files = [] + for file_path in batch_files: + file_name = os.path.basename(file_path) + files.append(('files', (file_name, open(file_path, 'rb'), 'image/jpeg'))) + + self.log_message(f"正在上传第 {batch_num + 1}/{total_batches} 批 ({len(batch_files)} 张图片)...") + + # 更新进度对话框 + self.progress.setText(f"正在上传 {total_files} 张图片 ({batch_num + 1}/{total_batches} 批),请稍候...") + QApplication.processEvents() + + try: + response = requests.post( + url, + headers={"Authorization": "null"}, + params=batch_params, + files=files, + timeout=30 # 设置超时时间为30秒 + ) + + if response.status_code == 200: + self.log_message(f"第 {batch_num + 1} 批上传成功!") + success_count += len(batch_files) + else: + self.log_message(f"第 {batch_num + 1} 批上传失败,状态码: {response.status_code}") + self.log_message(f"响应内容: {response.text}") + fail_count += len(batch_files) + + except Exception as e: + self.log_message(f"第 {batch_num + 1} 批上传过程中发生错误: {str(e)}") + fail_count += len(batch_files) + + finally: + # 确保所有文件都被关闭 + for file in files: + file[1][1].close() + + self.progress.close() + + # 上传结果统计 + if self.cancel_requested: + self.log_message(f"上传已取消,已上传 {success_count} 张,失败 {fail_count} 张") + QMessageBox.information(self, "上传取消", f"上传已取消,已上传 {success_count} 张,失败 {fail_count} 张") + else: + self.log_message(f"上传完成! 成功 {success_count} 张,失败 {fail_count} 张") + if fail_count == 0: + QMessageBox.information(self, "成功", f"所有图片上传成功! 共 {success_count} 张") + else: + QMessageBox.warning(self, "完成", f"上传完成! 成功 {success_count} 张,失败 {fail_count} 张") + + # 上传完成后自动清空已选文件 + if not self.cancel_requested and success_count > 0: + self.clear_selection() + + except Exception as e: + self.log_message(f"上传过程中发生错误: {str(e)}") + QMessageBox.critical(self, "错误", f"上传过程中发生错误: {str(e)}") + + finally: + self.reset_upload_state() + + def reset_upload_state(self): + """重置上传状态""" + self.upload_in_progress = False + self.cancel_requested = False + self.upload_btn.setEnabled(True) + self.clear_btn.setEnabled(True) + + def cancel_upload(self): + """取消上传操作""" + self.cancel_requested = True + self.progress.close() + + +if __name__ == "__main__": + app = QApplication(sys.argv) + + # 设置全局样式 + app.setStyle("Fusion") + app.setStyleSheet(""" + QMainWindow { + background-color: #f5f5f5; + } + QGroupBox { + font-weight: bold; + border: 1px solid #ccc; + border-radius: 5px; + margin-top: 10px; + padding-top: 15px; + } + QGroupBox::title { + subcontrol-origin: margin; + left: 10px; + padding: 0 3px; + } + QLabel { + font-size: 12px; + } + QComboBox, QLineEdit, QDateTimeEdit, QSpinBox, QDoubleSpinBox { + padding: 5px; + border: 1px solid #ccc; + border-radius: 3px; + min-width: 200px; + } + QTextEdit { + border: 1px solid #ccc; + border-radius: 3px; + } + QCalendarWidget { + border: 1px solid #ccc; + border-radius: 3px; + } + """) + + window = ImageUploaderApp() + window.show() + sys.exit(app.exec_()) \ No newline at end of file