791 lines
31 KiB
Python
791 lines
31 KiB
Python
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_()) |