498 lines
18 KiB
Python
498 lines
18 KiB
Python
import os
|
||
import sys
|
||
import requests
|
||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
||
QLabel, QLineEdit, QComboBox, QPushButton, QFileDialog,
|
||
QGroupBox, QDateTimeEdit, QDoubleSpinBox, QSpinBox, QTextEdit,
|
||
QTabWidget, QScrollArea, QMessageBox, QCalendarWidget,QGridLayout)
|
||
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, 850, 650)
|
||
|
||
# 初始化参数
|
||
self.base_url = "http://pms.dtyx.net:9158/image"
|
||
self.default_part_id = "a3b4b81c5973a1895a4d95d50dd8351c"
|
||
self.file_paths = []
|
||
self.upload_in_progress = False
|
||
|
||
# 初始化UI
|
||
self.init_ui()
|
||
|
||
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)
|
||
|
||
# 基本参数标签页
|
||
basic_tab = QWidget()
|
||
tab_widget.addTab(basic_tab, "基本参数")
|
||
self.setup_basic_tab(basic_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_basic_tab(self, tab):
|
||
layout = QVBoxLayout()
|
||
tab.setLayout(layout)
|
||
|
||
# Part ID设置
|
||
part_id_group = QGroupBox("部件ID设置")
|
||
part_id_layout = QVBoxLayout()
|
||
|
||
self.part_id_input = QLineEdit(self.default_part_id)
|
||
part_id_layout.addWidget(QLabel("部件ID:"))
|
||
part_id_layout.addWidget(self.part_id_input)
|
||
|
||
part_id_group.setLayout(part_id_layout)
|
||
layout.addWidget(part_id_group)
|
||
|
||
# 图片源选择
|
||
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)
|
||
|
||
# 文件选择按钮
|
||
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)
|
||
layout.addWidget(self.select_files_btn)
|
||
|
||
# 选中的文件列表
|
||
self.file_list_label = QLabel("已选择 0 个文件")
|
||
self.file_list_label.setFont(QFont("Arial", 10))
|
||
layout.addWidget(self.file_list_label)
|
||
|
||
# 添加弹性空间
|
||
layout.addStretch()
|
||
|
||
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 select_files(self):
|
||
options = QFileDialog.Options()
|
||
files, _ = QFileDialog.getOpenFileNames(
|
||
self, "选择图片文件", "",
|
||
"图片文件 (*.jpg *.jpeg *.png *.bmp *.gif)",
|
||
options=options
|
||
)
|
||
|
||
if files:
|
||
self.file_paths = files
|
||
self.file_list_label.setText(f"已选择 {len(files)} 个文件")
|
||
self.log_message(f"已选择 {len(files)} 个图片文件")
|
||
|
||
def clear_selection(self):
|
||
self.file_paths = []
|
||
self.file_list_label.setText("已选择 0 个文件")
|
||
self.log_message("已清空文件选择")
|
||
|
||
def log_message(self, message):
|
||
self.log_text.append(f"[{QDateTime.currentDateTime().toString('yyyy-MM-dd hh:mm:ss')}] {message}")
|
||
|
||
def start_upload(self):
|
||
if self.upload_in_progress:
|
||
QMessageBox.warning(self, "警告", "已有上传任务在进行中!")
|
||
return
|
||
|
||
if not self.file_paths:
|
||
QMessageBox.warning(self, "警告", "请先选择要上传的图片文件!")
|
||
return
|
||
|
||
self.upload_in_progress = True
|
||
self.upload_btn.setEnabled(False)
|
||
self.clear_btn.setEnabled(False)
|
||
|
||
# 使用QTimer在事件循环中启动上传,避免界面卡住
|
||
QTimer.singleShot(100, self.upload_images)
|
||
|
||
def upload_images(self):
|
||
try:
|
||
# 准备参数
|
||
part_id = self.part_id_input.text().strip() or self.default_part_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}"
|
||
|
||
# 准备图片列表参数
|
||
for i, file_path in enumerate(self.file_paths):
|
||
file_name = os.path.basename(file_path)
|
||
params[f"imageList[{i}].imageName"] = file_name
|
||
params[f"imageList[{i}].imagePath"] = file_path
|
||
|
||
if "imageType" in params:
|
||
params[f"imageList[{i}].imageType"] = params["imageType"]
|
||
if "shootingMethod" in params:
|
||
params[f"imageList[{i}].shootingMethod"] = params["shootingMethod"]
|
||
if "weather" in params:
|
||
params[f"imageList[{i}].weather"] = params["weather"]
|
||
|
||
# 准备文件数据
|
||
files = []
|
||
for file_path in self.file_paths:
|
||
file_name = os.path.basename(file_path)
|
||
files.append(('files', (file_name, open(file_path, 'rb'), 'image/jpeg')))
|
||
|
||
self.log_message(f"开始上传 {len(self.file_paths)} 张图片...")
|
||
self.log_message(f"请求URL: {url}")
|
||
self.log_message(f"请求参数: {params}")
|
||
|
||
# 显示上传进度对话框
|
||
self.progress = QMessageBox(self)
|
||
self.progress.setWindowTitle("上传中")
|
||
self.progress.setText(f"正在上传 {len(self.file_paths)} 张图片,请稍候...")
|
||
self.progress.setStandardButtons(QMessageBox.Cancel)
|
||
self.progress.buttonClicked.connect(self.cancel_upload)
|
||
self.progress.show()
|
||
|
||
# 确保UI更新
|
||
QApplication.processEvents()
|
||
|
||
response = requests.post(
|
||
url,
|
||
headers={"Authorization": "null"},
|
||
params=params,
|
||
files=files
|
||
)
|
||
|
||
self.progress.close()
|
||
|
||
if response.status_code == 200:
|
||
self.log_message("上传成功!")
|
||
self.log_message(f"响应数据: {response.text}")
|
||
QMessageBox.information(self, "成功", "图片上传成功!")
|
||
else:
|
||
self.log_message(f"上传失败,状态码: {response.status_code}")
|
||
self.log_message(f"响应内容: {response.text}")
|
||
QMessageBox.critical(self, "错误", f"上传失败: {response.text}")
|
||
|
||
except Exception as e:
|
||
self.log_message(f"上传过程中发生错误: {str(e)}")
|
||
QMessageBox.critical(self, "错误", f"上传过程中发生错误: {str(e)}")
|
||
|
||
finally:
|
||
# 确保所有文件都被关闭
|
||
if 'files' in locals():
|
||
for file in files:
|
||
file[1][1].close()
|
||
|
||
self.upload_in_progress = False
|
||
self.upload_btn.setEnabled(True)
|
||
self.clear_btn.setEnabled(True)
|
||
|
||
def cancel_upload(self):
|
||
self.upload_in_progress = False
|
||
self.log_message("用户取消了上传操作")
|
||
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_()) |