code sync
This commit is contained in:
parent
667f10e7d6
commit
6ec22eb909
@ -36,7 +36,7 @@
|
||||
<widget class="QComboBox" name="comboBox_proj_select"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEdit_console"/>
|
||||
<widget class="QTextBrowser" name="textBrowser_console"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import os
|
||||
import importlib
|
||||
from common import record_log
|
||||
|
||||
class AppService:
|
||||
def __init__(self):
|
||||
@ -95,10 +96,16 @@ def get_project_logname_list(key):
|
||||
|
||||
return result
|
||||
|
||||
def do_proj_parse(key="app"):
|
||||
def do_proj_parse(parseStatus, filepath, key="app", progress_callback = None):
|
||||
porj_package = "proj_" + key
|
||||
|
||||
pHandlers = dynamic_import_lib(porj_package)
|
||||
if hasattr(pHandlers, "start_parse"):
|
||||
# Step 1 . 解压日志文件, 并记录到历史信息
|
||||
|
||||
# Step 2 . 调用具体handler进行自定义解析
|
||||
pHandlers.start_parse()
|
||||
|
||||
else:
|
||||
record_log("ERROR", f"Project : {key} | No 'start_parse' function found, failed")
|
||||
if progress_callback:
|
||||
progress_callback(-1, "处理出错")
|
||||
@ -15,7 +15,8 @@ from .file_tool import(
|
||||
|
||||
from .cache_mgmt import(
|
||||
clean_app_cache,
|
||||
clean_running_log
|
||||
clean_running_log,
|
||||
add_onekeylog_info_cache
|
||||
)
|
||||
|
||||
# From ._globals
|
||||
@ -28,4 +29,4 @@ __all__ += [ record_log ]
|
||||
__all__ += [ force_empty_folder, add_to_json_array, get_user_appdata_path ]
|
||||
|
||||
# From .cache_mgmt
|
||||
__all__ += [ clean_app_cache, clean_running_log]
|
||||
__all__ += [ clean_app_cache, clean_running_log, add_onekeylog_info_cache ]
|
||||
@ -1,5 +1,6 @@
|
||||
import os
|
||||
import time
|
||||
import tarfile
|
||||
|
||||
from ._globals import cache_dir
|
||||
from .file_tool import force_empty_folder, add_to_json_array, get_user_appdata_path
|
||||
@ -21,7 +22,7 @@ def app_cache_create():
|
||||
os.makedirs(app_plugin_path)
|
||||
record_log("Info", f"Create Dir:{app_plugin_path}")
|
||||
|
||||
def add_onekeylog_info_cache(log_name):
|
||||
def add_onekeylog_info_cache(log_name, proj_key):
|
||||
"""
|
||||
添加一份新日志时触发这个动作, 将日志名称以及解析日期全部记录到
|
||||
parse_history.json
|
||||
@ -35,6 +36,7 @@ def add_onekeylog_info_cache(log_name):
|
||||
|
||||
# 生成新的记录Dict
|
||||
new_entry = {
|
||||
"project" : proj_key,
|
||||
"filename" : log_name,
|
||||
"timestamp" : current_time
|
||||
}
|
||||
@ -44,6 +46,41 @@ def add_onekeylog_info_cache(log_name):
|
||||
if not result_b:
|
||||
record_log("ERROR", "Fail to add parse history cache")
|
||||
|
||||
def unzip_log(tar_path, extract_path='.'):
|
||||
"""
|
||||
解压 tar.gz 文件到指定目录
|
||||
|
||||
参数:
|
||||
tar_path (str): tar.gz 文件的路径
|
||||
extract_path (str): 解压目标目录,默认为当前目录
|
||||
"""
|
||||
try:
|
||||
# 检查文件是否存在
|
||||
if not os.path.exists(tar_path):
|
||||
raise FileNotFoundError(f"文件不存在: {tar_path}")
|
||||
|
||||
# 创建解压目录(如果不存在)
|
||||
os.makedirs(extract_path, exist_ok=True)
|
||||
|
||||
# 打开 tar.gz 文件并解压
|
||||
with tarfile.open(tar_path, "r:gz") as tar:
|
||||
# 列出所有文件(可选)
|
||||
print(f"解压文件列表:")
|
||||
for member in tar.getmembers():
|
||||
print(f"- {member.name}")
|
||||
|
||||
# 解压所有文件到目标目录
|
||||
tar.extractall(path=extract_path)
|
||||
print(f"\n成功解压到: {os.path.abspath(extract_path)}")
|
||||
|
||||
except tarfile.TarError as e:
|
||||
print(f"tar 文件处理错误: {e}")
|
||||
except Exception as e:
|
||||
print(f"解压失败: {e}")
|
||||
|
||||
def send_log_to_cache(filepath, key):
|
||||
pass
|
||||
|
||||
def clean_app_cache():
|
||||
force_empty_folder(cache_dir)
|
||||
|
||||
|
||||
64
src/frontend/background_task.py
Normal file
64
src/frontend/background_task.py
Normal file
@ -0,0 +1,64 @@
|
||||
from PyQt6.QtCore import QThread, pyqtSignal
|
||||
from backend import do_proj_parse
|
||||
|
||||
class DiagnoseThread(QThread):
|
||||
"""后台线程,用于执行耗时的诊断任务"""
|
||||
# 定义信号:更新进度(值, 消息)、任务完成(是否成功, 消息)
|
||||
progress_updated = pyqtSignal(int, str)
|
||||
task_completed = pyqtSignal(bool, str)
|
||||
|
||||
def __init__(self, parse_status, file_path, key):
|
||||
super().__init__()
|
||||
self.parse_status = parse_status
|
||||
self.file_path = file_path
|
||||
self.is_running = True
|
||||
self.proj_key = key
|
||||
|
||||
def run(self):
|
||||
"""线程执行函数:执行耗时任务"""
|
||||
try:
|
||||
self.progress_updated.emit(1, "正在初始化解析...")
|
||||
|
||||
# 执行耗时任务
|
||||
do_proj_parse(self.parse_status, self.file_path, self.proj_key, 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()
|
||||
|
||||
class FileReaderThread(QThread):
|
||||
# 定义信号,用于传递读取到的文本块和完成状态
|
||||
text_chunk_ready = pyqtSignal(str)
|
||||
finished = pyqtSignal()
|
||||
|
||||
def __init__(self, file_path, chunk_size=4096):
|
||||
super().__init__()
|
||||
self.file_path = file_path
|
||||
self.chunk_size = chunk_size # 每次读取的块大小
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
with open(self.file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
while True:
|
||||
chunk = f.read(self.chunk_size) # 分块读取
|
||||
if not chunk:
|
||||
break
|
||||
self.text_chunk_ready.emit(chunk) # 发送文本块到主线程
|
||||
self.finished.emit()
|
||||
except Exception as e:
|
||||
self.text_chunk_ready.emit(f"读取文件错误: {str(e)}")
|
||||
self.finished.emit()
|
||||
@ -1,12 +1,17 @@
|
||||
import os
|
||||
from PyQt6.QtWidgets import QFileDialog
|
||||
from PyQt6.QtGui import QDragEnterEvent, QDropEvent
|
||||
from common import record_log
|
||||
|
||||
class EventHandler:
|
||||
def __init__(self, main_window):
|
||||
self.main_window = main_window # 持有主窗口引用
|
||||
self.file_processor = main_window.file_processor
|
||||
|
||||
def bind_event(self):
|
||||
record_log("INFO", "Start Bind Event")
|
||||
# 菜单项事件绑定
|
||||
self.main_window.action_uploadFile.triggered.connect(self.upload_file_in_resource)
|
||||
self.main_window.action_cleanCache.triggered.connect(self.on_click_clean_cache)
|
||||
self.main_window.action_cleanLog.triggered.connect(self.on_click_clean_running_log)
|
||||
|
||||
@ -40,15 +45,42 @@ class EventHandler:
|
||||
|
||||
def on_click_clean_running_log(self):
|
||||
from common import clean_running_log
|
||||
self.main_window.textEdit_console.append("系统信息: Clean apprunning.log")
|
||||
self.main_window.textBrowser_console.insertPlainText("系统信息: Clean apprunning.log")
|
||||
clean_running_log()
|
||||
|
||||
def on_click_clean_cache(self):
|
||||
from common import clean_app_cache
|
||||
self.main_window.textEdit_console.append("系统信息: Clean all app cache")
|
||||
self.main_window.textBrowser_console.insertPlainText("系统信息: Clean all app cache")
|
||||
clean_app_cache()
|
||||
|
||||
|
||||
|
||||
def upload_file_in_resource(self):
|
||||
"""打开文件选择对话框"""
|
||||
file_path, _ = QFileDialog.getOpenFileName(
|
||||
self.main_window,
|
||||
"选择文件",
|
||||
os.getcwd(),
|
||||
"所有文件 (*);;日志压缩文件 (*.gz)"
|
||||
)
|
||||
if file_path:
|
||||
self.file_processor.process_uploaded_file(file_path)
|
||||
|
||||
def dragEnterEvent(self, event: QDragEnterEvent):
|
||||
"""拖入事件:判断是否为文件"""
|
||||
if event.mimeData().hasUrls():
|
||||
event.acceptProposedAction()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def dropEvent(self, event: QDropEvent):
|
||||
"""放下事件:处理拖放的文件"""
|
||||
file_paths = [url.toLocalFile() for url in event.mimeData().urls()]
|
||||
for file_path in file_paths:
|
||||
self.file_processor.process_uploaded_file(file_path)
|
||||
|
||||
def patch_drag_event(self):
|
||||
"""将拖放事件绑定到主窗口(需在主窗口初始化时调用)"""
|
||||
self.main_window.dragEnterEvent = self.dragEnterEvent
|
||||
self.main_window.dropEvent = self.dropEvent
|
||||
|
||||
|
||||
|
||||
@ -1,3 +1,91 @@
|
||||
import os
|
||||
from .progress_diaglog import ProgressDialog
|
||||
from .background_task import DiagnoseThread
|
||||
from common import record_log
|
||||
from .utils import (show_upload_error_message,
|
||||
show_critical_message,
|
||||
show_info_message)
|
||||
|
||||
class FileProcessor:
|
||||
def __init__(self, main_window):
|
||||
self.main_window = main_window
|
||||
self.main_window = main_window
|
||||
self.serviceStatus = main_window.serviceStatus
|
||||
|
||||
def process_uploaded_file(self, file_path):
|
||||
record_log("INFO", f"Upload file : {file_path}")
|
||||
current_project_key = self.main_window.comboBox_proj_select.currentText()
|
||||
|
||||
# 格式校验
|
||||
if not self.is_valid_file_format(file_path):
|
||||
show_upload_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.serviceStatus, file_path, current_project_key)
|
||||
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 is_valid_file_format(self, file_path):
|
||||
# 原有实现...
|
||||
_, 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 _update_view_after_parse(self):
|
||||
"""
|
||||
解析任务完成, 将数据同步到视图中
|
||||
"""
|
||||
pass
|
||||
|
||||
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
|
||||
@ -36,9 +36,19 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
||||
self.ui_init.init_comboBox_project_select()
|
||||
self.ui_init.init_comboBox_temperature_select()
|
||||
self.ui_init.init_comboBox_logreader_select()
|
||||
self.ui_init.init_textEdit_console_style()
|
||||
self.ui_init.init_textBrowser_console_style()
|
||||
self.ui_init.init_textBrowser_baseinfo_style()
|
||||
self.ui_init.init_textBrowser_logreader_style()
|
||||
|
||||
# Event_Handler
|
||||
self.event_handler.bind_event()
|
||||
self.event_handler.bind_event()
|
||||
self.event_handler.patch_drag_event()
|
||||
|
||||
# 完成UI初始化
|
||||
record_log("INFO", "Complete UI Init")
|
||||
|
||||
def reinit_main_ui(self):
|
||||
"""
|
||||
在使用日志更新时重新刷新界面整体布局
|
||||
"""
|
||||
record_log("INFO", "Triggered reinit main window")
|
||||
@ -29,9 +29,9 @@ class Ui_MainWindow(object):
|
||||
self.comboBox_proj_select = QtWidgets.QComboBox(parent=self.tab_console)
|
||||
self.comboBox_proj_select.setObjectName("comboBox_proj_select")
|
||||
self.verticalLayout_4.addWidget(self.comboBox_proj_select)
|
||||
self.textEdit_console = QtWidgets.QTextEdit(parent=self.tab_console)
|
||||
self.textEdit_console.setObjectName("textEdit_console")
|
||||
self.verticalLayout_4.addWidget(self.textEdit_console)
|
||||
self.textBrowser_console = QtWidgets.QTextBrowser(parent=self.tab_console)
|
||||
self.textBrowser_console.setObjectName("textBrowser_console")
|
||||
self.verticalLayout_4.addWidget(self.textBrowser_console)
|
||||
self.tabWidget.addTab(self.tab_console, "")
|
||||
self.tab_baseinfo = QtWidgets.QWidget()
|
||||
self.tab_baseinfo.setObjectName("tab_baseinfo")
|
||||
|
||||
46
src/frontend/progress_diaglog.py
Normal file
46
src/frontend/progress_diaglog.py
Normal file
@ -0,0 +1,46 @@
|
||||
from PyQt6.QtWidgets import QDialog, QVBoxLayout, QProgressBar, QLabel
|
||||
from PyQt6.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() # 禁止关闭
|
||||
@ -38,19 +38,18 @@ class UIInitializer:
|
||||
if logname_count > 0:
|
||||
self.main_window.comboBox_logreader.addItems(logname_list)
|
||||
|
||||
def init_textEdit_console_style(self):
|
||||
# self.main_window.textEdit_console.setStyleSheet("""
|
||||
# QTextBrowser {
|
||||
# font-family: 'SimHei';
|
||||
# font-size: 20px;
|
||||
# color: #2c3e50;
|
||||
# background-color: #f8f9fa;
|
||||
# border: 1px solid #ddd;
|
||||
# border-radius: 4px;
|
||||
# padding: 8px;
|
||||
# }
|
||||
# """)
|
||||
pass
|
||||
def init_textBrowser_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;
|
||||
}
|
||||
""")
|
||||
|
||||
def init_textBrowser_baseinfo_style(self):
|
||||
self.main_window.textBrowser_baseinfo.setStyleSheet("""
|
||||
|
||||
35
src/frontend/utils.py
Normal file
35
src/frontend/utils.py
Normal file
@ -0,0 +1,35 @@
|
||||
from PyQt6.QtWidgets import QMessageBox
|
||||
|
||||
def show_upload_error_message(parent, file_path):
|
||||
"""显示文件格式错误提示弹窗"""
|
||||
QMessageBox.critical(
|
||||
parent,
|
||||
"文件格式错误",
|
||||
f"不支持的文件格式:\n{file_path}\n\n请上传.tar.gz格式的压缩包。"
|
||||
)
|
||||
|
||||
def show_critical_message(parent, title, message):
|
||||
"""显示通用错误提示弹窗"""
|
||||
QMessageBox.critical(
|
||||
parent,
|
||||
title,
|
||||
message
|
||||
)
|
||||
|
||||
def show_info_message(parent, title, message):
|
||||
"""显示信息提示弹窗"""
|
||||
QMessageBox.information(
|
||||
parent,
|
||||
title,
|
||||
message
|
||||
)
|
||||
|
||||
def show_question_message(parent, title, message):
|
||||
"""显示询问提示弹窗,返回用户选择(Yes/No)"""
|
||||
return QMessageBox.question(
|
||||
parent,
|
||||
title,
|
||||
message,
|
||||
QMessageBox.Yes | QMessageBox.No,
|
||||
QMessageBox.No
|
||||
)
|
||||
Loading…
Reference in New Issue
Block a user