feat: 日志解析时增加进度条展示

This commit is contained in:
leimingsheng 2025-08-25 14:44:51 +08:00
parent c6d3d57358
commit 40f4ed22cf
9 changed files with 239 additions and 67 deletions

@ -33,13 +33,15 @@
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QTextBrowser" name="textBrowser_console">
<property name="markdown">
<string/>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:18pt; font-weight:600;&quot;&gt;通过文件上传一键日志压缩包来开始&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:18pt; font-weight:600;&quot;&gt;或者将日志压缩包拖入程序&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>

@ -28,6 +28,7 @@ class Ui_MainWindow(object):
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tab_console)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.textBrowser_console = QtWidgets.QTextBrowser(self.tab_console)
self.textBrowser_console.setMarkdown("")
self.textBrowser_console.setObjectName("textBrowser_console")
self.horizontalLayout_4.addWidget(self.textBrowser_console)
self.tabWidget.addTab(self.tab_console, "")
@ -102,8 +103,7 @@ class Ui_MainWindow(object):
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:18pt; font-weight:600;\">通过文件上传一键日志压缩包来开始</span></p>\n"
"<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" font-size:18pt; font-weight:600;\">或者将日志压缩包拖入程序</span></p></body></html>"))
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_console), _translate("MainWindow", "控制台"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_baseinfo), _translate("MainWindow", "基本信息"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_alert), _translate("MainWindow", "关键告警"))

40
src/background_task.py Normal file

@ -0,0 +1,40 @@
from PyQt5.QtCore import QThread, pyqtSignal
import service
class DiagnoseThread(QThread):
"""后台线程,用于执行耗时的诊断任务"""
# 定义信号:更新进度(值, 消息)、任务完成(是否成功, 消息)
progress_updated = pyqtSignal(int, str)
task_completed = pyqtSignal(bool, str)
def __init__(self, parse_status, file_path):
super().__init__()
self.parse_status = parse_status
self.file_path = file_path
self.is_running = True
def run(self):
"""线程执行函数:执行耗时任务"""
try:
self.progress_updated.emit(1, "正在初始化解析...")
# 执行耗时任务
service.start_diagnose(self.parse_status, self.file_path, progress_callback=self.update_progress)
# 确保最终进度为100%
self.progress_updated.emit(100, "处理完成")
self.task_completed.emit(True, "日志解析任务已完成")
except Exception as e:
# 出错时也允许关闭对话框
self.progress_updated.emit(100, f"处理失败:{str(e)}")
self.task_completed.emit(False, f"处理失败:{str(e)}")
def update_progress(self, value, message):
"""接收service层的进度更新"""
if 0 <= value <= 100 and self.is_running:
self.progress_updated.emit(value, message)
def stop(self):
"""停止线程"""
self.is_running = False
self.wait()

@ -1,77 +1,119 @@
import time
import os
import service
from utils import show_error_message, show_critical_message
from utils import show_error_message, show_critical_message, show_info_message
from progress_diaglog import ProgressDialog
from background_task import DiagnoseThread
class FileProcessor:
def __init__(self, main_window):
self.main_window = main_window # 持有主窗口引用
self.parse_status = main_window.parse_status # 服务状态
self.view_renderer = main_window.view_renderer # 关联视图渲染模块
self.main_window = main_window
self.parse_status = main_window.parse_status
self.view_renderer = main_window.view_renderer
self.last_processed_path = None
self.last_processed_time = 0
self.diagnose_thread = None # 保存后台线程引用
def process_uploaded_file(self, file_path):
# 防重复处理
current_time = time.time()
if (file_path == self.last_processed_path and
current_time - self.last_processed_time < 1):
print(f"跳过重复处理:{file_path}")
return
self.last_processed_path = file_path
self.last_processed_time = current_time
# 格式校验
if not self.is_valid_file_format(file_path):
show_error_message(self.main_window, file_path)
return
try:
# 显示文件信息
file_name = os.path.basename(file_path)
file_size = os.path.getsize(file_path) / 1024
info = (
f"已上传文件:\n"
f"文件名:{file_name}\n"
f"路径:{file_path}\n"
f"大小:{file_size:.2f} KB\n"
f"-------------------------------------------" + "\n"
)
self.main_window.textBrowser_console.insertPlainText(info)
# 初始化进度对话框
self.progress_dialog = ProgressDialog(
parent=self.main_window,
title="正在处理文件",
label_text="准备开始处理..."
)
# 创建并配置后台线程
self.diagnose_thread = DiagnoseThread(self.parse_status, file_path)
self.diagnose_thread.progress_updated.connect(self.progress_dialog.update_progress)
self.diagnose_thread.task_completed.connect(self.on_diagnose_completed)
# 显示进度条并启动线程
self.progress_dialog.show()
self.diagnose_thread.start()
except Exception as e:
show_critical_message(self.main_window, "处理失败", f"文件处理出错:{str(e)}")
def on_diagnose_completed(self, success, message):
"""处理诊断任务完成后的逻辑"""
if hasattr(self, 'progress_dialog') and self.progress_dialog:
# 明确允许对话框关闭
self.progress_dialog.set_allow_close(True)
# 关闭对话框
self.progress_dialog.close()
# 释放引用
self.progress_dialog = None
# 显示结果信息
self.main_window.textBrowser_console.insertPlainText(f"{message}\n")
if success:
# 处理成功,更新视图
self._update_view_after_parse()
show_info_message(self.main_window, "成功", "文件解析完成")
else:
# 处理失败,显示错误
show_critical_message(self.main_window, "失败", message)
# 清理线程引用
self.diagnose_thread = None
# 其他原有方法保持不变...
def is_valid_file_format(self, file_path):
"""校验文件是否为 .tar.gz 格式"""
# 原有实现...
_, ext = os.path.splitext(file_path)
if ext.lower() == '.gz':
base, ext2 = os.path.splitext(os.path.splitext(file_path)[0])
return ext2.lower() == '.tar'
return False
def get_sensorhistory_path(self, key):
"""调用服务层获取传感器历史图片路径"""
return service.get_sensorhistory_path(key)
def process_uploaded_file(self, file_path):
"""核心:处理上传的文件(校验→解析→传递结果)"""
# 1. 格式校验
if not self.is_valid_file_format(file_path):
show_error_message(self.main_window, file_path)
return
try:
# 2. 提取文件信息并显示到控制台
file_name = os.path.basename(file_path)
file_size = os.path.getsize(file_path) / 1024 # 转换为 KB
info = (
f"已上传文件:\n"
f"文件名:{file_name}\n"
f"路径:{file_path}\n"
f"大小:{file_size:.2f} KB\n"
f"------------------------------------------------------"+ "\n"
)
self.main_window.textBrowser_console.insertPlainText(info)
# 3. 调用服务层解析文件
service.send_log_to_cache(file_path)
service.start_diagnose(self.parse_status)
# 4. 根据解析状态更新视图
self._update_view_after_parse()
self.main_window.textBrowser_console.insertPlainText("完成文件解析\n")
except Exception as e:
show_critical_message(self.main_window, "处理失败", f"文件处理出错:{str(e)}")
def _update_view_after_parse(self):
"""解析完成后更新各视图组件"""
# 更新基础信息文本框
if self.parse_status.baseinfo_status:
baseinfo_str = service.get_baseinfo_str()
self.main_window.textBrowser_info.insertPlainText(baseinfo_str)
# 更新传感器图片
if self.parse_status.sensorhistory_status:
self.view_renderer.display_pic(
self.get_sensorhistory_path("all"),
self.parse_status.diag_complete_status
)
# 更新告警表格
if self.parse_status.parseidl_status and not service.is_idl_alert_empty():
alert_json = service.get_idl_alert_json()
self.view_renderer.fill_tableView_alert(alert_json)
# 更新时间线
if self.parse_status.eventline_status:
event_json = service.get_timeline_event_json()
self.main_window.timeline_content.load_events_from_json(event_json)
def get_sensorhistory_path(self, key):
return service.get_sensorhistory_path(key)

@ -15,7 +15,7 @@ if __name__ == "__main__":
pass # 非Windows系统忽略
# 初始化服务缓存
service.app_cache_init()
# service.app_cache_init()
# 创建PyQt应用实例
app = QApplication(sys.argv)
# 获取文件系统中存放的icon

@ -25,6 +25,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
"""统一执行所有模块的初始化"""
# UI初始化样式、图形视图、表格模型、时间线
self.ui_init.init_ui()
self.ui_init.init_textbrower_console_style()
self.ui_init.init_graphic_views()
self.ui_init.init_textbrowser_style()
self.ui_init.init_tableView_alert_model()

46
src/progress_diaglog.py Normal file

@ -0,0 +1,46 @@
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QProgressBar, QLabel
from PyQt5.QtCore import Qt, pyqtSignal
class ProgressDialog(QDialog):
"""进度条对话框,支持程序控制自动关闭"""
def __init__(self, parent=None, title="处理中", label_text="正在处理,请稍候..."):
super().__init__(parent)
self.setWindowTitle(title)
self.setModal(True) # 模态对话框
self.setFixedSize(400, 120)
self.allow_close = False # 新增:允许关闭标记
# 布局
layout = QVBoxLayout()
# 提示文本
self.label = QLabel(label_text)
layout.addWidget(self.label)
# 进度条
self.progress_bar = QProgressBar()
self.progress_bar.setRange(0, 100)
self.progress_bar.setValue(0)
layout.addWidget(self.progress_bar)
self.setLayout(layout)
def update_progress(self, value, message=None):
"""更新进度值和提示信息"""
self.progress_bar.setValue(value)
if message:
self.label.setText(message)
# 当进度完成时,允许关闭
if value >= 100:
self.allow_close = True
def set_allow_close(self, allow):
"""设置是否允许关闭对话框"""
self.allow_close = allow
def closeEvent(self, event):
"""根据允许关闭标记决定是否关闭"""
if self.allow_close:
event.accept() # 允许关闭
else:
event.ignore() # 禁止关闭

@ -76,18 +76,44 @@ def is_idl_alert_empty():
def get_timeline_event_json():
return zijin_event.get_event_json()
def start_diagnose(parseStatus):
def start_diagnose(parseStatus, filepath, progress_callback=None):
"""
执行诊断任务添加进度回调
:param progress_callback: 进度回调函数接收(value, message)参数
"""
try:
if progress_callback:
progress_callback(10, "正在初始化应用cache...")
app_cache_init()
if progress_callback:
progress_callback(20, "正在解压日志...")
send_log_to_cache(filepath)
if progress_callback:
progress_callback(30, "正在解析Sensor历史数据信息...")
result_sensorhistory = sensorparse.program_main()
parseStatus.set_sensorhistory_status(result_sensorhistory)
if progress_callback:
progress_callback(50, "正在解析基础信息...")
result_baseinfo = baseinfo.program_main()
parseStatus.set_baseinfo_status(result_baseinfo)
if progress_callback:
progress_callback(80, "正在解析告警信息...")
result_parseidl = parseidl.program_main()
parseStatus.set_parseidl_status(result_parseidl)
if progress_callback:
progress_callback(95, "正在生成时间线...")
result_eventline = zijin_event.program_main()
parseStatus.set_eventline_status(result_eventline)
# 完成文件解析后将 diag_complete_status 置位
parseStatus.diag_complete_status = True
except Exception as e:
if progress_callback:
progress_callback(-1, f"处理出错:{str(e)}")
raise e

@ -42,6 +42,21 @@ class UIInitializer:
}
""")
def init_textbrower_console_style(self):
self.main_window.textBrowser_console.setStyleSheet("""
QTextBrowser {
font-family: 'SimHei';
font-size: 20px;
color: #2c3e50;
background-color: #f8f9fa;
border: 1px solid #ddd;
border-radius: 4px;
padding: 8px;
}
""")
message = "从文件打开日志压缩包或将压缩包拖入该窗口\n"
self.main_window.textBrowser_console.insertPlainText(f"{message}\n")
def init_graphic_views(self):
"""初始化图形视图(抗锯齿、场景绑定)"""
# 创建图形场景