2818 lines
133 KiB
Python
2818 lines
133 KiB
Python
from pathlib import Path
|
||
import os,time,json,sys
|
||
|
||
from PySide6.QtGui import QPixmap
|
||
from PySide6.QtWidgets import (QDialog, QVBoxLayout, QListWidget, QPushButton,
|
||
QHBoxLayout, QMessageBox, QFileDialog,
|
||
)
|
||
from PySide6 import QtCore, QtWidgets
|
||
from PySide6.QtCore import QDir, QSettings
|
||
|
||
from info_core.get_pictures import get_picture_nums, collect_defect_data
|
||
from info_core.MyQtClass import DLabel, AddProjectDialog, SmartDropdown
|
||
|
||
from ui.外部报告生成_初始化_ui import *
|
||
from ui.外部报告生成_内容填写_ui import *
|
||
|
||
class MainWindow(QtWidgets.QMainWindow):
|
||
project_info_received = QtCore.Signal(object) # 定义一个信号
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.ui = Ui_Form() # 创建UI实例
|
||
self.ui.setupUi(self) # 设置UI
|
||
# 存储结果的变量
|
||
self.project_info = None
|
||
# 连接信号
|
||
self.ui.file_dir_button.clicked.connect(self.get_file_dir)
|
||
self.ui.chooseproject.clicked.connect(self.get_project_info)
|
||
self.project_info_received.connect(self.handle_project_info) # 接收信号
|
||
self.ui.chooseproject_2.clicked.connect(self.open_saved_project)
|
||
self.ui.add_project.clicked.connect(self.add_project_info)
|
||
self.ui.edit_project.clicked.connect(self.edit_project_info)
|
||
# 获取项目目录
|
||
if getattr(sys, 'frozen', False):
|
||
self.project_dir = os.path.dirname(sys.executable)
|
||
else:
|
||
self.project_dir = os.path.dirname(os.path.abspath(__file__))
|
||
self.ui.file_dir.setText(self.project_dir)
|
||
self.ui.file_dir.setPlaceholderText('选择图片目录...')
|
||
self.ui.lineEdit.setText(self.project_dir)
|
||
self.ui.file_dir.setPlaceholderText('选择保存的项目...')
|
||
self.show()
|
||
self.init_project_info()
|
||
self.open_init()
|
||
print(f'初始化窗口初始化完毕,当前项目目录:{self.project_dir}')
|
||
|
||
def init_project_info(self):
|
||
"""从默认文件夹初始化项目信息"""
|
||
self.project_folder = os.path.join(self.project_dir, 'project')
|
||
project_folder = os.path.join(self.project_dir, 'project')
|
||
|
||
if os.path.exists(project_folder) and os.path.isdir(project_folder):
|
||
json_files = [f for f in os.listdir(project_folder) if f.endswith('.json')]
|
||
if json_files:
|
||
self.ui.projects.addItems([f.split('.json')[0] for f in json_files])
|
||
else:
|
||
QMessageBox.warning(self, "警告", "project文件夹中没有找到.json文件,请先手动添加一个项目信息")
|
||
else:
|
||
QMessageBox.warning(self, "警告", "project文件夹不在当前程序根目录下,请确认后选择项目基本信息文件夹")
|
||
self.choose_project_folder()
|
||
|
||
def choose_project_folder(self):
|
||
"""让用户选择保存了基本项目信息的文件夹"""
|
||
folder_path = QFileDialog.getExistingDirectory(None, "选择包含基本项目信息的文件夹")
|
||
if folder_path:
|
||
json_files = [f for f in os.listdir(folder_path) if f.endswith('.json')]
|
||
if json_files:
|
||
self.ui.projects.addItems(json_files)
|
||
self.project_folder = folder_path
|
||
else:
|
||
print("所选文件夹中没有找到.json文件")
|
||
else:
|
||
print("未选择文件夹")
|
||
|
||
def edit_project_info(self):
|
||
"""更改项目信息"""
|
||
dialog = AddProjectDialog(self.project_folder, self, self.get_current_project_path())
|
||
dialog.exec()
|
||
|
||
def add_project_info(self):
|
||
"""添加新的项目信息"""
|
||
dialog = AddProjectDialog(self.project_folder, self)
|
||
dialog.exec()
|
||
|
||
def get_current_project_path(self):
|
||
"""获取当前选中的项目文件路径"""
|
||
if not hasattr(self, 'project_folder'):
|
||
return None # 或者 raise 一个异常
|
||
|
||
current_project = self.ui.projects.currentText()
|
||
if not current_project:
|
||
return None
|
||
|
||
if self.project_folder.endswith('.json'):
|
||
return self.project_folder # 如果是文件路径,直接返回
|
||
# 拼接完整文件路径
|
||
file_path = os.path.join(self.project_folder, f"{current_project}.json")
|
||
return file_path
|
||
|
||
def open_saved_project(self):
|
||
# 确定保存目录路径
|
||
dir = self.ui.lineEdit.text()
|
||
|
||
# 创建对话框
|
||
dialog = QDialog(self)
|
||
dialog.setWindowTitle("打开保存的项目")
|
||
dialog.resize(600, 300)
|
||
|
||
# 主布局
|
||
layout = QVBoxLayout()
|
||
|
||
# 文件列表
|
||
self.file_list = QListWidget()
|
||
|
||
# 按钮布局
|
||
button_layout = QHBoxLayout()
|
||
|
||
# 浏览按钮
|
||
browse_btn = QPushButton("浏览")
|
||
browse_btn.clicked.connect(self.browse_directory)
|
||
|
||
# 打开按钮
|
||
open_btn = QPushButton("打开")
|
||
open_btn.clicked.connect(lambda: self.on_open_project(dialog))
|
||
|
||
# 刷新按钮
|
||
refresh_btn = QPushButton("刷新")
|
||
refresh_btn.clicked.connect(lambda: self.refresh_file_list(dir))
|
||
|
||
# 取消按钮
|
||
cancel_btn = QPushButton("取消")
|
||
cancel_btn.clicked.connect(dialog.reject)
|
||
|
||
button_layout.addWidget(browse_btn)
|
||
button_layout.addWidget(open_btn)
|
||
button_layout.addWidget(refresh_btn)
|
||
button_layout.addWidget(cancel_btn)
|
||
|
||
# 添加部件到主布局
|
||
layout.addWidget(self.file_list)
|
||
layout.addLayout(button_layout)
|
||
|
||
dialog.setLayout(layout)
|
||
|
||
# 初始加载文件列表
|
||
self.refresh_file_list(dir)
|
||
|
||
# 双击打开
|
||
self.file_list.itemDoubleClicked.connect(lambda: self.on_open_project(dialog))
|
||
|
||
return dialog.exec()
|
||
|
||
def browse_directory(self):
|
||
# 使用QFileDialog来选择目录
|
||
options = QFileDialog.Options()
|
||
options |= QFileDialog.ShowDirsOnly
|
||
dir = QFileDialog.getExistingDirectory(self, "选择目录", "", options=options)
|
||
if dir:
|
||
self.ui.lineEdit.setText(dir) # 更新保存目录路径
|
||
self.refresh_file_list(dir) # 刷新文件列表
|
||
|
||
|
||
def refresh_file_list(self, path):
|
||
"""刷新文件列表"""
|
||
self.file_list.clear()
|
||
|
||
# 检查目录是否存在
|
||
if not os.path.exists(path):
|
||
QMessageBox.warning(self, "警告", "保存目录不存在!")
|
||
return
|
||
|
||
# 获取目录下所有文件
|
||
dir = QDir(path)
|
||
files = dir.entryList(QDir.Files)
|
||
|
||
# 添加到列表
|
||
for file in files:
|
||
self.file_list.addItem(file)
|
||
|
||
def on_open_project(self, dialog):
|
||
"""打开选中的项目"""
|
||
selected_items = self.file_list.selectedItems()
|
||
|
||
if not selected_items:
|
||
QMessageBox.warning(self, "警告", "请先选择一个项目文件!")
|
||
return
|
||
|
||
filename = selected_items[0].text()
|
||
filepath = os.path.join(self.ui.lineEdit.text(), filename)
|
||
|
||
# 这里调用你的加载项目函数,传递文件路径
|
||
# 例如: self.load_project(filepath)
|
||
dialog.accept() # 关闭对话框
|
||
self.load_project(filepath)
|
||
|
||
# 返回文件路径,可以在外部调用load_project
|
||
return filepath
|
||
|
||
def load_project(self, filepath):
|
||
self.close()
|
||
with open(filepath, 'r') as f:
|
||
data = json.load(f)
|
||
|
||
dir = data['picture_dir']
|
||
print('获取保存记录信息中...')
|
||
if 'waibu_jiancha' in data.keys():
|
||
print('当前报告包含外部')
|
||
self.ui.ifwaibu.setChecked(data['waibu_jiancha'])
|
||
if 'neibu_jiancha' in data.keys():
|
||
print('当前报告包含内部')
|
||
self.ui.ifneibu.setChecked(data['neibu_jiancha'])
|
||
if 'fanglei_jiancha' in data.keys():
|
||
print('当前报告包含防雷')
|
||
self.ui.iffanglei.setChecked(data['fanglei_jiancha'])
|
||
|
||
self.ui.file_dir.setText(dir)
|
||
global Picture_dir
|
||
Picture_dir = dir
|
||
|
||
self.ui.comboBox.setCurrentIndex(data['get_info_mode'] if 'get_info_mode' in data.keys() else 0)
|
||
info = get_xiangmu_base_info(dir, None, None)
|
||
info.project_dir = self.project_dir
|
||
|
||
self.contentfill = contentfill_window(info, self)
|
||
self.contentfill.show()
|
||
self.contentfill.load_project(filepath)
|
||
|
||
def get_file_dir(self):
|
||
default_dir = self.ui.file_dir.text()
|
||
if not os.path.exists(default_dir): # 检查当前目录是否存在
|
||
default_dir = os.path.abspath(os.sep) # 如果不存在,则使用根目录
|
||
|
||
file_path = QtWidgets.QFileDialog.getExistingDirectory(
|
||
self,
|
||
"选择文件夹",
|
||
default_dir, # 使用检查后的默认目录
|
||
QtWidgets.QFileDialog.ShowDirsOnly | QtWidgets.QFileDialog.DontResolveSymlinks
|
||
)
|
||
if file_path:
|
||
self.ui.file_dir.setText(file_path)
|
||
|
||
def get_project_info(self):
|
||
if self.ui.file_dir.text():
|
||
project = self.ui.projects.currentText()
|
||
self.project_info = get_xiangmu_base_info(self.ui.file_dir.text(), project, self.project_folder)
|
||
self.project_info_received.emit(self.project_info) # 发送信号
|
||
return self.project_info
|
||
else:
|
||
QtWidgets.QMessageBox.warning(self, "警告", "请先选择图片目录")
|
||
return None
|
||
def handle_project_info(self, info):
|
||
if info:
|
||
self.close()
|
||
global Picture_dir
|
||
Picture_dir = self.ui.file_dir.text()
|
||
info.project_dir = self.project_dir
|
||
self.contentfill = contentfill_window(info, self)
|
||
self.contentfill.show()
|
||
|
||
def open_init(self):
|
||
settings = QSettings('baogao', 'baogao')
|
||
|
||
self.ui.ifwaibu.setChecked(settings.value("ifwaibu", False, type=bool))
|
||
self.ui.ifneibu.setChecked(settings.value("ifneibu", False, type=bool))
|
||
self.ui.iffanglei.setChecked(settings.value("iffanglei", False, type=bool))
|
||
|
||
self.ui.comboBox.setCurrentIndex(settings.value("generate_mode", 0, type=int))
|
||
self.ui.lineEdit.setText(settings.value("project_save_dir", "", type=str))
|
||
self.ui.file_dir.setText(settings.value("picture_dir", "", type=str))
|
||
if self.ui.lineEdit.text() == "":
|
||
self.ui.lineEdit.setText(self.project_dir)
|
||
if self.ui.file_dir.text() == "":
|
||
self.ui.file_dir.setText(self.project_dir)
|
||
|
||
def closeEvent(self, event):
|
||
settings = QSettings('baogao', 'baogao')
|
||
|
||
settings.setValue("ifwaibu", self.ui.ifwaibu.isChecked())
|
||
settings.setValue("ifneibu", self.ui.ifneibu.isChecked())
|
||
settings.setValue("iffanglei", self.ui.iffanglei.isChecked())
|
||
|
||
settings.setValue("generate_mode", self.ui.comboBox.currentIndex())
|
||
settings.setValue("project_save_dir", self.ui.lineEdit.text())
|
||
settings.setValue("picture_dir", os.path.dirname(self.ui.file_dir.text())) #获取file_dir的上一级路径
|
||
|
||
return super().closeEvent(event)
|
||
|
||
class contentfill_window(QtWidgets.QMainWindow):
|
||
global jituan_name, fengchang_name, jizu_type, company_name_yi, company_name_jia, project_location, fuzeren, phone_fuzeren, Y1, Y2, Y3, xiangmuguige
|
||
|
||
def __init__(self, info, mainwindow : MainWindow):
|
||
super().__init__()
|
||
self.Picture_dir = info.Picture_dir
|
||
self.project_dir = info.project_dir
|
||
self.Y1 = info.Y1
|
||
self.Y2 = info.Y2
|
||
self.Y3 = info.Y3
|
||
self.get_info_mode = mainwindow.ui.comboBox.currentIndex()
|
||
|
||
self.has_load_huizong = False
|
||
self.has_load_quexian = False
|
||
|
||
self.ifwaibu = mainwindow.ui.ifwaibu.isChecked()
|
||
self.ifneibu = mainwindow.ui.ifneibu.isChecked()
|
||
self.iffanglei = mainwindow.ui.iffanglei.isChecked()
|
||
|
||
self.ui = Ui_MainWindow()
|
||
self.ui.setupUi(self)
|
||
|
||
self._init_base_info(info)
|
||
|
||
self._init_cover_page()
|
||
|
||
self._init_project_overview()
|
||
|
||
self.start_init_check_method()
|
||
|
||
self._init_inspection_info()
|
||
|
||
self._init_inspection_content()
|
||
|
||
self.ui.conclusion.setPlaceholderText("""例:
|
||
1、因海上风电叶片运行环境恶劣、空气盐碱度高,叶片前缘合模缝区域及PS面(迎风面)涂层易受腐蚀,建议定期观察维护。
|
||
2、经无人机近距离外观检查发现H3-08#机位Y200220AF叶片PS面距叶根20m处发现一处裂纹,损伤长度轴向3m,该缺陷经我方判定为严重缺陷,建议尽快结安排对该机组停机并结合其他检查手段(如人工打磨)进一步勘查并决定维修处置方案,防止风险进一步升级。
|
||
3、经无人机近距离外观检查未发现H3-08#机位Y200249AF、Y200250AF叶片有明显影响机组正常运行的外部缺陷。""")
|
||
self.ui.conclusion_company.setText(company_name_yi)
|
||
self.ui.conclusion_date.setText(time.strftime("%Y年%m月%d日", time.localtime()))
|
||
self.ui.save_dir.setText(os.path.join(self.project_dir, '报告数据保存'))
|
||
self.ui.output_dir.setText(os.path.join(self.project_dir, '报告输出'))
|
||
self.ui.muban_dir.setText(os.path.join(self.project_dir,'报告模板'))
|
||
self.ui.bianzhishijian.setText(self.ui.calendarWidget.selectedDate().toString('yyyy/MM/dd'))
|
||
self.ui.conclusion_date.setText(self.ui.calendarWidget.selectedDate().toString('yyyy/MM/dd'))
|
||
|
||
self.total_picture_dir = None
|
||
|
||
self.ui.geregate_button.clicked.connect(self.start_generate_report)
|
||
|
||
self.ui.tabWidget.setCurrentIndex(0)
|
||
|
||
self.ui.label_45.setText(f"图片路径:{self.Picture_dir}")
|
||
|
||
self.has_init_inspection = False
|
||
|
||
self._init_connect()
|
||
|
||
def connect_calendar_to_lineedit_via_button(self, button: QPushButton, lineedit: QLineEdit):
|
||
"""
|
||
通过按钮触发日历选择,确认后更新LineEdit的值
|
||
|
||
参数:
|
||
button: QPushButton 触发按钮
|
||
lineedit: QLineEdit 文本输入框组件
|
||
"""
|
||
# 设置LineEdit为禁用状态
|
||
lineedit.setDisabled(True)
|
||
|
||
def show_calendar_dialog():
|
||
from PySide6.QtWidgets import QDialogButtonBox
|
||
# 创建对话框
|
||
dialog = QDialog()
|
||
dialog.setWindowTitle("选择日期")
|
||
layout = QVBoxLayout()
|
||
|
||
# 创建日历组件
|
||
calendar = QCalendarWidget()
|
||
layout.addWidget(calendar)
|
||
|
||
# 创建确认按钮
|
||
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||
button_box.accepted.connect(dialog.accept)
|
||
button_box.rejected.connect(dialog.reject)
|
||
layout.addWidget(button_box)
|
||
|
||
dialog.setLayout(layout)
|
||
|
||
# 如果用户确认选择
|
||
if dialog.exec() == QDialog.Accepted:
|
||
selected_date = calendar.selectedDate()
|
||
date_str = selected_date.toString("yyyy/MM/dd")
|
||
lineedit.setText(date_str)
|
||
|
||
# 连接按钮点击事件到显示日历对话框
|
||
button.clicked.connect(show_calendar_dialog)
|
||
|
||
def closeEvent(self, event):
|
||
# 创建一个QMessageBox实例
|
||
msg_box = QMessageBox(self)
|
||
msg_box.setWindowTitle('保存进度')
|
||
msg_box.setText('是否保存当前报告修改进度?')
|
||
# 添加自定义按钮
|
||
save_button = msg_box.addButton("保存", QMessageBox.YesRole)
|
||
no_save_button = msg_box.addButton("不保存", QMessageBox.NoRole)
|
||
cancel_button = msg_box.addButton("取消", QMessageBox.RejectRole)
|
||
|
||
msg_box.setDefaultButton(save_button)
|
||
msg_box.exec() # 执行对话框
|
||
|
||
clicked_button = msg_box.clickedButton() # 获取被点击的按钮
|
||
|
||
if clicked_button == save_button:
|
||
self.ui.messagebrowser.append("保存数据")
|
||
result = self.save_form_data()
|
||
if result == False:
|
||
event.ignore() # 阻止关闭
|
||
return
|
||
event.accept() # 允许关闭
|
||
elif clicked_button == no_save_button:
|
||
event.accept() # 允许关闭
|
||
else: # 取消或其他情况
|
||
event.ignore() # 阻止关闭
|
||
|
||
def save_textbrowser_content(self ,text_browser, file_path: str):
|
||
"""
|
||
保存QTextBrowser内容到文件
|
||
:param text_browser: QTextBrowser实例
|
||
:param file_path: 要保存的文件路径
|
||
"""
|
||
# 获取全部文本
|
||
full_text = text_browser.toPlainText()
|
||
temp = ''
|
||
lines = ''
|
||
for line in full_text.split('\n'):
|
||
if temp == line:
|
||
continue
|
||
temp = line
|
||
lines += line + '\n'
|
||
|
||
# 写入文件
|
||
with open(file_path, 'w', encoding='utf-8') as f:
|
||
f.write(full_text)
|
||
|
||
def start_init_check_method(self):
|
||
# 获取scrollArea的内容区域
|
||
area = self.ui.scrollAreaWidgetContents_4
|
||
area.setLayout(QVBoxLayout())
|
||
|
||
# 创建标签和对应的TextEdit框
|
||
labels_and_textedits = {
|
||
"工作内容": QTextEdit(),
|
||
"人员配置": QTextEdit(),
|
||
"设备配置": QTextEdit(),
|
||
"施工方案": QTextEdit(),
|
||
"备注": QTextEdit()
|
||
}
|
||
|
||
# 设置每个TextEdit的一些基本属性
|
||
for label, textedit in labels_and_textedits.items():
|
||
textedit.setPlaceholderText(f"请输入{label}...")
|
||
textedit.setMinimumHeight(100) # 设置最小高度
|
||
textedit.setStyleSheet("""
|
||
QTextEdit {
|
||
border: 1px solid #ccc;
|
||
border-radius: 4px;
|
||
padding: 5px;
|
||
font-size: 14px;
|
||
}
|
||
QTextEdit:focus {
|
||
border: 1px solid #0078d7;
|
||
}
|
||
""")
|
||
|
||
# 添加到布局中
|
||
area.layout().addWidget(textedit)
|
||
|
||
# 设置布局的间距和边距
|
||
area.layout().setSpacing(10) # 控件之间的间距
|
||
area.layout().setContentsMargins(10, 10, 10, 10) # 布局的边距
|
||
|
||
# 保存这些TextEdit以便后续访问
|
||
self.check_method_textedits = labels_and_textedits
|
||
|
||
text_to_add = {
|
||
"工作内容": '',
|
||
"人员配置": '',
|
||
"设备配置": '',
|
||
"施工方案": '',
|
||
"备注": '',
|
||
}
|
||
if self.ifwaibu:
|
||
text_to_add['工作内容'] += '无人机叶片外观巡检\n'
|
||
text_to_add['人员配置'] += '1人;主检飞手1人\n'
|
||
text_to_add['设备配置'] += '大疆无人机1台(M350rtk,M300rtk,M30T,M30,精灵4PRO)||大疆精灵4PRO+索尼A7R2机身+索尼200-600mm镜头/适马150-600mm镜头\n'
|
||
|
||
if self.ifneibu:
|
||
text_to_add['工作内容'] += '叶片内部检查\n'
|
||
text_to_add['人员配置'] += '2人;轮毂作业检查2人\n'
|
||
text_to_add['设备配置'] += '人工检查:照明设备2套,视频记录手机2台,含氧量监测仪1台,电动扳手2套,卷尺1个。||爬壁机器人检查:无人作业校车+视频图传1套,照明设备2套,含氧量监测仪1台,电动扳手2套,卷尺1个。\n'
|
||
|
||
if self.iffanglei:
|
||
text_to_add['工作内容'] += '无人机叶片防雷导通测\n无人吊篮叶片导通测试(含机舱设备、)'
|
||
text_to_add['人员配置'] += '2人;主检飞手1人,副检抄表1人\n3人,轮毂机舱作业1人,揽风绳作业1人,无人设备操作员及抄表1人'
|
||
text_to_add['设备配置'] += '1四轴电阻无人机1套,电子微欧计1台,视频记录手机1台\n无人吊篮系统1套(爬绳器+接触平台)、电子微欧计1套,视频记录手机1台,对讲机2台'
|
||
|
||
for name, textedit in labels_and_textedits.items():
|
||
if text_to_add[name]:
|
||
textedit.setPlainText(text_to_add[name])
|
||
|
||
def start_init_huizong(self):
|
||
if not self.get_info_mode:
|
||
self._init_inspection_content_label()
|
||
else:
|
||
self._init_inspection_content_from_file()
|
||
self.has_load_huizong = True
|
||
self.ui.label_53.setText(f"初始化汇总完毕,如需要重新初始化,请新建项目")
|
||
self.ui.start_init_huizong.setEnabled(False)
|
||
|
||
def _init_inspection_content_from_file(self):
|
||
#主要部位图片展示表/检查内容表
|
||
try:
|
||
search_file_list = []
|
||
if self.ifwaibu:
|
||
search_file_list.append("外汇总")
|
||
if self.ifneibu:
|
||
search_file_list.append("内汇总")
|
||
if self.iffanglei:
|
||
search_file_list.append("防汇总")
|
||
picture_Y1_num, Y1_dict = collect_defect_data(Y1, Picture_dir, search_file_list)
|
||
picture_Y2_num, Y2_dict = collect_defect_data(Y2, Picture_dir, search_file_list)
|
||
picture_Y3_num, Y3_dict = collect_defect_data(Y3, Picture_dir, search_file_list)
|
||
for name, path in Y1_dict.items():
|
||
self.add_dlabel(0, self.ui.scrollAreaWidgetContents, name, path, True)
|
||
for name, path in Y2_dict.items():
|
||
self.add_dlabel(1, self.ui.scrollAreaWidgetContents_2, name, path, True)
|
||
for name, path in Y3_dict.items():
|
||
self.add_dlabel(2, self.ui.scrollAreaWidgetContents_3, name, path, True)
|
||
self.ui.messagebrowser.append(f"自动获取汇总图成功,共{picture_Y1_num+picture_Y2_num+picture_Y3_num}张图片")
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"自动获取汇总图失败:{e}")
|
||
# def toggle_terminal_window(self):
|
||
# """切换终端窗口的显示状态"""
|
||
# if self.terminal_window is None or not self.terminal_window.isVisible():
|
||
# # 创建或显示终端窗口
|
||
# self.terminal_window = TerminalWindow(self)
|
||
# self.terminal_window.show()
|
||
# print("终端输出窗口已打开")
|
||
# else:
|
||
# # 隐藏终端窗口
|
||
# self.terminal_window.hide()
|
||
# print("终端输出窗口已隐藏")
|
||
|
||
def save_form_data(self):
|
||
"""保存表单数据到JSON文件"""
|
||
#保存静态内容:
|
||
try:
|
||
self.ui.messagebrowser.append('保存静态数据中...')
|
||
self.data = {
|
||
'has_load_huizong': self.has_load_huizong,
|
||
'has_load_quexian': self.has_load_quexian,
|
||
'key_words': '缺,损,裂,脱,污', #关键字,用于汇总图的名字包含缺陷时标红,匹配逻辑为正则匹配单个字则为红 后续可优化
|
||
#当前检查报告基本内容
|
||
'jizu_type': self.ui.jizu_type.text(), #检查的机组编号
|
||
'jiancha_date': self.ui.jiancha_date.text(), #检查叶片日期 注意空格分开 小时分钟间隔为.
|
||
'baogao_date': self.ui.date.text(), #生成报告时间 注意空格分开
|
||
'baogao_type': self.ui.baogao_name.text(), #报告类型
|
||
'jiancha_shijian': self.ui.jiancha_shijian.text(),
|
||
'chuli_shijian': self.ui.chulishijian.text(),
|
||
'bianzhi_shijian': self.ui.bianzhishijian.text(),
|
||
'weak_Y1': self.ui.weak_num_Y1.text(),
|
||
'weak_Y2': self.ui.weak_num_Y2.text(),
|
||
'weak_Y3': self.ui.weak_num_Y3.text(),
|
||
'quexian_Y1': self.ui.textEdit.toPlainText(),
|
||
'quexian_Y2': self.ui.textEdit_2.toPlainText(),
|
||
'quexian_Y3': self.ui.textEdit_3.toPlainText(),
|
||
#检查方案
|
||
#待完善
|
||
'beizhu': '无',
|
||
'renyuan_peizhi': '''2人;主检飞手1人,副检抄表1人
|
||
3人,轮毂机舱作业1人,揽风绳作业1人,无人设备操作员及抄表1人
|
||
1人;抄表人员1人,检测人员1人,监护1人。
|
||
1人;主检飞手1人
|
||
2人;轮毂作业检查2人''',
|
||
'gongzuo_neirong': '''无人机叶片防雷导通测
|
||
无人吊篮叶片导通测试(含机舱设备、)
|
||
风机基础、办公楼、变电站防雷接地检测及浪涌保护器测试
|
||
无人机叶片外观巡检
|
||
叶片内部检查''',
|
||
'shigong_fangan': '无',
|
||
'shebei_peizhi': '''1四轴电阻无人机1套,电子微欧计1台,视频记录手机1台
|
||
无人吊篮系统1套(爬绳器+接触平台)、电子微欧计1套,视频记录手机1台,对讲机2台
|
||
接地电阻测试仪1套、SPD测试仪1套、对讲机2个、
|
||
1、大疆无人机1台(M350rtk,M300rtk,M30T,M30,精灵4PRO)2、大疆精灵4PRO+索尼A7R2机身+索尼200-600mm镜头/适马150-600mm镜头
|
||
1、人工检查:照明设备2套,视频记录手机2台,含氧量监测仪1台,电动扳手2套,卷尺1个。2、爬壁机器人检查:无人作业校车+视频图传1套,照明设备2套,含氧量监测仪1台,电动扳手2套,卷尺1个。''',
|
||
'jiancha_renyuan': '张三',
|
||
#检查信息
|
||
'waibu_jiancha': self.ifwaibu, #是否包含外部检查
|
||
'neibu_jiancha': self.ifneibu, #是否包含内部检查
|
||
'fanglei_jiancha': self.iffanglei, #是否包含防雷检查 #注:防雷检测占不存放缺陷图
|
||
'jiancha_location': '、'.join(self.checkpos), #检查内容文字
|
||
'jiancha_fangshi': self.ui.jiancha_fangshi.text(), #检查方式文字
|
||
'yepian_changjia': self.ui.changjia.text(), #叶片厂家信息
|
||
'jiancha_renyuan': self.ui.jiancha_renyuan.text(), #检查人员
|
||
'Tatong_pic_dir': self.ui.Tatong_label.current_path if self.ui.Tatong_label.current_path else '',
|
||
#报告处理信息
|
||
'yezhu_renyuan': self.ui.yezhu_renyuan.text(), #业主(人员)
|
||
'changjia_renyuan': self.ui.changjia_renyuan.text(), #厂家(人员)
|
||
'date_process': self.ui.shujuchuli.text(), #数据处理人员 吴总希望获取前端用户执行生成人员为这个人
|
||
'baogao_bianzhi': self.ui.baogao_bianzhi.text(), #报告编制人员 吴总希望获取前端用户执行生成人员为这个人
|
||
'baogao_shenghe': self.ui.baogao_shenghe.text(), #报告审核人员
|
||
'shenghe_date': self.ui.shenghe_shijian.text(), #报告审核日期
|
||
#检查情况汇总表(文字信息) 前端根据是否包含对应部分检查自行确定检查内容,这里默认全部包含
|
||
'Y1_jiancha_neirong': self.ui.textEdit_4.toPlainText(),
|
||
'Y2_jiancha_neirong': self.ui.textEdit_5.toPlainText(),
|
||
'Y3_jiancha_neirong': self.ui.textEdit_6.toPlainText(),
|
||
#报告总结
|
||
'baogao_zongjie': self.ui.conclusion.toPlainText(), #报告总结文字
|
||
}
|
||
self.data.update(
|
||
{
|
||
#目录
|
||
'picture_dir': self.Picture_dir, #图片存放地址 为了报告美观,希望总的汇总图片数量为3的倍数
|
||
'shengcheng_dir': self.ui.output_dir.text(), #工作路径(报告生成的路径、报告模板存放的路径)
|
||
'muban_dir': self.ui.muban_dir.text(), #文档模板存放路径
|
||
'save_dir': self.ui.save_dir.text(), #项目保存路径
|
||
#项目概况
|
||
'jituan_jianxie': self.ui.jituan_jianxie.text(),
|
||
'jia_Company': self.ui.Jia_Company.text(),
|
||
'jizu_num': self.ui.project_num.text(),
|
||
'jizu_bianhao': self.ui.jizu_bianhao.text(),
|
||
'fengchang_name': self.ui.fengchang_name.text(),
|
||
'fengchang_location': self.ui.fengchang_location.text(),
|
||
#乙方信息
|
||
'yi_Company': self.ui.Yi_Company.text(),
|
||
'fuzeren': self.ui.fuzeren.text(),
|
||
'phone_fuzeren': self.ui.phone_fuzeren.text(),
|
||
|
||
'get_info_mode': self.get_info_mode,
|
||
'if_xiangmugaikuang': self.ui.checkBox.isChecked(),
|
||
'if_jianchaxinxi': self.ui.checkBox_8.isChecked(),
|
||
'if_chengguodijiao': self.ui.checkBox_9.isChecked(),
|
||
'if_jianchahuizong': self.ui.checkBox_10.isChecked(),
|
||
'if_jiancha_yilan': self.ui.checkBox_11.isChecked(),
|
||
'if_quexian_xiangqing': self.ui.checkBox_13.isChecked(),
|
||
}
|
||
)
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"保存静态内容失败:{e}")
|
||
try:
|
||
if self.total_picture_dir:
|
||
self.data['total_picture_dir'] = self.total_picture_dir
|
||
else: self.data['total_picture_dir'] = None
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f'保存略缩图信息失败:{e}')
|
||
#保存动态内容:
|
||
try:
|
||
if self.has_load_huizong:
|
||
self.ui.messagebrowser.append('保存动态数据中...')
|
||
#汇总部分
|
||
self.data.update({
|
||
'labels':{
|
||
'init':[[],[],[]],
|
||
'add': [[],[],[]],
|
||
},
|
||
'lines':{
|
||
'init':[[],[],[]],
|
||
'add': [[],[],[]]
|
||
}
|
||
})
|
||
self.ui.messagebrowser.append('保存汇总图片与描述')
|
||
for i in range(3):
|
||
if self.get_info_mode == 0:
|
||
if self.label_init_list is not None:
|
||
for j in range(len(self.label_init_list[i])):
|
||
self.data['labels']['init'][i].append(self.label_init_list[i][j].current_path)
|
||
if self.line_init_list is not None:
|
||
for j in range(len(self.line_init_list[i])):
|
||
self.data['lines']['init'][i].append(self.line_init_list[i][j].text())
|
||
if self.new_add_dlabel is not None:
|
||
for j in range(len(self.new_add_dlabel[i])):
|
||
self.data['labels']['add'][i].append(self.new_add_dlabel[i][j].current_path)
|
||
if self.new_add_lines is not None:
|
||
for j in range(len(self.new_add_lines[i])):
|
||
self.data['lines']['add'][i].append(self.new_add_lines[i][j].text())
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"保存动态组件失败:{e}")
|
||
return
|
||
try:
|
||
if self.has_load_quexian:
|
||
#缺陷部分
|
||
self.ui.messagebrowser.append('保存缺陷图片与描述')
|
||
self.data.update({
|
||
'quexian_image_infos': [[], [], []]
|
||
})
|
||
if self.table_infos_modules is not None:
|
||
for i in range(len(self.data['quexian_image_infos'])):
|
||
for image in self.table_infos_modules[i]:
|
||
self.data['quexian_image_infos'][i].append({
|
||
'image_path': image['image_path'],
|
||
'visibility': image['visibility_combo'].currentIndex(),
|
||
'severity': image['severity_combo'].currentIndex(),
|
||
'urgency': image['urgency_combo'].currentIndex(),
|
||
'defect_type': image['defect_type_combo'].current_index,
|
||
'defect_location': image['defect_location_edit'].text(),
|
||
'defect_size': image['defect_size_edit'].text(),
|
||
'repair_suggestion': image['repair_suggestion_edit'].toPlainText(),
|
||
})
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"保存缺陷图片与描述失败:{e}")
|
||
return
|
||
|
||
try:
|
||
self.data.update({
|
||
"工作内容": '',
|
||
"人员配置": '',
|
||
"设备配置": '',
|
||
"施工方案": '',
|
||
"备注": '',
|
||
})
|
||
for name, textedit in self.check_method_textedits.items():
|
||
self.data[name] = self.check_method_textedits[name].toPlainText()
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f'保存施工方案失败')
|
||
# data['quexian_info'] = self.table_info_to_save
|
||
#保存
|
||
|
||
# 默认保存路径
|
||
default_savepath = self.ui.save_dir.text() + rf'\{self.ui.jituan_jianxie.text()}{self.ui.fengchang_name_2.text()}项目{self.ui.baogao_name.text()}{self.ui.jizu_type_2.text()}{time.strftime("%Y%m%d_%H%M%S", time.localtime())}.json'
|
||
|
||
# 弹出文件保存对话框
|
||
file_path, _ = QFileDialog.getSaveFileName(
|
||
self, # 父窗口
|
||
"保存文件", # 对话框标题
|
||
default_savepath, # 默认路径和文件名
|
||
"JSON文件 (*.json);;所有文件 (*)" # 文件过滤器
|
||
)
|
||
|
||
# 如果用户取消了对话框,file_path会是空字符串
|
||
if not file_path:
|
||
return False
|
||
|
||
try:
|
||
with open(file_path, 'w') as f:
|
||
json.dump(self.data, f)
|
||
self.ui.messagebrowser.append(f"保存成功,保存路径:{file_path}")
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"保存失败: {str(e)}")
|
||
QMessageBox.information(self, "保存成功", f"保存成功,保存路径:{file_path}")
|
||
return True
|
||
|
||
def apply_date_to_calendar(self, date_str: str, calendar: QCalendarWidget):
|
||
"""
|
||
将 "年/月/日" 格式的日期字符串应用到日历组件
|
||
|
||
参数:
|
||
date_str: str, 格式如 "2023/12/31"
|
||
calendar: QCalendarWidget 日历组件
|
||
"""
|
||
# 使用 QDate.fromString 解析日期字符串
|
||
date = QDate.fromString(date_str, "yyyy/MM/dd")
|
||
|
||
# 检查日期是否有效(避免无效日期,如 "2023/02/30")
|
||
if date.isValid():
|
||
calendar.setSelectedDate(date)
|
||
else:
|
||
print(f"警告:无效的日期格式 {date_str},无法应用到日历")
|
||
|
||
def load_project(self, path):
|
||
"""从JSON文件加载表单数据"""
|
||
self.ui.messagebrowser.append(f"加载项目数据: {path}")
|
||
self.ui.messagebrowser.append('加载静态数据中...')
|
||
try:
|
||
with open(path, 'r') as f:
|
||
self.data = json.load(f)
|
||
except FileNotFoundError:
|
||
print(f"错误:文件 {path} 不存在")
|
||
return None
|
||
except json.JSONDecodeError:
|
||
print(f"错误:文件 {path} 不是有效的JSON格式")
|
||
return None
|
||
#加载静态内容
|
||
try:
|
||
# 当前检查报告基本内容
|
||
self.ui.jizu_type_2.setText(self.data['jizu_bianhao']) # 检查的机组编号
|
||
self.ui.jizu_type.setText(self.data['jizu_type'])
|
||
self.ui.jiancha_date.setText(self.data['jiancha_date']) # 检查叶片日期
|
||
self.ui.date.setText(self.data['baogao_date']) # 生成报告时间
|
||
self.ui.baogao_name.setText(self.data['baogao_type']) # 报告类型
|
||
|
||
# 检查信息
|
||
self.ui.changjia.setText(self.data['yepian_changjia']) # 叶片厂家信息
|
||
self.ui.jiancha_fangshi.setText(self.data['jiancha_fangshi'])
|
||
self.ui.jiancha_renyuan.setText(self.data['jiancha_renyuan'])
|
||
self.ui.jiancha_renyuan2.setText(self.data['jiancha_renyuan'])
|
||
|
||
self.ui.jizu_bianhao.setText(self.data['jizu_bianhao'])
|
||
self.ui.jizu_bianhao2.setText(self.data['jizu_bianhao'])
|
||
|
||
try:
|
||
if self.data['Tatong_pic_dir'] != '':
|
||
self.ui.Tatong_label.load_image(self.data['Tatong_pic_dir'])
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"加载塔筒图片失败,请再次导入:{e}")
|
||
self.ui.jiancha_shijian.setText(self.data['jiancha_shijian'])
|
||
self.ui.chulishijian.setText(self.data['chuli_shijian'])
|
||
|
||
self.ui.bianzhishijian.setText(self.data['bianzhi_shijian'])
|
||
|
||
self.apply_date_to_calendar(self.data['bianzhi_shijian'], self.ui.calendarWidget)
|
||
# 检查方案
|
||
try:
|
||
for name, textedit in self.check_method_textedits.items():
|
||
self.check_method_textedits[name].setPlainText(self.data[name])
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f'加载施工方案失败')
|
||
# 报告处理信息
|
||
self.ui.yezhu_renyuan.setText(self.data['yezhu_renyuan']) # 业主(人员)
|
||
self.ui.changjia_renyuan.setText(self.data['changjia_renyuan']) # 厂家(人员)
|
||
self.ui.shujuchuli.setText(self.data['date_process']) # 数据处理人员
|
||
self.ui.baogao_bianzhi.setText(self.data['baogao_bianzhi']) # 报告编制人员
|
||
self.ui.docx_bianzhi.setText(self.data['baogao_bianzhi'])
|
||
self.ui.docx_check.setText(self.data['baogao_shenghe'])
|
||
self.ui.baogao_shenghe.setText(self.data['baogao_shenghe']) # 报告审核人员
|
||
self.ui.shenghe_shijian.setText(self.data['shenghe_date']) # 报告审核日期
|
||
|
||
# 检查情况汇总表(文字信息)
|
||
self.ui.weak_num_Y1.setText(self.data['weak_Y1'])
|
||
self.ui.weak_num_Y2.setText(self.data['weak_Y2'])
|
||
self.ui.weak_num_Y3.setText(self.data['weak_Y3'])
|
||
self.ui.textEdit_4.setPlainText(self.data['Y1_jiancha_neirong'])
|
||
self.ui.textEdit_5.setPlainText(self.data['Y2_jiancha_neirong'])
|
||
self.ui.textEdit_6.setPlainText(self.data['Y3_jiancha_neirong'])
|
||
self.ui.textEdit.setPlainText(self.data['quexian_Y1'])
|
||
self.ui.textEdit_2.setPlainText(self.data['quexian_Y2'])
|
||
self.ui.textEdit_3.setPlainText(self.data['quexian_Y3'])
|
||
|
||
# 报告总结
|
||
self.ui.conclusion.setPlainText(self.data['baogao_zongjie']) # 报告总结文字
|
||
|
||
# 项目概况
|
||
self.ui.jituan_jianxie.setText(self.data['jituan_jianxie'])
|
||
self.ui.Jia_Company.setText(self.data['jia_Company'])
|
||
self.ui.project_num.setText(self.data['jizu_num'])
|
||
self.ui.fengchang_name.setText(self.data['fengchang_name'])
|
||
self.ui.fengchang_location.setText(self.data['fengchang_location'])
|
||
self.ui.fengchang_name_2.setText(self.data['fengchang_name'])
|
||
|
||
# 乙方信息
|
||
self.ui.Yi_Company.setText(self.data['yi_Company'])
|
||
self.ui.Jiancha_company.setText(self.data['yi_Company'])
|
||
self.ui.conclusion_company.setText(self.data['yi_Company'])
|
||
self.ui.fuzeren.setText(self.data['fuzeren'])
|
||
self.ui.phone_fuzeren.setText(self.data['phone_fuzeren'])
|
||
|
||
self.has_load_huizong = self.data['has_load_huizong']
|
||
self.has_load_quexian = self.data['has_load_quexian']
|
||
|
||
self.ui.muban_dir.setText(self.data['muban_dir'])
|
||
self.ui.save_dir.setText(self.data['save_dir'] if self.data.get('save_dir') else '')
|
||
self.ui.output_dir.setText(self.data['shengcheng_dir'] if self.data.get('shengcheng_dir') else '')
|
||
try:
|
||
if self.data['total_picture_dir']:
|
||
total_picture_dir = self.data['total_picture_dir']
|
||
self.total_picture_dir = total_picture_dir
|
||
self.total_picture_count = 0
|
||
self.recursive_count_images(total_picture_dir)
|
||
self.ui.label_18.setText('路径:' + str(total_picture_dir))
|
||
self.ui.messagebrowser.append(f"图片总数: {self.total_picture_count}、文件夹路径:{total_picture_dir}")
|
||
self.ui.total_picture_num.setText(str(self.total_picture_count))
|
||
self.ui.messagebrowser.append(f"已选择图片目录项,程序生成时会自动读取这些目录进行略缩图创建")
|
||
output_dir = self.Picture_dir
|
||
if os.path.exists(output_dir + '/merged_thumbnail.jpg'):
|
||
self.ui.messagebrowser.append(f"!!!报告生成图片路径已有略缩图,如需使用本功能生成,请先前往目录:{output_dir} 删除略缩图 merged_thumbnail.jpg")
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"加载图片总数失败:{e}")
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"加载静态数据失败:{e}")
|
||
return
|
||
|
||
print('开始加载数据')
|
||
try:
|
||
# 加载动态内容
|
||
self.ui.messagebrowser.append('加载动态数据中...')
|
||
scrollarea = [self.ui.scrollAreaWidgetContents, self.ui.scrollAreaWidgetContents_2, self.ui.scrollAreaWidgetContents_3]
|
||
if self.data['has_load_huizong']:
|
||
self.ui.messagebrowser.append('上次保存时已初始化汇总数据,开始加载初始化汇总数据')
|
||
for i in range(3):
|
||
for label, line in zip(self.data['labels']['init'][i], self.data['lines']['init'][i]):
|
||
self.ui.messagebrowser.append(f"加载初始化汇总数据: {label}, {line}")
|
||
self.add_dlabel(i, scrollarea[i], line, label)
|
||
for i in range(3):
|
||
for label, line in zip(self.data['labels']['add'][i], self.data['lines']['add'][i]):
|
||
self.add_dlabel(i, scrollarea[i], line, label)
|
||
self.ui.messagebrowser.append('加载初始化汇总数据成功')
|
||
|
||
self.ui.label_53.setText(f"初始化汇总完毕,如需要重新初始化,请保存项目信息,重新加载")
|
||
self.ui.start_init_huizong.setEnabled(False)
|
||
|
||
if self.data['has_load_quexian']:
|
||
self.ui.messagebrowser.append('上次保存时已初始化缺陷数据,开始加载初始化缺陷数据')
|
||
self.load_tabs_from_saved_data(self.ui.tab_8, self.data['quexian_image_infos'])
|
||
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"加载动态数据失败:{e}")
|
||
return
|
||
|
||
self.ui.messagebrowser.append('加载数据成功')
|
||
|
||
def set_tab_fill_disable(self, box : QtWidgets.QCheckBox, index : int):
|
||
parent = box.parent()
|
||
while parent is not None:
|
||
if isinstance(parent, QTabWidget):
|
||
break
|
||
parent = parent.parent()
|
||
for child in parent.widget(index).findChildren(QWidget):
|
||
if child.objectName() == box.objectName():
|
||
continue
|
||
if isinstance(child, QLineEdit) or isinstance(child, QTextEdit):
|
||
if box.isChecked():
|
||
child.setReadOnly(True)
|
||
else:
|
||
child.setReadOnly(False)
|
||
|
||
class ReportGeneratorThread(QtCore.QThread):
|
||
class MessageStream(QtCore.QObject):
|
||
"""自定义流,用于实时捕获 print 输出并发送信号"""
|
||
from io import StringIO
|
||
message_received = QtCore.Signal(str) # 定义信号,用于传递消息
|
||
|
||
def write(self, text):
|
||
self.message_received.emit(text) # 发送消息到主线程
|
||
# 可以在这里添加额外的处理(如过滤空行)
|
||
if text.strip(): # 如果非空才发送
|
||
self.message_received.emit(text)
|
||
|
||
def flush(self):
|
||
pass # 必须实现 flush,但可以留空
|
||
|
||
report_generated = QtCore.Signal(str) # 报告生成完成信号
|
||
error_occurred = QtCore.Signal(str) # 错误信号
|
||
|
||
def __init__(self, ui, parent):
|
||
super().__init__()
|
||
self.ui = ui
|
||
self.parent = parent
|
||
self.message_stream = self.MessageStream() # 自定义流
|
||
|
||
def run(self):
|
||
try:
|
||
from main import generate_report
|
||
import asyncio
|
||
|
||
# 重定向标准输出到自定义流(实时捕获 print)
|
||
sys.stdout = self.message_stream
|
||
|
||
# 调用异步函数生成报告
|
||
file_path = asyncio.run(generate_report(self.ui, self.parent))
|
||
|
||
# 恢复标准输出
|
||
sys.stdout = sys.__stdout__
|
||
|
||
# 发送完成信号
|
||
self.report_generated.emit(file_path)
|
||
except Exception as e:
|
||
# 恢复标准输出
|
||
sys.stdout = sys.__stdout__
|
||
self.error_occurred.emit(str(e))
|
||
|
||
def start_generate_report(self):
|
||
"""启动报告生成线程"""
|
||
dirs = [self.ui.save_dir.text(), self.ui.output_dir.text(), self.ui.muban_dir.text()]
|
||
if not self.check_dir(dirs):
|
||
return
|
||
if not self.validate_form_auto():
|
||
return
|
||
|
||
# 创建并启动线程
|
||
self.report_thread = self.ReportGeneratorThread(self.ui, self)
|
||
self.report_thread.report_generated.connect(self.on_report_generated)
|
||
self.report_thread.error_occurred.connect(self.on_report_error)
|
||
self.report_thread.message_stream.message_received.connect(self.append_message) # 连接实时输出信号
|
||
self.report_thread.start()
|
||
|
||
# 显示提示
|
||
QMessageBox.information(self, "提示", "报告正在生成中,请稍候...", QMessageBox.Ok)
|
||
|
||
def append_message(self, message):
|
||
"""实时追加消息到 messagebrowser"""
|
||
self.ui.messagebrowser.append(message) # 去除多余空白
|
||
|
||
def on_report_generated(self, file_path):
|
||
"""报告生成成功回调"""
|
||
self.ui.messagebrowser.append("------------\n报告生成成功\n------------")
|
||
self.save_textbrowser_content(self.ui.messagebrowser, self.ui.output_dir.text() + r'\log.txt')
|
||
self.ui.messagebrowser.append(f"------------\n生成日志已保存在输出路径{self.ui.output_dir.text()}/log.txt\n------------")
|
||
reply = QMessageBox.question(
|
||
self,
|
||
"报告生成成功",
|
||
f"报告已生成在: {file_path}\n\n是否要现在打开?",
|
||
QMessageBox.Yes | QMessageBox.No,
|
||
QMessageBox.Yes
|
||
)
|
||
if reply == QMessageBox.Yes:
|
||
import os
|
||
os.startfile(file_path) # Windows
|
||
|
||
def on_report_error(self, error_msg):
|
||
"""错误回调"""
|
||
QMessageBox.critical(self, "错误", f"生成报告时出错:\n{error_msg}")
|
||
|
||
def validate_form_auto(self):
|
||
|
||
line_edits = self.findChildren(QLineEdit)
|
||
text_edits = self.findChildren(QTextEdit)
|
||
self.ui.messagebrowser.append(f"找到的 QLineEdit 数量: {len(line_edits)}")
|
||
self.ui.messagebrowser.append(f"找到的 QTextEdit 数量: {len(text_edits)}")
|
||
|
||
missing = False
|
||
missing_tabs = set()
|
||
|
||
try:
|
||
for le in line_edits:
|
||
if le.text().strip() == '' and le.isReadOnly() == False: #为空且不只读
|
||
self.ui.messagebrowser.append(f"发现未填写的 QLineEdit: {le.objectName()}") # 调试点1
|
||
stylesheet = le.styleSheet() + "\nbackground-color: yellow;"
|
||
le.setStyleSheet(stylesheet)
|
||
le.textChanged.connect(lambda text, widget=le: self.reset_lineedit_style(widget))
|
||
missing = True
|
||
|
||
tab_widget, tab_name = self.find_tab_widget_and_name(le)
|
||
self.ui.messagebrowser.append(f"所属标签页: {tab_name}") # 调试点2
|
||
if tab_name:
|
||
missing_tabs.add(tab_name)
|
||
|
||
for te in text_edits:
|
||
if te.toPlainText().strip() == '' and te.isReadOnly() == False:
|
||
self.ui.messagebrowser.append(f"发现未填写的 QTextEdit: {te.objectName()}") # 调试点1
|
||
stylesheet = te.styleSheet() + "\nbackground-color: yellow;"
|
||
te.setStyleSheet(stylesheet)
|
||
te.textChanged.connect(lambda widget=te: self.reset_lineedit_style(widget))
|
||
missing = True
|
||
|
||
tab_widget, tab_name = self.find_tab_widget_and_name(te)
|
||
self.ui.messagebrowser.append(f"所属标签页: {tab_name}") # 调试点2
|
||
if tab_name:
|
||
missing_tabs.add(tab_name)
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"验证表单错误: {e}")
|
||
|
||
if missing:
|
||
self.ui.messagebrowser.append(f"所有缺失的标签页: {missing_tabs}") # 调试点3
|
||
message = "有未填写部分,已标黄"
|
||
if missing_tabs:
|
||
message += f"\n未填写部分位于以下标签页:"
|
||
for tab in missing_tabs:
|
||
message += f"\n{tab}"
|
||
if self.ui.Tatong_label.text() == "塔筒号图片选择" and not self.ui.checkBox_8.isChecked():
|
||
message += "\n塔筒号图片未选择"
|
||
message += '\n是否继续生成?'
|
||
msg_box = QMessageBox(self)
|
||
msg_box.setIcon(QMessageBox.Warning)
|
||
msg_box.setWindowTitle("错误")
|
||
msg_box.setText(message)
|
||
msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
|
||
msg_box.setWindowModality(Qt.NonModal) # 关键:设为非模态
|
||
response = msg_box.exec()
|
||
if response == QMessageBox.Yes:
|
||
self.empty_line_set()
|
||
return True
|
||
else:
|
||
return False
|
||
return True
|
||
|
||
|
||
def find_tab_widget_and_name(self, widget):
|
||
try:
|
||
parent = widget.parent()
|
||
while parent is not None:
|
||
self.ui.messagebrowser.append(f"当前父组件: {parent}, 类型: {type(parent)}") # 调试点4
|
||
|
||
# 检查当前父级是否是 QTabWidget
|
||
if isinstance(parent, QTabWidget):
|
||
# 获取当前控件所在的标签页索引
|
||
tab_index = parent.indexOf(widget)
|
||
if tab_index == -1:
|
||
# 如果控件不在 QTabWidget 的直接子级,尝试查找其所在的 QWidget 子级
|
||
for i in range(parent.count()):
|
||
tab_content = parent.widget(i)
|
||
if tab_content and widget in tab_content.findChildren(QWidget, options=Qt.FindChildrenRecursively):
|
||
tab_name = parent.tabText(i)
|
||
self.ui.messagebrowser.append(f"找到标签页: {tab_name} (索引: {i})") # 调试点5
|
||
return parent, tab_name
|
||
else:
|
||
tab_name = parent.tabText(tab_index)
|
||
self.ui.messagebrowser.append(f"找到标签页: {tab_name} (索引: {tab_index})") # 调试点5
|
||
return parent, tab_name
|
||
|
||
parent = parent.parent()
|
||
|
||
self.ui.messagebrowser.append("未找到 QTabWidget 父组件") # 调试点6
|
||
return None, None
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"查找标签页错误: {e}")
|
||
return None, None
|
||
|
||
def reset_lineedit_style(self, line_edit : QLineEdit):
|
||
"""重置 QLineEdit 的样式表为空"""
|
||
self.remove_last_style_sheet_rule(line_edit)
|
||
try:
|
||
line_edit.textChanged.disconnect()
|
||
except:
|
||
pass
|
||
|
||
|
||
def remove_last_style_sheet_rule(self, widget : QWidget):
|
||
stylesheet = widget.styleSheet()
|
||
if stylesheet:
|
||
lines = stylesheet.splitlines() # 分割为行列表
|
||
if lines: # 如果非空
|
||
lines.pop() # 移除最后一行
|
||
new_stylesheet = "\n".join(lines) # 重新合并
|
||
widget.setStyleSheet(new_stylesheet)
|
||
|
||
def _init_cover_page(self):
|
||
# 初始化日期显示
|
||
self._init_date_display()
|
||
|
||
# 设置封面内容
|
||
self.ui.jituan_jianxie.setText(jituan_name)
|
||
self.ui.fengchang_name_2.setText(fengchang_name)
|
||
self.ui.jizu_type.setText(jizu_type)
|
||
self.ui.Yi_Company.setText(company_name_yi)
|
||
|
||
def _init_date_display(self):
|
||
"""初始化日期显示,包括中文转换"""
|
||
unit_map = {'1' : '一', '2' : '二', '3' : '三', '4' : '四', '5' : '五', '6' : '六', '7' : '七', '8' : '八', '9' : '九', '0' : '〇'}
|
||
|
||
unit_map_month = {1 : '一', 2 : '二', 3 : '三', 4 : '四', 5 : '五', 6 : '六', 7 : '七', 8 : '八', 9 : '九', 10 : '十', 11 : '十一', 12 : '十二'}
|
||
|
||
self.dateyear = self.ui.calendarWidget.selectedDate().toString('yyyy年')
|
||
self.dateyear = self.dateyear.translate(str.maketrans(unit_map))
|
||
|
||
self.datemonth = self.ui.calendarWidget.selectedDate().toString('MM')
|
||
self.datemonth = (unit_map_month[int(self.datemonth)] + '月')
|
||
|
||
self.ui.date.setText(self.dateyear + self.datemonth)
|
||
|
||
def _init_project_overview(self):
|
||
"""初始化项目概况部分"""
|
||
self.ui.fengchang_name.setText(fengchang_name)
|
||
self.ui.fengchang_location.setText(project_location)
|
||
self.ui.Jia_Company.setText(company_name_jia)
|
||
self.ui.jizu_type.setText(jizu_type)
|
||
self.ui.Jiancha_company.setText(company_name_yi)
|
||
self.ui.fuzeren.setText(fuzeren)
|
||
self.ui.phone_fuzeren.setText(phone_fuzeren)
|
||
self.ui.project_num.setText(xiangmuguige)
|
||
|
||
def _init_inspection_info(self):
|
||
"""初始化检查信息部分"""
|
||
try:
|
||
jiancha = []
|
||
self.neirong = []
|
||
xiangmu = []
|
||
self.checkpos = []
|
||
if self.ifwaibu:
|
||
self.checkpos.append('叶片外部外观')
|
||
jiancha.append("无人机外部高精度飞行")
|
||
self.neirong.append(f"{Y1}、{Y2}、{Y3}三支叶片的前缘、后缘、迎风面、背风面。")
|
||
xiangmu.append("外部")
|
||
if self.ifneibu:
|
||
self.checkpos.append('叶片内部')
|
||
jiancha.append("人工内部拍摄")
|
||
self.neirong.append(f"{Y1}、{Y2}、{Y3}三支叶片的内部导雷卡、腹板、透光、人孔盖版、叶根盖板...")
|
||
xiangmu.append("内部")
|
||
if self.iffanglei:
|
||
self.checkpos.append('机组防雷')
|
||
jiancha.append("人工防雷")
|
||
self.neirong.append(f"轮毂至塔基导通、内部导线线阻、外部导线线阻...")
|
||
xiangmu.append("防雷")
|
||
|
||
self.ui.label_49.setText(f"当前报告项目为:" + '、'.join(_ for _ in xiangmu))
|
||
normal_picture_num = get_picture_nums(self.Picture_dir)
|
||
|
||
#使用根目录文件夹名作为机组编号
|
||
self.ui.jizu_bianhao.setText(self.Picture_dir.split('/')[-1])
|
||
self.ui.jizu_bianhao2.setText(self.Picture_dir.split('/')[-1])
|
||
self.ui.jizu_type_2.setText(self.Picture_dir.split('/')[-1])
|
||
|
||
self.ui.Y1.setText(f'叶片 1:{Y1}')
|
||
self.ui.Y2.setText(f'叶片 2:{Y2}')
|
||
self.ui.Y3.setText(f'叶片 3:{Y3}')
|
||
self.ui.label_52.setText("本次" + "、".join(_ for _ in jiancha) + "检查,采集叶片图片")
|
||
self.ui.jiancha_xiangqing.setText("内容覆盖" + ";".join(_ for _ in self.neirong))
|
||
self.ui.Tatong_label.messagebrowser = self.ui.messagebrowser
|
||
self.ui.Tatong_label.setGeometry(QtCore.QRect(10, 10, 151, 151))
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"初始化检查信息失败:{e}")
|
||
print(f"初始化检查信息失败:{e}")
|
||
|
||
def _init_inspection_summary(self):
|
||
"""
|
||
初始化检查情况汇总。
|
||
具体问题描述初始化。如果实现了图片缺陷命名到图片上即可实现。
|
||
"""
|
||
try:
|
||
self.Y1_quexian_num, self.Y2_quexian_num, self.Y3_quexian_num, self.quexian_num = 0, 0, 0, 0
|
||
self.Y1_quexian_dict, self.Y2_quexian_dict, self.Y3_quexian_dict = {}, {}, {}
|
||
search_file_list = []
|
||
if self.ifwaibu:
|
||
search_file_list.append("外缺陷图")
|
||
if self.ifneibu:
|
||
search_file_list.append("内缺陷图")
|
||
self.Y1_quexian_num, self.Y1_quexian_dict = collect_defect_data(Y1, Picture_dir, search_file_list)
|
||
self.Y2_quexian_num, self.Y2_quexian_dict = collect_defect_data(Y2, Picture_dir, search_file_list)
|
||
self.Y3_quexian_num, self.Y3_quexian_dict = collect_defect_data(Y3, Picture_dir, search_file_list)
|
||
self.ui.weak_num_Y1.setText(f"{Y1}共发现缺陷{self.Y1_quexian_num}处")
|
||
self.ui.weak_num_Y2.setText(f"{Y2}共发现缺陷{self.Y2_quexian_num}处")
|
||
self.ui.weak_num_Y3.setText(f"{Y3}共发现缺陷{self.Y3_quexian_num}处")
|
||
|
||
self.ui.textEdit_4.setPlainText("\n".join(f"{i+1}.{neirong}" for i, neirong in enumerate(self.neirong) if neirong))
|
||
self.ui.textEdit_5.setPlainText("\n".join(f"{i+1}.{neirong}" for i, neirong in enumerate(self.neirong) if neirong))
|
||
self.ui.textEdit_6.setPlainText("\n".join(f"{i+1}.{neirong}" for i, neirong in enumerate(self.neirong) if neirong))
|
||
|
||
if self.get_info_mode == 1:
|
||
self.ui.textEdit.setPlainText("\n".join([f"{i+1}.{name}" for i, (name, path) in enumerate(self.Y1_quexian_dict.items())]) if self.Y1_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷')
|
||
self.ui.textEdit_2.setPlainText("\n".join([f"{i+1}.{name}" for i, (name, path) in enumerate(self.Y2_quexian_dict.items())]) if self.Y2_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷')
|
||
self.ui.textEdit_3.setPlainText("\n".join([f"{i+1}.{name}" for i, (name, path) in enumerate(self.Y3_quexian_dict.items())]) if self.Y2_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷')
|
||
|
||
self.ui.messagebrowser.append(f"读取路径:{Picture_dir},读取到{Y1}缺陷数:{self.Y1_quexian_num}")
|
||
self.ui.messagebrowser.append(f"读取路径:{Picture_dir},读取到{Y2}缺陷数:{self.Y2_quexian_num}")
|
||
self.ui.messagebrowser.append(f"读取路径:{Picture_dir},读取到{Y3}缺陷数:{self.Y3_quexian_num}")
|
||
# 保存缺陷总数供其他方法使用
|
||
self.quexian_num = self.Y1_quexian_num + self.Y2_quexian_num + self.Y3_quexian_num
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"获取缺陷表信息失败:{e}")
|
||
|
||
|
||
def _init_inspection_content(self):
|
||
"""初始化检查内容一览"""
|
||
# 设置页标题
|
||
self.ui.tabWidget_2.setTabText(0, f"叶片1:{Y1}检查内容")
|
||
self.ui.tabWidget_2.setTabText(1, f"叶片2:{Y2}检查内容")
|
||
self.ui.tabWidget_2.setTabText(2, f"叶片3:{Y3}检查内容")
|
||
self.picture_line = [[],[],[]]
|
||
self.picture_labels = [[],[],[]]
|
||
|
||
# 初始化布局和计数器
|
||
self.current_row = [0, 0, 0]
|
||
self.current_col = [0, 0, 0]
|
||
|
||
# 创建三个网格布局
|
||
self.gridlayout = [
|
||
QtWidgets.QGridLayout(self.ui.scrollAreaWidgetContents),
|
||
QtWidgets.QGridLayout(self.ui.scrollAreaWidgetContents_2),
|
||
QtWidgets.QGridLayout(self.ui.scrollAreaWidgetContents_3)
|
||
]
|
||
|
||
# 设置布局对齐方式
|
||
for layout in self.gridlayout:
|
||
layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||
|
||
# 连接按钮信号
|
||
self.ui.add_dlabel.clicked.connect(lambda: self.add_dlabel(0, self.ui.scrollAreaWidgetContents))
|
||
self.ui.add_dlabel_2.clicked.connect(lambda: self.add_dlabel(1, self.ui.scrollAreaWidgetContents_2))
|
||
self.ui.add_dlabel_3.clicked.connect(lambda: self.add_dlabel(2, self.ui.scrollAreaWidgetContents_3))
|
||
self.label_init_list = [[],[],[]]
|
||
self.line_init_list = [[],[],[]]
|
||
self.new_add_dlabel = [[],[],[]]
|
||
self.new_add_lines = [[],[],[]]
|
||
|
||
def _init_inspection_content_label(self):
|
||
self.photo_pos = ["PS面(最大弦长)", "SS面(最大弦长)", "PS面(叶尖)", "SS面(叶尖)", "前缘局部", "后缘局部"]
|
||
self.init_dlabel(self.gridlayout)
|
||
self.ui.tabWidget_2.setCurrentIndex(0)
|
||
|
||
def start_init_quexian(self):
|
||
'''
|
||
开始初始化缺陷信息
|
||
'''
|
||
self.has_load_quexian = True
|
||
self._init_inspection_summary()
|
||
self.setup_tabs(self.ui.tab_8, self.quexian_num, self.Picture_dir)
|
||
|
||
def add_waibu_text(self):
|
||
self.ui.messagebrowser.append("自动在三个叶片中添加关于外部检查的检查内容信息...")
|
||
self.ui.textEdit_4.append('外部检查:叶片前缘、后缘、PS面、SS面。')
|
||
self.ui.textEdit_5.append('外部检查:叶片前缘、后缘、PS面、SS面。')
|
||
self.ui.textEdit_6.append('外部检查:叶片前缘、后缘、PS面、SS面。')
|
||
def add_neibu_text(self):
|
||
self.ui.messagebrowser.append("自动在三个叶片中添加关于内部检查的检查内容信息...")
|
||
self.ui.textEdit_4.append('内部检查:叶片内部导雷卡、腹板、透光、人孔盖版、叶根盖板...')
|
||
self.ui.textEdit_5.append('内部检查:叶片内部导雷卡、腹板、透光、人孔盖版、叶根盖板...')
|
||
self.ui.textEdit_6.append('内部检查:叶片内部导雷卡、腹板、透光、人孔盖版、叶根盖板...')
|
||
def add_fanglei_text(self):
|
||
self.ui.messagebrowser.append("自动在三个叶片中添加关于防雷检查的检查内容信息...")
|
||
self.ui.textEdit_4.append('防雷检查:轮毂至塔基导通、内部导线线阻、外部导线线阻...')
|
||
self.ui.textEdit_5.append('防雷检查:轮毂至塔基导通、内部导线线阻、外部导线线阻...')
|
||
self.ui.textEdit_6.append('防雷检查:轮毂至塔基导通、内部导线线阻、外部导线线阻...')
|
||
|
||
def _init_connect(self):
|
||
#相同表单同步
|
||
self.ui.saveproject.clicked.connect(lambda: self.save_form_data())
|
||
def create_sync_handler(source, target, message):
|
||
def handler():
|
||
if source.text() != target.text():
|
||
target.setText(source.text())
|
||
# 只在真正有变化时才显示消息
|
||
if message:
|
||
self.ui.messagebrowser.append(message)
|
||
return handler
|
||
|
||
# 检查人员
|
||
self.ui.jiancha_renyuan.editingFinished.connect(
|
||
create_sync_handler(self.ui.jiancha_renyuan, self.ui.jiancha_renyuan2,
|
||
"检测到检查人员更改,自动同步所有检查人员栏..."))
|
||
self.ui.jiancha_renyuan2.editingFinished.connect(
|
||
create_sync_handler(self.ui.jiancha_renyuan2, self.ui.jiancha_renyuan,
|
||
"检测到检查人员更改,自动同步所有检查人员栏..."))
|
||
|
||
# 风场名
|
||
self.ui.fengchang_name.editingFinished.connect(
|
||
create_sync_handler(self.ui.fengchang_name, self.ui.fengchang_name_2,
|
||
"检测到风场名更改,自动同步风场名栏..."))
|
||
self.ui.fengchang_name_2.editingFinished.connect(
|
||
create_sync_handler(self.ui.fengchang_name_2, self.ui.fengchang_name,
|
||
"检测到风场名更改,自动同步风场名栏..."))
|
||
|
||
# 检查日期
|
||
self.ui.jiancha_date.textChanged.connect(lambda: (self.ui.messagebrowser.append("检测到检查日期更改,自动同步检查日期栏..."), self.ui.jiancha_riqi.setText(self.ui.jiancha_date.text())))
|
||
self.ui.jiancha_riqi.textChanged.connect(lambda: (self.ui.messagebrowser.append("检测到检查日期更改,自动同步检查日期栏..."), self.ui.jiancha_date.setText(self.ui.jiancha_riqi.text())))
|
||
# 报告编制人员
|
||
self.ui.baogao_bianzhi.editingFinished.connect(
|
||
create_sync_handler(self.ui.baogao_bianzhi, self.ui.docx_bianzhi,
|
||
"检测到报告编制人员更改,自动同步编制人员栏..."))
|
||
self.ui.docx_bianzhi.editingFinished.connect(
|
||
create_sync_handler(self.ui.docx_bianzhi, self.ui.baogao_bianzhi,
|
||
"检测到报告编制人员更改,自动同步编制人员栏..."))
|
||
|
||
# 报告审核人员
|
||
self.ui.baogao_shenghe.editingFinished.connect(
|
||
create_sync_handler(self.ui.baogao_shenghe, self.ui.docx_check,
|
||
"检测到报告审核人员更改,自动同步审核人员栏..."))
|
||
self.ui.docx_check.editingFinished.connect(
|
||
create_sync_handler(self.ui.docx_check, self.ui.baogao_shenghe,
|
||
"检测到报告审核人员更改,自动同步审核人员栏..."))
|
||
# 乙方公司/检查公司/结论公司
|
||
def sync_bianhao(source):
|
||
if (source.text() != self.ui.jizu_bianhao.text() or
|
||
source.text() != self.ui.jizu_bianhao2.text() or
|
||
source.text() != self.ui.jizu_type_2.text()):
|
||
self.ui.jizu_bianhao.setText(source.text())
|
||
self.ui.jizu_bianhao2.setText(source.text())
|
||
self.ui.jizu_type_2.setText(source.text())
|
||
self.ui.messagebrowser.append("检测到机组编号更改,自动同步所有机组编号栏...")
|
||
self.ui.jizu_bianhao.editingFinished.connect(lambda: sync_bianhao(self.ui.jizu_bianhao))
|
||
self.ui.jizu_bianhao2.editingFinished.connect(lambda: sync_bianhao(self.ui.jizu_bianhao2))
|
||
self.ui.jizu_type_2.editingFinished.connect(lambda: sync_bianhao(self.ui.jizu_type_2))
|
||
|
||
# 乙方公司/检查公司/结论公司
|
||
def sync_companies(source):
|
||
if (source.text() != self.ui.Yi_Company.text() or
|
||
source.text() != self.ui.Jiancha_company.text() or
|
||
source.text() != self.ui.conclusion_company.text()):
|
||
self.ui.Yi_Company.setText(source.text())
|
||
self.ui.Jiancha_company.setText(source.text())
|
||
self.ui.conclusion_company.setText(source.text())
|
||
self.ui.messagebrowser.append("检测到公司名称更改,自动同步所有公司名称栏...")
|
||
|
||
self.ui.Yi_Company.editingFinished.connect(lambda: sync_companies(self.ui.Yi_Company))
|
||
self.ui.Jiancha_company.editingFinished.connect(lambda: sync_companies(self.ui.Jiancha_company))
|
||
self.ui.conclusion_company.editingFinished.connect(lambda: sync_companies(self.ui.conclusion_company))
|
||
|
||
|
||
|
||
self.ui.calendarWidget.selectionChanged.connect(lambda: self.tongbu_time())
|
||
|
||
self.ui.waibu_add_text.clicked.connect(lambda: self.add_waibu_text())
|
||
self.ui.neibu_add_text.clicked.connect(lambda: self.add_neibu_text())
|
||
self.ui.fanglei_add_text.clicked.connect(lambda: self.add_fanglei_text())
|
||
|
||
self.ui.start_init_quexian.clicked.connect(lambda: self.start_init_quexian())
|
||
self.ui.start_init_huizong.clicked.connect(lambda: self.start_init_huizong())
|
||
|
||
self.bind_directory_browser(self.ui.muban_dir, self.ui.get_muban)
|
||
self.bind_directory_browser(self.ui.output_dir, self.ui.get_output)
|
||
self.bind_directory_browser(self.ui.save_dir, self.ui.get_save)
|
||
|
||
self.ui.checkBox.checkStateChanged.connect(lambda: self.set_tab_fill_disable(self.ui.checkBox, 1))
|
||
self.ui.checkBox_8.checkStateChanged.connect(lambda: self.set_tab_fill_disable(self.ui.checkBox_8, 3))
|
||
self.ui.checkBox_9.checkStateChanged.connect(lambda: self.set_tab_fill_disable(self.ui.checkBox_9, 4))
|
||
self.ui.checkBox_10.checkStateChanged.connect(lambda: self.set_tab_fill_disable(self.ui.checkBox_10, 5))
|
||
self.ui.checkBox_11.checkStateChanged.connect(lambda: self.set_tab_fill_disable(self.ui.checkBox_11, 6))
|
||
self.ui.checkBox_13.checkStateChanged.connect(lambda: self.set_tab_fill_disable(self.ui.checkBox_13, 7))
|
||
#日期/日历绑定
|
||
self.connect_calendar_to_lineedit_via_button(self.ui.jiancha_calander, self.ui.jiancha_riqi)
|
||
self.connect_calendar_to_lineedit_via_button(self.ui.chuli_calader, self.ui.chulishijian)
|
||
self.connect_calendar_to_lineedit_via_button(self.ui.shenghe_calendar, self.ui.shenghe_shijian)
|
||
|
||
self.connect_timeedit_to_lineedit(self.ui.timeEdit, self.ui.jiancha_shijian)
|
||
|
||
self.ui.choose_picture_file.clicked.connect(lambda: self.choose_file_to_get_picture_num())
|
||
|
||
def choose_file_to_get_picture_num(self):
|
||
"""选择文件夹,计算文件夹内图片数量并生成略缩图,并记录文件夹路径。
|
||
"""
|
||
# 打开文件夹选择对话框,允许多选
|
||
folder_path = QFileDialog.getExistingDirectory(self, "选择文件夹")
|
||
|
||
if folder_path:
|
||
self.total_picture_dir = folder_path
|
||
self.total_picture_count = 0
|
||
self.recursive_count_images(folder_path)
|
||
self.ui.label_18.setText('路径:' + str(folder_path))
|
||
self.ui.messagebrowser.append(f"图片总数: {self.total_picture_count}、文件夹路径:{folder_path}")
|
||
self.ui.total_picture_num.setText(str(self.total_picture_count))
|
||
self.ui.messagebrowser.append(f"已选择图片目录项,程序生成时会自动读取这些目录进行略缩图创建")
|
||
output_dir = self.Picture_dir
|
||
if os.path.exists(output_dir + '/merged_thumbnail.jpg'):
|
||
self.ui.messagebrowser.append(f"!!!报告生成图片路径已有略缩图,如需使用本功能生成,请先前往目录:{output_dir} 删除略缩图 merged_thumbnail.jpg")
|
||
|
||
def recursive_count_images(self, path):
|
||
"""
|
||
递归遍历文件夹,统计图片数量
|
||
"""
|
||
for root, dirs, files in os.walk(path):
|
||
for file in files:
|
||
# 检查文件扩展名是否为图片格式
|
||
if file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff')):
|
||
self.total_picture_count += 1
|
||
|
||
def connect_timeedit_to_lineedit(self, timeedit: 'QTimeEdit', lineedit: 'QLineEdit'):
|
||
"""
|
||
将时间编辑组件的时间连接到LineEdit,格式化为 时:分 格式
|
||
|
||
参数:
|
||
timeedit: QTimeEdit 时间编辑组件
|
||
lineedit: QLineEdit 文本输入框组件
|
||
"""
|
||
# 定义时间转换函数
|
||
def update_lineedit_time(time: QTime):
|
||
# 将时间转换为 时:分 格式 (HH:mm)
|
||
time_str = time.toString("HH:mm")
|
||
lineedit.setText(time_str)
|
||
|
||
# 连接时间编辑的时间变化信号到更新函数
|
||
timeedit.timeChanged.connect(update_lineedit_time)
|
||
|
||
# 初始设置当前时间
|
||
update_lineedit_time(timeedit.time())
|
||
|
||
def _init_base_info(self, info):
|
||
if not info:
|
||
return
|
||
global jituan_name, fengchang_name, jizu_type, company_name_yi, company_name_jia, project_location, fuzeren, phone_fuzeren, Y1, Y2, Y3, xiangmuguige
|
||
#获取项目基本信息。
|
||
self.ui.output_dir.setText('\\'.join(self.Picture_dir.split('\\')[:-1])) #插入图片存放路径
|
||
xiangmubaseinfo = info
|
||
jituan_name, fengchang_name, jizu_type, company_name_yi, company_name_jia, project_location, fuzeren, phone_fuzeren, Y1, Y2, Y3, xiangmuguige = xiangmubaseinfo.get_base_info()
|
||
|
||
def clear_tab_contents(self, tab : QtWidgets.QTabWidget):
|
||
"""清除tab中的所有子控件和布局"""
|
||
if not tab.layout():
|
||
return
|
||
|
||
# 保存布局的间距等属性
|
||
old_spacing = tab.layout().spacing()
|
||
old_contents_margins = tab.layout().contentsMargins()
|
||
|
||
# 清除所有子控件
|
||
self.clear_layout(tab.layout())
|
||
|
||
# 重新设置布局属性
|
||
tab.layout().setSpacing(old_spacing)
|
||
tab.layout().setContentsMargins(old_contents_margins)
|
||
|
||
def clear_layout(self, layout):
|
||
"""递归清除布局中的所有子控件"""
|
||
while layout.count():
|
||
item = layout.takeAt(0)
|
||
if item.widget():
|
||
item.widget().deleteLater()
|
||
elif item.layout():
|
||
self.clear_layout(item.layout())
|
||
|
||
def load_tabs_from_saved_data(self, tab, saved_data):
|
||
"""
|
||
从保存的数据加载标签页内容到给定的tab对象中
|
||
|
||
参数:
|
||
tab: 你在Qt Designer中创建的容器对象
|
||
saved_data: 包含保存的缺陷信息的字典
|
||
"""
|
||
try:
|
||
# 清除tab中现有的内容
|
||
self.clear_tab_contents(tab)
|
||
|
||
# 确保 tab 有一个新的布局
|
||
if tab.layout() is None:
|
||
tab.setLayout(QVBoxLayout())
|
||
else:
|
||
# 如果已有布局,确保它是空的
|
||
self.clear_layout(tab.layout())
|
||
|
||
# 获取或创建主布局
|
||
layout = tab.layout() if tab.layout() else QVBoxLayout(tab)
|
||
layout.setSpacing(10)
|
||
|
||
# 重置存储结构
|
||
self.table_infos_modules = [[], [], []]
|
||
self.table_info_to_save = []
|
||
|
||
# 计算总缺陷数
|
||
total_defects = sum(len(images) for images in saved_data)
|
||
|
||
# 添加缺陷数量标签
|
||
num_label = QLabel(f"文件夹内总缺陷数: {total_defects}")
|
||
num_label.setStyleSheet("font-size: 16px; font-weight: bold;")
|
||
layout.addWidget(num_label)
|
||
|
||
# 创建QTabWidget(确保只创建一个)
|
||
if not hasattr(self, 'tab_widget') or not isinstance(getattr(self, 'tab_widget', None), QTabWidget):
|
||
self.tab_widget = QTabWidget()
|
||
else:
|
||
# 如果已存在,清除所有页签
|
||
while self.tab_widget.count():
|
||
self.tab_widget.removeTab(0)
|
||
|
||
layout.addWidget(self.tab_widget)
|
||
|
||
yepians = [Y1, Y2, Y3]
|
||
self.quexian_images = [[], [], []]
|
||
|
||
# 为每个叶片的每张图片创建标签页
|
||
image_num = 0
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"加载数据错误: {str(e)}")
|
||
return
|
||
|
||
for yepian_idx, Y in enumerate(yepians):
|
||
for img_idx, image_info in enumerate(saved_data[yepian_idx]):
|
||
image_path = image_info['image_path']
|
||
|
||
# 更新主进度
|
||
current_progress = sum(len(saved_data[i]) for i in range(yepian_idx)) + img_idx + 1
|
||
self.ui.messagebrowser.append(f"加载图片 {current_progress}/{total_defects}")
|
||
|
||
# 创建单个标签页
|
||
page = QWidget()
|
||
main_layout = QVBoxLayout(page)
|
||
|
||
# 创建水平布局容器(图片+信息 与 组合框+输入框)
|
||
container = QWidget()
|
||
h_layout = QHBoxLayout(container)
|
||
h_layout.setSpacing(1)
|
||
|
||
# 左侧:图片和信息区域
|
||
left_widget = QWidget()
|
||
left_layout = QVBoxLayout(left_widget)
|
||
left_layout.setSpacing(0)
|
||
|
||
# 添加图片路径信息
|
||
path_label = QLabel(f"图片地址(双击打开图片): {image_path}")
|
||
path_label.setWordWrap(True)
|
||
path_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
path_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse | Qt.TextInteractionFlag.TextSelectableByKeyboard)
|
||
left_layout.addWidget(path_label)
|
||
|
||
# 添加图片
|
||
image_label = DLabel(getevent=False, messagebrowser=self.ui.messagebrowser)
|
||
image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
image_label.current_path = image_path
|
||
try:
|
||
# 加载图片
|
||
MAX_SIZE = 5 * 1024 * 1024 # 5MB
|
||
try:
|
||
file_size = os.path.getsize(image_path)
|
||
if file_size <= MAX_SIZE:
|
||
pixmap = QPixmap(image_path)
|
||
else:
|
||
self.progress_dialog.setLabelText(f"压缩图片{image_path}中...")
|
||
# 图片过大,进行压缩处理
|
||
from PIL import Image
|
||
import io
|
||
|
||
img = Image.open(image_path)
|
||
quality = 95 # 初始质量
|
||
|
||
while quality > 10: # 设置最低质量限制
|
||
buffer = io.BytesIO()
|
||
img.save(buffer, format="JPEG", quality=quality)
|
||
if buffer.tell() <= MAX_SIZE:
|
||
break
|
||
quality -= 5
|
||
|
||
buffer.seek(0)
|
||
pixmap = QPixmap()
|
||
pixmap.loadFromData(buffer.getvalue())
|
||
|
||
if not pixmap.isNull():
|
||
scaled_pixmap = pixmap.scaled(300, 200, aspectMode=Qt.AspectRatioMode.IgnoreAspectRatio)
|
||
image_label.setPixmap(scaled_pixmap)
|
||
else:
|
||
image_label.setText(f"无法加载图片: {image_path}")
|
||
except Exception as e:
|
||
image_label.setText(f"图片加载错误: {str(e)}")
|
||
except Exception as e:
|
||
image_label.setText(f"图片加载错误: {str(e)}")
|
||
|
||
left_layout.addWidget(image_label)
|
||
h_layout.addWidget(left_widget)
|
||
|
||
# 右侧:组合框和输入框区域
|
||
right_widget = QWidget()
|
||
right_layout = QVBoxLayout(right_widget)
|
||
|
||
# 添加水平布局的ComboBox组
|
||
combo_layout = QHBoxLayout()
|
||
combo_layout.setSpacing(0)
|
||
|
||
# 可见程度ComboBox
|
||
visibility_widget = QWidget()
|
||
visibility_layout = QHBoxLayout(visibility_widget)
|
||
visibility_layout.setSpacing(0)
|
||
visibility_combo = QComboBox()
|
||
visibility_combo.addItems(["轻微", "一般", "重要", "严重"])
|
||
visibility_combo.setCurrentIndex(image_info['visibility']) # 从保存的数据设置值
|
||
visibility_label = QLabel("可见程度:")
|
||
visibility_layout.addWidget(visibility_label)
|
||
visibility_layout.addWidget(visibility_combo)
|
||
combo_layout.addWidget(visibility_widget)
|
||
|
||
# 严重程度ComboBox
|
||
severity_widget = QWidget()
|
||
severity_layout = QHBoxLayout(severity_widget)
|
||
severity_layout.setSpacing(0)
|
||
severity_combo = QComboBox()
|
||
severity_combo.addItems(["轻微", "一般", "重要", "严重"])
|
||
severity_combo.setCurrentIndex(image_info['severity']) # 从保存的数据设置值
|
||
severity_label = QLabel("严重程度:")
|
||
severity_layout.addWidget(severity_label)
|
||
severity_layout.addWidget(severity_combo)
|
||
combo_layout.addWidget(severity_widget)
|
||
|
||
# 紧急程度ComboBox
|
||
urgency_widget = QWidget()
|
||
urgency_layout = QHBoxLayout(urgency_widget)
|
||
urgency_layout.setSpacing(0)
|
||
urgency_combo = QComboBox()
|
||
urgency_combo.addItems(["不紧急", "一般", "紧急", "非常紧急"])
|
||
urgency_combo.setCurrentIndex(image_info['urgency']) # 从保存的数据设置值
|
||
urgency_combo.setMinimumWidth(74)
|
||
urgency_label = QLabel("紧急程度:")
|
||
urgency_layout.addWidget(urgency_label)
|
||
urgency_layout.addWidget(urgency_combo)
|
||
combo_layout.addWidget(urgency_widget)
|
||
|
||
input_layout = QVBoxLayout()
|
||
# 缺陷类型输入框
|
||
defect_type_widget = QWidget()
|
||
defect_type_layout = QVBoxLayout(defect_type_widget)
|
||
defect_type_combo = SmartDropdown('缺陷类型', self.ui.messagebrowser, '缺陷类型:')
|
||
defect_type_combo.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
defect_type_combo.current_index = image_info['defect_type'] # 从保存的数据设置值
|
||
defect_type_layout.addWidget(defect_type_combo)
|
||
input_layout.addWidget(defect_type_widget)
|
||
|
||
# 缺陷位置输入框
|
||
defect_location_widget = QWidget()
|
||
defect_location_layout = QVBoxLayout(defect_location_widget)
|
||
defect_location_label = QLabel("缺陷位置:")
|
||
defect_location_label.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
defect_location_edit = QLineEdit()
|
||
defect_location_edit.setText(image_info['defect_location']) # 从保存的数据设置值
|
||
defect_location_edit.setPlaceholderText("如:叶片ps面距叶根3m处")
|
||
defect_location_edit.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
defect_location_layout.addWidget(defect_location_label)
|
||
defect_location_layout.addWidget(defect_location_edit)
|
||
input_layout.addWidget(defect_location_widget)
|
||
|
||
# 缺陷尺寸输入框
|
||
defect_size_widget = QWidget()
|
||
defect_size_layout = QVBoxLayout(defect_size_widget)
|
||
defect_size_label = QLabel("缺陷尺寸(mm):")
|
||
defect_size_label.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
defect_size_edit = QLineEdit()
|
||
defect_size_edit.setText(image_info['defect_size']) # 从保存的数据设置值
|
||
defect_size_edit.setPlaceholderText("如:弦向100mm,轴向800mm")
|
||
defect_size_edit.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
defect_size_layout.addWidget(defect_size_label)
|
||
defect_size_layout.addWidget(defect_size_edit)
|
||
input_layout.addWidget(defect_size_widget)
|
||
|
||
right_layout.setSpacing(0)
|
||
right_layout.addLayout(combo_layout)
|
||
right_layout.addLayout(input_layout)
|
||
h_layout.addWidget(right_widget)
|
||
|
||
main_layout.addWidget(container)
|
||
main_layout.setSpacing(0)
|
||
|
||
# 添加维修建议
|
||
repair_suggestion_label = QLabel("维修建议:")
|
||
repair_suggestion_edit = QTextEdit()
|
||
repair_suggestion_edit.setPlainText(image_info['repair_suggestion']) # 从保存的数据设置值
|
||
repair_suggestion_edit.setPlaceholderText("暂不处理/观察运行/建议(尽快)打磨维修/(雷雨季前)修复")
|
||
repair_suggestion_edit.setMaximumHeight(100)
|
||
|
||
repair_layout = QVBoxLayout()
|
||
repair_layout.addWidget(repair_suggestion_label)
|
||
repair_layout.addWidget(repair_suggestion_edit)
|
||
main_layout.addLayout(repair_layout)
|
||
|
||
self.tab_widget.addTab(page, f"叶片:{Y} 缺陷 {img_idx+1}")
|
||
|
||
# 为ComboBox设置objectName
|
||
visibility_combo.setObjectName(f"visibility_combo_{image_num}")
|
||
severity_combo.setObjectName(f"severity_combo_{image_num}")
|
||
urgency_combo.setObjectName(f"urgency_combo_{image_num}")
|
||
|
||
# 为LineEdit设置objectName
|
||
defect_type_combo.setObjectName(f"defect_type_edit_{image_num}")
|
||
defect_location_edit.setObjectName(f"defect_location_edit_{image_num}")
|
||
defect_size_edit.setObjectName(f"defect_size_edit_{image_num}")
|
||
|
||
# 为TextEdit设置objectName
|
||
repair_suggestion_edit.setObjectName(f"repair_suggestion_edit_{image_num}")
|
||
image_num += 1
|
||
|
||
self.ui.messagebrowser.append(f"""设置组件对象名称:{visibility_combo.objectName()}, {severity_combo.objectName()}, {urgency_combo.objectName()}, {defect_type_combo.objectName()}, {defect_location_edit.objectName()}, {defect_size_edit.objectName()}, {repair_suggestion_edit.objectName()}""")
|
||
|
||
# 将当前图片的所有组件对象存储到对应的叶片列表中
|
||
loaded_image_info = {
|
||
'image_path': image_path,
|
||
'path_label': path_label,
|
||
'image_label': image_label,
|
||
'visibility_combo': visibility_combo,
|
||
'severity_combo': severity_combo,
|
||
'urgency_combo': urgency_combo,
|
||
'defect_type_combo': defect_type_combo,
|
||
'defect_location_edit': defect_location_edit,
|
||
'defect_size_edit': defect_size_edit,
|
||
'repair_suggestion_edit': repair_suggestion_edit,
|
||
}
|
||
self.table_infos_modules[yepian_idx].append(loaded_image_info)
|
||
|
||
self.ui.checkBox_13.raise_() #置顶
|
||
self.has_init_inspection = True
|
||
self.ui.start_init_quexian.setDisabled(True)
|
||
self.ui.messagebrowser.append("从保存的数据加载完成")
|
||
|
||
def setup_tabs(self, tab, num, Picture_dir):
|
||
"""
|
||
在给定的tab对象中设置带有num个页面的QTabWidget
|
||
|
||
参数:
|
||
tab: 你在Qt Designer中创建的容器对象
|
||
num: 要创建的标签页数量
|
||
Picture_dir: 图片目录路径
|
||
"""
|
||
# 清除tab中现有的内容
|
||
self.clear_tab_contents(tab)
|
||
|
||
# 确保 tab 有一个新的布局(解决第二个问题)
|
||
if tab.layout() is None:
|
||
tab.setLayout(QVBoxLayout())
|
||
else:
|
||
# 如果已有布局,确保它是空的
|
||
self.clear_layout(tab.layout())
|
||
|
||
# 获取或创建主布局
|
||
layout = tab.layout() if tab.layout() else QVBoxLayout(tab)
|
||
layout.setSpacing(10)
|
||
|
||
# 重置存储结构
|
||
self.table_infos_modules = [[], [], []]
|
||
self.table_info_to_save = []
|
||
|
||
# 添加缺陷数量标签
|
||
num_label = QLabel(f"文件夹内总缺陷数: {num}")
|
||
num_label.setStyleSheet("font-size: 16px; font-weight: bold;")
|
||
layout.addWidget(num_label)
|
||
|
||
# 创建QTabWidget(确保只创建一个)
|
||
if not hasattr(self, 'tab_widget') or not isinstance(getattr(self, 'tab_widget', None), QTabWidget):
|
||
self.tab_widget = QTabWidget()
|
||
else:
|
||
# 如果已存在,清除所有页签
|
||
while self.tab_widget.count():
|
||
self.tab_widget.removeTab(0)
|
||
|
||
layout.addWidget(self.tab_widget)
|
||
|
||
yepians = [Y1, Y2, Y3]
|
||
self.quexian_images = [[], [], []]
|
||
|
||
try:
|
||
# 收集所有缺陷图片路径,按叶片分类
|
||
for i, Y in enumerate(yepians):
|
||
# 获取图片路径
|
||
image_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp'}
|
||
current_images = []
|
||
if self.ifwaibu:
|
||
for root, _, files in os.walk(os.path.join(Picture_dir, Y, "外缺陷图")):
|
||
for file in files:
|
||
if os.path.splitext(file)[1].lower() in image_extensions:
|
||
current_images.append(os.path.join(root, file))
|
||
if self.ifneibu:
|
||
for root, _, files in os.walk(os.path.join(Picture_dir, Y, "内缺陷图")):
|
||
for file in files:
|
||
if os.path.splitext(file)[1].lower() in image_extensions:
|
||
current_images.append(os.path.join(root, file))
|
||
self.quexian_images[i] = current_images
|
||
except Exception as e:
|
||
self.ui.messagebrowser.append(f"缺陷图获取失败: {e}")
|
||
|
||
# 设置主进度条
|
||
total_images = sum(len(images) for images in self.quexian_images)
|
||
|
||
# 为每个叶片的每张图片创建标签页
|
||
image_num = 0
|
||
for yepian_idx, Y in enumerate(yepians):
|
||
for img_idx, image_path in enumerate(self.quexian_images[yepian_idx]):
|
||
# 更新主进度
|
||
current_progress = sum(len(self.quexian_images[i]) for i in range(yepian_idx)) + img_idx + 1
|
||
self.ui.messagebrowser.append(f"处理图片 {current_progress}/{total_images}")
|
||
|
||
# 创建单个标签页
|
||
page = QWidget()
|
||
main_layout = QVBoxLayout(page)
|
||
|
||
# 创建水平布局容器(图片+信息 与 组合框+输入框)
|
||
container = QWidget()
|
||
h_layout = QHBoxLayout(container)
|
||
h_layout.setSpacing(1)
|
||
|
||
# 左侧:图片和信息区域
|
||
left_widget = QWidget()
|
||
left_layout = QVBoxLayout(left_widget)
|
||
left_layout.setSpacing(0)
|
||
|
||
# 添加图片路径信息
|
||
path_label = QLabel(f"图片地址(双击打开图片): {image_path}")
|
||
path_label.setWordWrap(True)
|
||
path_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
path_label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse | Qt.TextInteractionFlag.TextSelectableByKeyboard)
|
||
left_layout.addWidget(path_label)
|
||
|
||
# 添加图片
|
||
image_label = DLabel(getevent=False, messagebrowser=self.ui.messagebrowser)
|
||
image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
image_label.current_path = image_path
|
||
|
||
# image_button = QPushButton("编辑图片")
|
||
# left_layout.addWidget(image_button)
|
||
#self.bind_edit_function(image_label, image_button)
|
||
try:
|
||
# 加载图片
|
||
MAX_SIZE = 5 * 1024 * 1024 # 5MB
|
||
try:
|
||
file_size = os.path.getsize(image_path)
|
||
if file_size <= MAX_SIZE:
|
||
pixmap = QPixmap(image_path)
|
||
else:
|
||
self.progress_dialog.setLabelText(f"压缩图片{image_path}中...")
|
||
# 图片过大,进行压缩处理
|
||
from PIL import Image
|
||
import io
|
||
|
||
img = Image.open(image_path)
|
||
quality = 95 # 初始质量
|
||
|
||
while quality > 10: # 设置最低质量限制
|
||
buffer = io.BytesIO()
|
||
img.save(buffer, format="JPEG", quality=quality)
|
||
if buffer.tell() <= MAX_SIZE:
|
||
break
|
||
quality -= 5
|
||
|
||
buffer.seek(0)
|
||
pixmap = QPixmap()
|
||
pixmap.loadFromData(buffer.getvalue())
|
||
|
||
if not pixmap.isNull():
|
||
scaled_pixmap = pixmap.scaled(300, 200, aspectMode=Qt.AspectRatioMode.IgnoreAspectRatio)
|
||
image_label.setPixmap(scaled_pixmap)
|
||
else:
|
||
image_label.setText(f"无法加载图片: {image_path}")
|
||
except Exception as e:
|
||
image_label.setText(f"图片加载错误: {str(e)}")
|
||
except Exception as e:
|
||
image_label.setText(f"图片加载错误: {str(e)}")
|
||
|
||
left_layout.addWidget(image_label)
|
||
h_layout.addWidget(left_widget)
|
||
|
||
# 右侧:组合框和输入框区域
|
||
right_widget = QWidget()
|
||
right_layout = QVBoxLayout(right_widget)
|
||
|
||
# 添加水平布局的ComboBox组
|
||
combo_layout = QHBoxLayout()
|
||
combo_layout.setSpacing(0)
|
||
|
||
# 可见程度ComboBox
|
||
visibility_widget = QWidget()
|
||
visibility_layout = QHBoxLayout(visibility_widget)
|
||
visibility_layout.setSpacing(0)
|
||
visibility_combo = QComboBox()
|
||
visibility_combo.addItems(["轻微", "一般", "重要", "严重"])
|
||
visibility_combo.setCurrentIndex(1) # 默认选择"一般"
|
||
visibility_label = QLabel("可见程度:")
|
||
visibility_layout.addWidget(visibility_label)
|
||
visibility_layout.addWidget(visibility_combo)
|
||
combo_layout.addWidget(visibility_widget)
|
||
|
||
# 严重程度ComboBox
|
||
severity_widget = QWidget()
|
||
severity_layout = QHBoxLayout(severity_widget)
|
||
severity_layout.setSpacing(0)
|
||
severity_combo = QComboBox()
|
||
severity_combo.addItems(["轻微", "一般", "重要", "严重"])
|
||
severity_combo.setCurrentIndex(1) # 默认选择"一般"
|
||
severity_label = QLabel("严重程度:")
|
||
severity_layout.addWidget(severity_label)
|
||
severity_layout.addWidget(severity_combo)
|
||
combo_layout.addWidget(severity_widget)
|
||
|
||
# 紧急程度ComboBox
|
||
urgency_widget = QWidget()
|
||
urgency_layout = QHBoxLayout(urgency_widget)
|
||
urgency_layout.setSpacing(0)
|
||
urgency_combo = QComboBox()
|
||
urgency_combo.addItems(["不紧急", "一般", "紧急", "非常紧急"])
|
||
urgency_combo.setCurrentIndex(1) # 默认选择"一般"
|
||
urgency_combo.setMinimumWidth(74)
|
||
urgency_label = QLabel("紧急程度:")
|
||
urgency_layout.addWidget(urgency_label)
|
||
urgency_layout.addWidget(urgency_combo)
|
||
combo_layout.addWidget(urgency_widget)
|
||
|
||
input_layout = QVBoxLayout()
|
||
# 缺陷类型输入框
|
||
defect_type_widget = QWidget()
|
||
defect_type_layout = QVBoxLayout(defect_type_widget)
|
||
defect_type_combo = SmartDropdown('缺陷类型', self.ui.messagebrowser, '缺陷类型')
|
||
defect_type_combo.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
defect_type_layout.addWidget(defect_type_combo)
|
||
input_layout.addWidget(defect_type_widget)
|
||
|
||
# 缺陷位置输入框
|
||
defect_location_widget = QWidget()
|
||
defect_location_layout = QVBoxLayout(defect_location_widget)
|
||
defect_location_label = QLabel("缺陷位置:")
|
||
defect_location_label.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
defect_location_edit = QLineEdit()
|
||
defect_location_edit.setPlaceholderText("如:叶片ps面距叶根3m处")
|
||
defect_location_edit.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
defect_location_layout.addWidget(defect_location_label)
|
||
defect_location_layout.addWidget(defect_location_edit)
|
||
input_layout.addWidget(defect_location_widget)
|
||
|
||
# 缺陷尺寸输入框
|
||
defect_size_widget = QWidget()
|
||
defect_size_layout = QVBoxLayout(defect_size_widget)
|
||
defect_size_label = QLabel("缺陷尺寸(mm):")
|
||
defect_size_label.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
defect_size_edit = QLineEdit()
|
||
defect_size_edit.setPlaceholderText("如:弦向100mm,轴向800mm")
|
||
defect_size_edit.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
defect_size_layout.addWidget(defect_size_label)
|
||
defect_size_layout.addWidget(defect_size_edit)
|
||
input_layout.addWidget(defect_size_widget)
|
||
|
||
right_layout.setSpacing(0)
|
||
right_layout.addLayout(combo_layout)
|
||
right_layout.addLayout(input_layout)
|
||
h_layout.addWidget(right_widget)
|
||
|
||
main_layout.addWidget(container)
|
||
main_layout.setSpacing(0)
|
||
|
||
# 添加维修建议
|
||
repair_suggestion_label = QLabel("维修建议:")
|
||
repair_suggestion_edit = QTextEdit()
|
||
repair_suggestion_edit.setPlaceholderText("暂不处理/观察运行/建议(尽快)打磨维修/(雷雨季前)修复")
|
||
repair_suggestion_edit.setMaximumHeight(100)
|
||
|
||
repair_layout = QVBoxLayout()
|
||
repair_layout.addWidget(repair_suggestion_label)
|
||
repair_layout.addWidget(repair_suggestion_edit)
|
||
main_layout.addLayout(repair_layout)
|
||
|
||
self.tab_widget.addTab(page, f"叶片:{Y} 缺陷 {img_idx+1}")
|
||
|
||
# 为ComboBox设置objectName
|
||
visibility_combo.setObjectName(f"visibility_combo_{image_num}")
|
||
severity_combo.setObjectName(f"severity_combo_{image_num}")
|
||
urgency_combo.setObjectName(f"urgency_combo_{image_num}")
|
||
|
||
# 为LineEdit设置objectName
|
||
defect_type_combo.setObjectName(f"defect_type_edit_{image_num}")
|
||
defect_location_edit.setObjectName(f"defect_location_edit_{image_num}")
|
||
defect_size_edit.setObjectName(f"defect_size_edit_{image_num}")
|
||
|
||
# 为TextEdit设置objectName
|
||
repair_suggestion_edit.setObjectName(f"repair_suggestion_edit_{image_num}")
|
||
image_num += 1
|
||
|
||
self.ui.messagebrowser.append(f"""设置组件对象名称:{visibility_combo.objectName()}, {severity_combo.objectName()}, {urgency_combo.objectName()}, {defect_type_combo.objectName()}, {defect_location_edit.objectName()}, {defect_size_edit.objectName()}, {repair_suggestion_edit.objectName()}""")
|
||
# 将当前图片的所有组件对象存储到对应的叶片列表中
|
||
image_info = {
|
||
'image_path': image_path,
|
||
'path_label': path_label,
|
||
'image_label': image_label,
|
||
'visibility_combo': visibility_combo,
|
||
'severity_combo': severity_combo,
|
||
'urgency_combo': urgency_combo,
|
||
'defect_type_combo': defect_type_combo,
|
||
'defect_location_edit': defect_location_edit,
|
||
'defect_size_edit': defect_size_edit,
|
||
'repair_suggestion_edit': repair_suggestion_edit,
|
||
}
|
||
self.table_infos_modules[yepian_idx].append(image_info)
|
||
self.ui.checkBox_13.raise_() #置顶
|
||
self.has_init_inspection = True
|
||
self.ui.start_init_quexian.setDisabled(True)
|
||
self.ui.messagebrowser.append("初次初始化完成,如需要重新初始化,请保存项目信息,重新加载")
|
||
|
||
# def bind_edit_function(self, dlabel, button):
|
||
# """绑定按钮和DLabel类,点击按钮触发编辑功能
|
||
|
||
# Args:
|
||
# dlabel (DLabel): DLabel实例
|
||
# button (QPushButton): 触发编辑功能的按钮
|
||
# """
|
||
# button.clicked.connect(lambda: self._start_edit(dlabel))
|
||
|
||
# def _start_edit(self, dlabel):
|
||
# """开始编辑图片
|
||
|
||
# Args:
|
||
# dlabel (DLabel): DLabel实例
|
||
# """
|
||
# if not dlabel.current_path:
|
||
# QMessageBox.warning(dlabel, "警告", "请先加载图片")
|
||
# return
|
||
|
||
# # 创建编辑对话框
|
||
# dialog = QDialog(dlabel)
|
||
# dialog.setWindowTitle("图片编辑")
|
||
# dialog.setMinimumSize(800, 600)
|
||
|
||
# # 主布局
|
||
# main_layout = QVBoxLayout(dialog)
|
||
|
||
# # 创建图形视图和场景
|
||
# class ZoomableGraphicsView(QGraphicsView):
|
||
# def wheelEvent(self, event):
|
||
# zoom_factor = 1.15
|
||
# if event.angleDelta().y() > 0:
|
||
# self.scale(zoom_factor, zoom_factor)
|
||
# else:
|
||
# self.scale(1/zoom_factor, 1/zoom_factor)
|
||
|
||
# view = ZoomableGraphicsView()
|
||
# scene = QGraphicsScene()
|
||
# view.setScene(scene)
|
||
# view.setRenderHint(QPainter.Antialiasing)
|
||
# view.setRenderHint(QPainter.SmoothPixmapTransform)
|
||
# view.setDragMode(QGraphicsView.ScrollHandDrag)
|
||
|
||
# # 使用dlabel的load_image方法加载图片
|
||
# if not hasattr(dlabel, 'load_image'):
|
||
# QMessageBox.warning(dlabel, "错误", "DLabel缺少load_image方法")
|
||
# return
|
||
|
||
# # 获取当前图片路径
|
||
# original_path = dlabel.current_path
|
||
|
||
# # 加载图片
|
||
# pixmap = QPixmap(original_path)
|
||
# if pixmap.isNull():
|
||
# QMessageBox.warning(dlabel, "错误", "无法加载图片")
|
||
# return
|
||
|
||
# pixmap_item = QGraphicsPixmapItem(pixmap)
|
||
# scene.addItem(pixmap_item)
|
||
# scene.setSceneRect(QRectF(pixmap.rect()))
|
||
|
||
# # 创建选择标记(更粗的红色边框,透明填充)
|
||
# selection_marker = QGraphicsRectItem(0, 0, 0, 0)
|
||
# selection_marker.setPen(QPen(QColor(255, 0, 0), 10)) # 加粗到5像素
|
||
# selection_marker.setBrush(Qt.NoBrush)
|
||
# selection_marker.setVisible(False)
|
||
# scene.addItem(selection_marker)
|
||
|
||
# # 添加一个状态变量来跟踪是否有有效选择
|
||
# has_valid_selection = False
|
||
|
||
# # 按钮布局
|
||
# btn_layout = QHBoxLayout()
|
||
# btn_layout.setContentsMargins(5, 5, 5, 5)
|
||
# btn_layout.setSpacing(10)
|
||
|
||
# # 选择模式按钮 - 改为文字按钮
|
||
# btn_select = QPushButton("选择区域")
|
||
# btn_select.setToolTip("选择要裁剪的区域")
|
||
# btn_select.setCheckable(True)
|
||
# btn_select.setCursor(QCursor(Qt.PointingHandCursor))
|
||
# btn_select.setFixedSize(100, 30)
|
||
# btn_select.setStyleSheet("""
|
||
# QPushButton {
|
||
# background-color: rgba(200, 200, 200, 150);
|
||
# border-radius: 4px;
|
||
# border: 1px solid #888;
|
||
# }
|
||
# QPushButton:checked {
|
||
# background-color: rgba(100, 150, 255, 150);
|
||
# }
|
||
# """)
|
||
|
||
# # 适应窗口按钮 - 改为文字按钮
|
||
# btn_fit = QPushButton("适应窗口")
|
||
# btn_fit.setToolTip("将图片适应窗口大小")
|
||
# btn_fit.setCursor(QCursor(Qt.PointingHandCursor))
|
||
# btn_fit.setFixedSize(100, 30)
|
||
# btn_fit.setStyleSheet("""
|
||
# QPushButton {
|
||
# background-color: rgba(200, 200, 200, 150);
|
||
# border-radius: 4px;
|
||
# border: 1px solid #888;
|
||
# }
|
||
# """)
|
||
|
||
# # 保存按钮
|
||
# btn_save = QPushButton("保存修改")
|
||
# btn_save.setToolTip("保存修改并覆盖原图")
|
||
# btn_save.setCursor(QCursor(Qt.PointingHandCursor))
|
||
# btn_save.setFixedSize(100, 30)
|
||
# btn_save.setStyleSheet("""
|
||
# QPushButton {
|
||
# background-color: rgba(200, 200, 200, 150);
|
||
# border-radius: 4px;
|
||
# border: 1px solid #888;
|
||
# }
|
||
# QPushButton:hover {
|
||
# background-color: rgba(100, 255, 100, 150);
|
||
# }
|
||
# """)
|
||
|
||
# # 添加按钮到布局
|
||
# btn_layout.addWidget(btn_select)
|
||
# btn_layout.addWidget(btn_fit)
|
||
# btn_layout.addWidget(btn_save)
|
||
# btn_layout.addStretch()
|
||
|
||
# # 添加到主布局
|
||
# main_layout.addWidget(view)
|
||
# main_layout.addLayout(btn_layout)
|
||
|
||
# # 状态变量
|
||
# is_selecting = False
|
||
# start_pos = QPointF()
|
||
# current_rect = QRectF()
|
||
|
||
# # 初始适应窗口
|
||
# def fit_to_view():
|
||
# view.fitInView(scene.sceneRect(), Qt.KeepAspectRatio)
|
||
|
||
# fit_to_view()
|
||
|
||
# # 切换选择模式
|
||
# def toggle_selection_mode(checked):
|
||
# nonlocal is_selecting
|
||
# is_selecting = checked
|
||
# if checked:
|
||
# view.setDragMode(QGraphicsView.NoDrag)
|
||
# selection_marker.setVisible(False)
|
||
# view.setCursor(QCursor(Qt.CrossCursor))
|
||
# else:
|
||
# view.setDragMode(QGraphicsView.ScrollHandDrag)
|
||
# selection_marker.setVisible(False)
|
||
# view.setCursor(QCursor(Qt.ArrowCursor))
|
||
|
||
# # 鼠标按下事件
|
||
# def mouse_press(event):
|
||
# nonlocal start_pos, current_rect, has_valid_selection
|
||
# if is_selecting and event.button() == Qt.LeftButton:
|
||
# # 使用 position() 替代 pos()
|
||
# pos = event.position() if hasattr(event, 'position') else event.posF() if hasattr(event, 'posF') else event.pos()
|
||
# start_pos = view.mapToScene(pos.toPoint())
|
||
# current_rect = QRectF(start_pos, QSizeF(0, 0))
|
||
# selection_marker.setRect(current_rect)
|
||
# selection_marker.setVisible(True)
|
||
# has_valid_selection = False
|
||
# else:
|
||
# QGraphicsView.mousePressEvent(view, event)
|
||
|
||
# # 鼠标移动事件
|
||
# def mouse_move(event):
|
||
# nonlocal has_valid_selection
|
||
# if is_selecting and selection_marker.isVisible() and (event.buttons() & Qt.LeftButton):
|
||
# # 使用 position() 替代 pos()
|
||
# pos = event.position() if hasattr(event, 'position') else event.posF() if hasattr(event, 'posF') else event.pos()
|
||
# current_pos = view.mapToScene(pos.toPoint())
|
||
# current_rect = QRectF(
|
||
# min(start_pos.x(), current_pos.x()),
|
||
# min(start_pos.y(), current_pos.y()),
|
||
# abs(current_pos.x() - start_pos.x()),
|
||
# abs(current_pos.y() - start_pos.y())
|
||
# )
|
||
# selection_marker.setRect(current_rect)
|
||
# has_valid_selection = current_rect.width() > 5 and current_rect.height() > 5
|
||
# self.ui.messagebrowser.append(f"当前选择区域:{current_rect}")
|
||
# else:
|
||
# QGraphicsView.mouseMoveEvent(view, event)
|
||
|
||
# # 鼠标释放事件
|
||
# def mouse_release(event):
|
||
# if is_selecting and event.button() == Qt.LeftButton:
|
||
# # 不需要额外处理,状态已在mouse_move中更新
|
||
# pass
|
||
# else:
|
||
# QGraphicsView.mouseReleaseEvent(view, event)
|
||
|
||
# # 保存裁剪并覆盖原图
|
||
# def save_cropped_image():
|
||
# nonlocal has_valid_selection, current_rect, original_path
|
||
# if not has_valid_selection:
|
||
# QMessageBox.warning(dialog, "警告", "请先选择有效的区域")
|
||
# return
|
||
|
||
# try:
|
||
# # 获取原始图片
|
||
# original_pixmap = QPixmap(original_path)
|
||
# if original_pixmap.isNull():
|
||
# QMessageBox.warning(dialog, "错误", "无法加载原始图片")
|
||
# return
|
||
|
||
# # 创建一个新的QPixmap作为画布,大小与原始图片相同
|
||
# result_pixmap = QPixmap(original_pixmap.size())
|
||
# result_pixmap.fill(Qt.transparent) # 透明背景
|
||
|
||
# # 创建QPainter来绘制
|
||
# painter = QPainter(result_pixmap)
|
||
|
||
# # 首先绘制原始图片
|
||
# painter.drawPixmap(0, 0, original_pixmap)
|
||
|
||
# # 设置画笔属性(红色,10像素宽)
|
||
# pen = QPen(QColor(255, 0, 0), 10)
|
||
# pen.setStyle(Qt.SolidLine)
|
||
# painter.setPen(pen)
|
||
# painter.setBrush(Qt.NoBrush) # 无填充
|
||
|
||
# # 将场景坐标转换为图片坐标
|
||
# pixmap_rect = pixmap_item.pixmap().rect()
|
||
# scene_rect = pixmap_item.sceneBoundingRect()
|
||
|
||
# # 计算缩放比例
|
||
# scale_x = pixmap_rect.width() / scene_rect.width()
|
||
# scale_y = pixmap_rect.height() / scene_rect.height()
|
||
|
||
# # 将选择区域转换为原始图片坐标
|
||
# draw_rect = QRect(
|
||
# int((current_rect.x() - scene_rect.x()) * scale_x),
|
||
# int((current_rect.y() - scene_rect.y()) * scale_y),
|
||
# int(current_rect.width() * scale_x),
|
||
# int(current_rect.height() * scale_y)
|
||
# )
|
||
|
||
# # 确保绘制区域在图片范围内
|
||
# draw_rect = draw_rect.intersected(pixmap_rect)
|
||
# if draw_rect.isEmpty():
|
||
# QMessageBox.warning(dialog, "错误", "绘制区域无效")
|
||
# return
|
||
|
||
# # 绘制红色矩形框
|
||
# painter.drawRect(draw_rect)
|
||
|
||
# # 结束绘制
|
||
# painter.end()
|
||
|
||
# # 保存图片,覆盖原图
|
||
# if not result_pixmap.save(original_path):
|
||
# QMessageBox.warning(dialog, "错误", "保存图片失败")
|
||
# return
|
||
|
||
# # 重新加载图片
|
||
# dlabel.load_image(original_path)
|
||
|
||
# QMessageBox.information(dialog, "成功", "图片已保存并重新加载")
|
||
# dialog.accept()
|
||
|
||
# except Exception as e:
|
||
# QMessageBox.critical(dialog, "错误", f"保存过程中发生错误: {str(e)}")
|
||
# # 添加快捷键支持
|
||
# def key_press_event(event):
|
||
# if event.key() == Qt.Key_F:
|
||
# fit_to_view()
|
||
# elif event.key() == Qt.Key_C:
|
||
# btn_select.setChecked(not btn_select.isChecked())
|
||
# elif event.key() == Qt.Key_S:
|
||
# save_cropped_image()
|
||
# else:
|
||
# QGraphicsView.keyPressEvent(view, event)
|
||
|
||
# # 连接信号
|
||
# btn_select.toggled.connect(toggle_selection_mode)
|
||
# btn_fit.clicked.connect(fit_to_view)
|
||
# btn_save.clicked.connect(save_cropped_image)
|
||
|
||
# # 重写事件
|
||
# view.mousePressEvent = mouse_press
|
||
# view.mouseMoveEvent = mouse_move
|
||
# view.mouseReleaseEvent = mouse_release
|
||
# view.keyPressEvent = key_press_event
|
||
|
||
# # 窗口大小变化时重新适应
|
||
# def resize_event(e):
|
||
# fit_to_view()
|
||
# QDialog.resizeEvent(dialog, e)
|
||
|
||
# dialog.resizeEvent = resize_event
|
||
|
||
# # 设置对话框焦点,以便接收键盘事件
|
||
# dialog.setFocus()
|
||
|
||
# dialog.exec()
|
||
# def setup_tabs(self, tab, num, Picture_dir):
|
||
# """
|
||
# 在给定的tab对象中设置带有num个页面的QTabWidget
|
||
|
||
# 参数:
|
||
# tab: 你在Qt Designer中创建的容器对象
|
||
# num: 要创建的标签页数量
|
||
# Picture_dir: 图片目录路径
|
||
# """
|
||
# self.table_infos_modules = [{},{},{}]
|
||
# self._clear_existing_layout(tab)
|
||
# layout = self._create_main_layout(tab)
|
||
# self._add_count_label(layout, num)
|
||
# tab_widget = self._create_tab_widget(layout)
|
||
# self._populate_tabs(tab_widget, Picture_dir)
|
||
# self.ui.checkBox_13.raise_() #置顶
|
||
|
||
# def _clear_existing_layout(self, tab):
|
||
# """清除tab中现有的布局和控件"""
|
||
# if tab.layout():
|
||
# while tab.layout().count():
|
||
# item = tab.layout().takeAt(0)
|
||
# if item.widget():
|
||
# item.widget().deleteLater()
|
||
|
||
# def _create_main_layout(self, tab):
|
||
# """创建并返回主布局"""
|
||
# layout = QVBoxLayout(tab)
|
||
# layout.setSpacing(10)
|
||
# return layout
|
||
|
||
# def _add_count_label(self, layout, num):
|
||
# """添加缺陷数量标签"""
|
||
# num_label = QLabel(f"文件夹内总缺陷数: {num}")
|
||
# num_label.setStyleSheet("font-size: 16px; font-weight: bold;")
|
||
# layout.addWidget(num_label)
|
||
|
||
# def _create_tab_widget(self, layout):
|
||
# """创建并返回QTabWidget"""
|
||
# tab_widget = QTabWidget()
|
||
# layout.addWidget(tab_widget)
|
||
# return tab_widget
|
||
|
||
# def _populate_tabs(self, tab_widget, Picture_dir):
|
||
# """填充标签页内容"""
|
||
# yepians = [Y1, Y2, Y3]
|
||
# quexian_images = []
|
||
# self.quexian_images = [[],[],[]]
|
||
|
||
# # 收集所有缺陷图片路径
|
||
# for i, Y in enumerate(yepians):
|
||
# quexian_images.extend(self.get_image_paths(
|
||
# os.path.join(Picture_dir, Y, "缺陷图"), Y))
|
||
# self.quexian_images[i] = quexian_images
|
||
|
||
|
||
# # 设置主进度条
|
||
# self.progress_dialog.setRange(0, len(quexian_images))
|
||
|
||
# # 为每个图片创建标签页
|
||
# for i, (image_path, yepian) in enumerate(quexian_images, 1):
|
||
# # 更新主进度
|
||
# self.progress_dialog.setValue(i)
|
||
# self.progress_dialog.setLabelText(f"处理图片 {i+1}/{len(quexian_images)}")
|
||
# page = self._create_tab_page(image_path)
|
||
|
||
# tab_widget.addTab(page, f"叶片:{yepian} 缺陷 {i}")
|
||
# self.progress_dialog.close()
|
||
|
||
# def _create_tab_page(self, image_path):
|
||
# """创建单个标签页"""
|
||
# page = QWidget()
|
||
# main_layout = QVBoxLayout(page)
|
||
|
||
# # 创建水平布局容器
|
||
# horizontal_container = self._create_horizontal_container(image_path)
|
||
# main_layout.addWidget(horizontal_container)
|
||
# main_layout.setSpacing(0)
|
||
|
||
# # 添加维修建议
|
||
# self._add_repair_suggestion(main_layout)
|
||
|
||
# return page
|
||
|
||
# def _create_horizontal_container(self, image_path):
|
||
# """创建水平布局容器(图片+信息 与 组合框+输入框)"""
|
||
# container = QWidget()
|
||
# layout = QHBoxLayout(container)
|
||
# layout.setSpacing(1)
|
||
|
||
# # 左侧:图片和信息区域
|
||
# left_widget = self._create_image_widget(image_path)
|
||
|
||
# # 右侧:组合框和输入框区域
|
||
# right_widget = QWidget()
|
||
# right_layout = QVBoxLayout(right_widget)
|
||
# self.add_quexian_combo(right_layout)
|
||
|
||
# layout.addWidget(left_widget)
|
||
# layout.addWidget(right_widget)
|
||
|
||
# return container
|
||
|
||
# def _create_image_widget(self, image_path):
|
||
# """创建图片显示部件"""
|
||
# widget = QWidget()
|
||
# layout = QVBoxLayout(widget)
|
||
# layout.setSpacing(0)
|
||
|
||
# # 添加图片路径信息
|
||
# self._add_image_path_label(layout, image_path)
|
||
|
||
# # 添加图片
|
||
# image_label = self._create_image_label(image_path)
|
||
# layout.addWidget(image_label)
|
||
|
||
# return widget
|
||
|
||
# def _add_image_path_label(self, layout, image_path):
|
||
# """添加图片路径标签"""
|
||
# path_label = QLabel(f"图片地址(双击打开图片): {image_path}")
|
||
# path_label.setWordWrap(True)
|
||
# path_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
# layout.addWidget(path_label)
|
||
|
||
# def _create_image_label(self, image_path):
|
||
# """创建并返回图片标签"""
|
||
# image_label = DLabel(getevent = False)
|
||
# image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
# image_label.current_path = image_path
|
||
# try:
|
||
# pixmap = self._load_pixmap(image_path)
|
||
# if not pixmap.isNull():
|
||
# scaled_pixmap = pixmap.scaled(300, 200, aspectMode = Qt.AspectRatioMode.IgnoreAspectRatio)
|
||
# image_label.setPixmap(scaled_pixmap)
|
||
# else:
|
||
# image_label.setText(f"无法加载图片: {image_path}")
|
||
# except Exception as e:
|
||
# image_label.setText(f"图片加载错误: {str(e)}")
|
||
|
||
# return image_label
|
||
|
||
|
||
# def _load_pixmap(self, image_path):
|
||
# """加载并返回QPixmap,处理大图片压缩"""
|
||
# MAX_SIZE = 5 * 1024 * 1024 # 5MB
|
||
|
||
# try:
|
||
# file_size = os.path.getsize(image_path)
|
||
# if file_size <= MAX_SIZE:
|
||
# return QPixmap(image_path)
|
||
# self.progress_dialog.setLabelText(f"压缩图片{image_path}中...")
|
||
# # 图片过大,进行压缩处理
|
||
# from PIL import Image
|
||
# import io
|
||
|
||
# img = Image.open(image_path)
|
||
# quality = 95 # 初始质量
|
||
|
||
# while quality > 10: # 设置最低质量限制
|
||
# buffer = io.BytesIO()
|
||
# img.save(buffer, format="JPEG", quality=quality)
|
||
# if buffer.tell() <= MAX_SIZE:
|
||
# break
|
||
# quality -= 5
|
||
|
||
# buffer.seek(0)
|
||
# pixmap = QPixmap()
|
||
# pixmap.loadFromData(buffer.getvalue())
|
||
# return pixmap
|
||
|
||
# except Exception as e:
|
||
# print(f"图片加载/压缩失败: {e}")
|
||
# return QPixmap()
|
||
|
||
# def _add_repair_suggestion(self, layout):
|
||
# """添加维修建议部件"""
|
||
# repair_suggestion_label = QLabel("维修建议:")
|
||
# self.repair_suggestion_edit = QTextEdit()
|
||
# self.repair_suggestion_edit.setPlaceholderText("暂不处理/观察运行/建议(尽快)打磨维修/(雷雨季前)修复")
|
||
# self.repair_suggestion_edit.setMaximumHeight(100)
|
||
|
||
# repair_layout = QVBoxLayout()
|
||
# repair_layout.addWidget(repair_suggestion_label)
|
||
# repair_layout.addWidget(self.repair_suggestion_edit)
|
||
# layout.addLayout(repair_layout)
|
||
|
||
|
||
# def add_quexian_combo(self, page_layout):
|
||
# """添加缺陷类型填写框
|
||
|
||
# Args:
|
||
# page_layout (QtWidgets.QVBoxLayout): 页面布局对象
|
||
# """
|
||
# # 添加水平布局的ComboBox组
|
||
# layout = QHBoxLayout()
|
||
# layout.setSpacing(0)
|
||
|
||
# # 可见程度ComboBox
|
||
# visibility_widget = QWidget()
|
||
# visibility_layout = QHBoxLayout(visibility_widget)
|
||
# visibility_layout.setSpacing(0)
|
||
# visibility_combo = QComboBox()
|
||
# visibility_combo.addItems(["轻微", "一般", "重要", "严重"])
|
||
# visibility_combo.setCurrentIndex(1) # 默认选择"一般"
|
||
# visibility_label = QLabel("可见程度:")
|
||
# visibility_layout.addWidget(visibility_label)
|
||
# visibility_layout.addWidget(visibility_combo)
|
||
# layout.addWidget(visibility_widget)
|
||
|
||
# # 严重程度ComboBox
|
||
# severity_widget = QWidget()
|
||
# severity_layout = QHBoxLayout(severity_widget)
|
||
# severity_layout.setSpacing(0)
|
||
# severity_combo = QComboBox()
|
||
# severity_combo.addItems(["轻微", "一般", "重要", "严重"])
|
||
# severity_combo.setCurrentIndex(1) # 默认选择"一般"
|
||
# severity_label = QLabel("严重程度:")
|
||
# severity_layout.addWidget(severity_label)
|
||
# severity_layout.addWidget(severity_combo)
|
||
# layout.addWidget(severity_widget)
|
||
|
||
# # 紧急程度ComboBox
|
||
# urgency_widget = QWidget()
|
||
# urgency_layout = QHBoxLayout(urgency_widget)
|
||
# urgency_layout.setSpacing(0)
|
||
# urgency_combo = QComboBox()
|
||
# urgency_combo.addItems(["不紧急", "一般", "紧急", "非常紧急"])
|
||
# urgency_combo.setCurrentIndex(1) # 默认选择"一般"
|
||
# urgency_combo.setMinimumWidth(74)
|
||
# urgency_label = QLabel("紧急程度:")
|
||
# urgency_layout.addWidget(urgency_label)
|
||
# urgency_layout.addWidget(urgency_combo)
|
||
# layout.addWidget(urgency_widget)
|
||
|
||
# vlayout = QVBoxLayout()
|
||
# # 缺陷类型输入框
|
||
# defect_type_widget = QWidget()
|
||
# defect_type_layout = QVBoxLayout(defect_type_widget)
|
||
# defect_type_label = QLabel("缺陷类型:")
|
||
# defect_type_label.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
# self.defect_type_edit = QLineEdit()
|
||
# self.defect_type_edit.setPlaceholderText("如:涂层损伤")
|
||
# self.defect_type_edit.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
# defect_type_layout.addWidget(defect_type_label)
|
||
# defect_type_layout.addWidget(self.defect_type_edit)
|
||
# vlayout.addWidget(defect_type_widget)
|
||
|
||
# # 缺陷位置输入框
|
||
# defect_location_widget = QWidget()
|
||
# defect_location_layout = QVBoxLayout(defect_location_widget)
|
||
# defect_location_label = QLabel("缺陷位置:")
|
||
# defect_location_label.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
# self.defect_location_edit = QLineEdit()
|
||
# self.defect_location_edit.setPlaceholderText("如:叶片ps面距叶根3m处")
|
||
# self.defect_location_edit.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
# defect_location_layout.addWidget(defect_location_label)
|
||
# defect_location_layout.addWidget(self.defect_location_edit)
|
||
# vlayout.addWidget(defect_location_widget)
|
||
|
||
# # 缺陷尺寸输入框
|
||
# defect_size_widget = QWidget()
|
||
# defect_size_layout = QVBoxLayout(defect_size_widget)
|
||
# defect_size_label = QLabel("缺陷尺寸(mm):")
|
||
# defect_size_label.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
# self.defect_size_edit = QLineEdit()
|
||
# self.defect_size_edit.setPlaceholderText("如:弦向100mm,轴向800mm")
|
||
# self.defect_size_edit.setStyleSheet("font-size: 18px; font-weight: bold;")
|
||
# defect_size_layout.addWidget(defect_size_label)
|
||
# defect_size_layout.addWidget(self.defect_size_edit)
|
||
# vlayout.addWidget(defect_size_widget)
|
||
|
||
# page_layout.setSpacing(0)
|
||
# page_layout.addLayout(layout)
|
||
# page_layout.addLayout(vlayout)
|
||
|
||
|
||
# def get_image_paths(self, directory, Y):
|
||
# image_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp'}
|
||
# image_paths = []
|
||
|
||
# # 使用 os.walk
|
||
# for root, _, files in os.walk(directory):
|
||
# for file in files:
|
||
# if os.path.splitext(file)[1].lower() in image_extensions:
|
||
# image_paths.append((os.path.join(root, file), Y))
|
||
# return image_paths
|
||
|
||
def init_dlabel(self,scrollArea):
|
||
"""初始化图片控件 汇总部分
|
||
Args:
|
||
scrollArea (QtWidgets.QScrollArea): 页面控件对象
|
||
"""
|
||
area = scrollArea
|
||
for i in range(3):
|
||
for j in range(6):
|
||
container = QWidget()
|
||
container.setFixedSize(150,150)
|
||
v_layout = QVBoxLayout(container)
|
||
|
||
label = DLabel(messagebrowser=self.ui.messagebrowser)
|
||
label.setGeometry(10, 10, 150, 150)
|
||
v_layout.addWidget(label)
|
||
|
||
line_edit = QLineEdit()
|
||
line_edit.setPlaceholderText("输入图片拍摄点位")
|
||
line_edit.setText(self.photo_pos[j])
|
||
v_layout.addWidget(line_edit)
|
||
|
||
self.gridlayout[i].addWidget(container, self.current_row[i], self.current_col[i])
|
||
self.current_col[i] += 1
|
||
if self.current_col[i] >= 5:
|
||
self.current_col[i] = 0
|
||
self.current_row[i] += 1
|
||
self.picture_line[i].append(line_edit)
|
||
self.picture_labels[i].append(label)
|
||
self.label_init_list[i].append(label)
|
||
self.line_init_list[i].append(line_edit)
|
||
|
||
def add_dlabel(self, i, scrollArea, text = None, picture_path = None, ifauto = False):
|
||
"""增加图片控件
|
||
|
||
Args:
|
||
i (int): 第几个页面
|
||
scrollArea (QtWidgets.QScrollArea): 页面控件对象
|
||
"""
|
||
area = scrollArea
|
||
|
||
container = QWidget()
|
||
container.setFixedSize(150,150)
|
||
v_layout = QVBoxLayout(container)
|
||
|
||
label = DLabel(messagebrowser=self.ui.messagebrowser)
|
||
label.setGeometry(10, 10, 150, 150)
|
||
if picture_path:
|
||
label.load_image(picture_path)
|
||
v_layout.addWidget(label)
|
||
|
||
line_edit = QLineEdit()
|
||
line_edit.setPlaceholderText("输入图片拍摄点位")
|
||
if text:
|
||
line_edit.setText(text)
|
||
v_layout.addWidget(line_edit)
|
||
|
||
self.gridlayout[i].addWidget(container, self.current_row[i], self.current_col[i])
|
||
self.current_col[i] += 1
|
||
if self.current_col[i] >= 5:
|
||
self.current_col[i] = 0
|
||
self.current_row[i] += 1
|
||
self.picture_line[i].append(line_edit)
|
||
self.picture_labels[i].append(label)
|
||
self.new_add_dlabel[i].append(label)
|
||
self.new_add_lines[i].append(line_edit)
|
||
|
||
return label, line_edit
|
||
|
||
def empty_line_set(self):
|
||
"""遍历整个页面,如果有输入框为空,则置为‘未输入’"""
|
||
widget = self.ui.tabWidget # 最根组件
|
||
|
||
# 遍历所有的标签页
|
||
for i in range(widget.count()):
|
||
tab = widget.widget(i)
|
||
|
||
# 假设输入框是通过布局(例如 QVBoxLayout 或 QHBoxLayout)来组织的
|
||
# 如果输入框是直接作为子部件添加的,可以使用 tab.children() 来获取所有子部件
|
||
for child in tab.findChildren(QtWidgets.QLineEdit):
|
||
if not child.text().strip(): # 如果输入框为空(包括空白字符)
|
||
child.setText('未输入') # 设置为“未输入”
|
||
|
||
|
||
def check_dir(self, dirs: list[str]):
|
||
"""检查目录是否存在,不存在则提示不可达"""
|
||
missing_dirs = []
|
||
ifmisiing = False
|
||
for directory in dirs:
|
||
if not os.path.isdir(directory):
|
||
missing_dirs.append(directory)
|
||
ifmisiing = True
|
||
if ifmisiing:
|
||
QtWidgets.QMessageBox.warning(None, "提示", f"以下目录不存在:\n{missing_dirs}\n请检查后重试")
|
||
return False
|
||
return True
|
||
|
||
def bind_directory_browser(self, line_edit: QLineEdit, button: QPushButton):
|
||
"""绑定目录浏览功能到QLineEdit和QPushButton
|
||
|
||
Args:
|
||
line_edit: 显示和存储目录路径的QLineEdit组件
|
||
button: 触发目录浏览的QPushButton组件
|
||
"""
|
||
def browse_directory():
|
||
# 获取当前line_edit中的路径
|
||
current_dir = line_edit.text().strip()
|
||
|
||
# 检查路径是否存在,不存在则使用根目录
|
||
if not current_dir or not QDir(current_dir).exists():
|
||
current_dir = QDir.rootPath()
|
||
|
||
# 打开目录选择对话框
|
||
selected_dir = QFileDialog.getExistingDirectory(
|
||
button, # 父组件
|
||
"选择目录", # 标题
|
||
current_dir, # 初始目录
|
||
QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks # 选项
|
||
)
|
||
|
||
# 如果用户选择了目录(未点击取消),则更新line_edit
|
||
if selected_dir:
|
||
line_edit.setText(QDir.toNativeSeparators(selected_dir))
|
||
|
||
# 连接按钮的点击信号到浏览函数
|
||
button.clicked.connect(browse_directory)
|
||
|
||
def tongbu_time(self):
|
||
self.dateyear = self.ui.calendarWidget.selectedDate().toString('yyyy年')
|
||
unit_map = {'1' : '一', '2' : '二', '3' : '三', '4' : '四', '5' : '五', '6' : '六', '7' : '七', '8' : '八', '9' : '九', '0' : '〇'}
|
||
unit_map_month = {1 : '一', 2 : '二', 3 : '三', 4 : '四', 5 : '五', 6 : '六', 7 : '七', 8 : '八', 9 : '九', 10 : '十', 11 : '十一', 12 : '十二'}
|
||
self.dateyear = self.dateyear.translate(str.maketrans(unit_map))
|
||
self.datemonth = self.ui.calendarWidget.selectedDate().toString('MM')
|
||
self.datemonth = (unit_map_month[int(self.datemonth)] + '月')
|
||
self.ui.date.setText(self.dateyear + self.datemonth)
|
||
self.ui.bianzhishijian.setText(self.ui.calendarWidget.selectedDate().toString('yyyy/MM/dd'))
|
||
self.ui.conclusion_date.setText(self.ui.calendarWidget.selectedDate().toString('yyyy/MM/dd'))
|
||
self.ui.messagebrowser.append("检测到手动更改报告时间,自动同步编制时间、总结时间")
|
||
|
||
class XiangmuBaseInfo:
|
||
"""项目基本信息类"""
|
||
def __init__(self, tupian_dir, project_info_data):
|
||
self.tupian_dir = tupian_dir
|
||
if len(project_info_data) > 0:
|
||
self.jituan_name = project_info_data["甲方集团"]
|
||
self.fengchang_name = project_info_data["风场名"]
|
||
self.jizu_type = project_info_data["机组型号"]
|
||
self.company_name_yi = project_info_data["乙方公司"]
|
||
self.company_name_jia = project_info_data["甲方公司"]
|
||
self.project_location = project_info_data["风场地址"]
|
||
self.fuzeren = project_info_data["负责人"]
|
||
self.phone_fuzeren = project_info_data["负责人电话"]
|
||
self.xiangmuguige = project_info_data["项目规格"]
|
||
else:
|
||
self.jituan_name = ''
|
||
self.fengchang_name = ''
|
||
self.jizu_type = ''
|
||
self.company_name_yi = ''
|
||
self.company_name_jia = ''
|
||
self.project_location = ''
|
||
self.fuzeren = ''
|
||
self.phone_fuzeren = ''
|
||
self.xiangmuguige = ''
|
||
self.Picture_dir = tupian_dir
|
||
self.project_dir = ''
|
||
tupian_dir = Path(tupian_dir)
|
||
folder_names = [f.name for f in tupian_dir.iterdir() if f.is_dir()]
|
||
#通过图片路径获取各叶片编号
|
||
self.Y1 = folder_names[0]
|
||
self.Y2 = folder_names[1]
|
||
self.Y3 = folder_names[2]
|
||
def get_base_info(self):
|
||
'''返回项目基本信息
|
||
Returns:
|
||
tuple: jituan_name, fengchang_name, jizu_type, company_name_yi, company_name_jia, project_location, fuzeren, phone_fuzeren, Y1, Y2, Y3, xiangmuguige
|
||
'''
|
||
return self.jituan_name, self.fengchang_name, self.jizu_type, self.company_name_yi, self.company_name_jia, self.project_location, self.fuzeren, self.phone_fuzeren, self.Y1, self.Y2, self.Y3, self.xiangmuguige
|
||
|
||
def get_xiangmu_base_info(tupian_dir, project = None, exe_dir = None, ifdatabase = False):
|
||
"""获取项目基本信息
|
||
|
||
Args:
|
||
tupian_dir (str): 图片文件夹路径
|
||
project: 项目名称(通过此项目名称查找本地/数据库对应项目信息) 未实现
|
||
|
||
Returns:
|
||
XiangmuBaseInfo: 项目基本信息对象
|
||
"""
|
||
if not project:
|
||
return XiangmuBaseInfo(tupian_dir, {})
|
||
if not ifdatabase and project and exe_dir:
|
||
# 构建项目信息JSON文件的路径
|
||
project_info_path = exe_dir + rf"\{project}.json"
|
||
|
||
# 检查文件是否存在
|
||
if os.path.exists(project_info_path):
|
||
# 读取并解析JSON文件中的数据
|
||
with open(project_info_path, 'r', encoding='utf-8') as file:
|
||
project_info_data = json.load(file)
|
||
else:
|
||
# 如果文件不存在,可以抛出异常或返回空信息
|
||
raise FileNotFoundError(f"项目信息文件未找到: {project_info_path}")
|
||
return XiangmuBaseInfo(tupian_dir, project_info_data)
|
||
|
||
if __name__ == '__main__':
|
||
app = QtWidgets.QApplication(sys.argv) # 创建QApplication实例
|
||
QCoreApplication.setOrganizationName('DTAI')
|
||
QCoreApplication.setApplicationName('baogao_shengcheng')
|
||
# 创建并显示主窗口
|
||
window = MainWindow()
|
||
window.show()
|
||
|
||
# 运行应用程序
|
||
sys.exit(app.exec()) |