2025-08-21 18:19:04 +08:00
|
|
|
|
import sys
|
|
|
|
|
|
import os
|
2025-08-22 21:19:53 +08:00
|
|
|
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QFileDialog, QTextBrowser, QVBoxLayout,
|
2025-08-21 18:19:04 +08:00
|
|
|
|
QMessageBox, QTextEdit, QGraphicsScene, QGraphicsPixmapItem)
|
2025-08-22 21:19:53 +08:00
|
|
|
|
from PyQt5.QtCore import Qt, QMimeData
|
2025-08-21 18:19:04 +08:00
|
|
|
|
from PyQt5.QtGui import QPixmap
|
|
|
|
|
|
from PyQt5.QtGui import QPainter # 单独导入QPainter用于抗锯齿设置
|
2025-08-22 21:19:53 +08:00
|
|
|
|
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QDragEnterEvent, QDropEvent
|
2025-08-21 18:19:04 +08:00
|
|
|
|
from MainWindow_ui import Ui_MainWindow # 导入转换后的UI类
|
|
|
|
|
|
import service
|
2025-08-22 21:19:53 +08:00
|
|
|
|
import timelineEvent
|
2025-08-21 18:19:04 +08:00
|
|
|
|
|
|
|
|
|
|
class MainWindow(QMainWindow, Ui_MainWindow):
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
|
super().__init__() # 初始化父类
|
|
|
|
|
|
self.setupUi(self) # 调用UI类的方法初始化界面
|
|
|
|
|
|
self.init_events() # 绑定事件(自定义方法)
|
|
|
|
|
|
self.parseStatus = service.ServiceStatus()
|
|
|
|
|
|
self.init_graphic_views()
|
|
|
|
|
|
self.init_textbrowser_style()
|
2025-08-22 21:19:53 +08:00
|
|
|
|
self.init_tableView_alert_model()
|
|
|
|
|
|
self.initUI()
|
|
|
|
|
|
self.reInitTimeLineTab()
|
2025-08-21 18:19:04 +08:00
|
|
|
|
|
|
|
|
|
|
self.comboBoxTextDict = {
|
|
|
|
|
|
"ALL_Temp": "all",
|
|
|
|
|
|
"CPU_Temp": "cpu",
|
|
|
|
|
|
"FPGA_Temp": "fpga",
|
|
|
|
|
|
"DIMM_Temp": "dimm",
|
|
|
|
|
|
"Inlet_CPU_Temp": "inlet_cpu",
|
|
|
|
|
|
"Inlet_FPGA_Temp": "inlet_fpga",
|
|
|
|
|
|
"M2_Temp": "m2",
|
|
|
|
|
|
"Outlet_CPU_Temp": "outlet_cpu",
|
|
|
|
|
|
"Outlet_FPGA_Temp": "outlet_fpga",
|
|
|
|
|
|
"VR_CPU_Temp": "vr_cpu",
|
|
|
|
|
|
"VR_FPGA_Temp": "vr_fpga"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-22 21:19:53 +08:00
|
|
|
|
def initUI(self):
|
|
|
|
|
|
# 允许窗口接收拖放事件
|
|
|
|
|
|
self.setAcceptDrops(True)
|
|
|
|
|
|
|
|
|
|
|
|
def reInitTimeLineTab(self):
|
|
|
|
|
|
# 1. 清除目标标签页的现有布局
|
|
|
|
|
|
if hasattr(self.tab_timeline_event, 'layout'):
|
|
|
|
|
|
# 移除现有布局中的所有组件
|
|
|
|
|
|
layout = self.tab_timeline_event.layout()
|
|
|
|
|
|
if layout:
|
|
|
|
|
|
while layout.count():
|
|
|
|
|
|
item = layout.takeAt(0)
|
|
|
|
|
|
widget = item.widget()
|
|
|
|
|
|
if widget:
|
|
|
|
|
|
widget.deleteLater()
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 创建时间线内容组件实例
|
|
|
|
|
|
self.timeline_content = timelineEvent.TimelineTabContent()
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 为标签页创建新布局并添加时间线组件
|
|
|
|
|
|
layout = QVBoxLayout(self.tab_timeline_event)
|
|
|
|
|
|
layout.setContentsMargins(0, 0, 0, 0) # 可选:去除边距
|
|
|
|
|
|
layout.addWidget(self.timeline_content)
|
|
|
|
|
|
|
|
|
|
|
|
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.process_uploaded_file(file_path) # 这是你已经写好的上传函数
|
|
|
|
|
|
|
2025-08-21 18:19:04 +08:00
|
|
|
|
def init_textbrowser_style(self):
|
|
|
|
|
|
# 设置样式表(全局文本样式)
|
|
|
|
|
|
self.textBrowser_info.setStyleSheet("""
|
|
|
|
|
|
QTextBrowser {
|
|
|
|
|
|
font-family: 'SimHei';
|
2025-08-22 21:19:53 +08:00
|
|
|
|
font-size: 24px;
|
2025-08-21 18:19:04 +08:00
|
|
|
|
color: #2c3e50;
|
|
|
|
|
|
background-color: #f8f9fa;
|
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
padding: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
|
|
def init_events(self):
|
|
|
|
|
|
"""绑定界面元素的事件(如按钮点击、输入框变化等)"""
|
|
|
|
|
|
# 示例:给按钮添加点击事件
|
|
|
|
|
|
# self.pushButton.clicked.connect(self.on_button_click) # pushButton是UI中定义的按钮对象名
|
|
|
|
|
|
|
|
|
|
|
|
# 绑定上传菜单项事件
|
|
|
|
|
|
self.actionUpload_log.triggered.connect(self.upload_file)
|
|
|
|
|
|
self.comboBox.currentTextChanged.connect(self.on_combo_changed)
|
|
|
|
|
|
|
|
|
|
|
|
def init_graphic_views(self):
|
|
|
|
|
|
# 初始化图形场景和视图
|
|
|
|
|
|
self.scene = QGraphicsScene(self) # 创建场景
|
|
|
|
|
|
self.graphicsView.setScene(self.scene) # 将场景绑定到视图
|
|
|
|
|
|
self.graphicsView.setRenderHint(QPainter.Antialiasing) # 抗锯齿
|
|
|
|
|
|
self.graphicsView.setRenderHint(QPainter.SmoothPixmapTransform) # 平滑缩放
|
|
|
|
|
|
|
|
|
|
|
|
# 存储当前显示的图片项
|
|
|
|
|
|
self.pixmap_item = None
|
|
|
|
|
|
self.current_image_path = None
|
2025-08-22 21:19:53 +08:00
|
|
|
|
|
|
|
|
|
|
def init_tableView_alert_model(self):
|
|
|
|
|
|
# 创建一个4行3列的模型
|
|
|
|
|
|
self.model_alert = QStandardItemModel(4, 5)
|
|
|
|
|
|
|
|
|
|
|
|
# 设置表头
|
|
|
|
|
|
self.model_alert.setHorizontalHeaderLabels(["时间", "部件", "等级", "方向", "描述"])
|
|
|
|
|
|
|
|
|
|
|
|
# 将模型绑定到QTableView
|
|
|
|
|
|
self.tableView_alert.setModel(self.model_alert)
|
|
|
|
|
|
|
|
|
|
|
|
# 可选:调整列宽自适应内容
|
|
|
|
|
|
self.tableView_alert.horizontalHeader().setSectionResizeMode(
|
|
|
|
|
|
self.tableView_alert.horizontalHeader().ResizeToContents
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def show_error_message(self, file_path):
|
|
|
|
|
|
"""显示格式错误提示弹窗"""
|
|
|
|
|
|
QMessageBox.critical(
|
|
|
|
|
|
self,
|
|
|
|
|
|
"文件格式错误",
|
|
|
|
|
|
f"不支持的文件格式:\n{os.path.basename(file_path)}\n\n请上传.tar.gz格式的压缩包。"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def is_valid_file_format(self, file_path):
|
|
|
|
|
|
"""检查文件是否为.tar.gz格式"""
|
|
|
|
|
|
# 获取文件后缀
|
|
|
|
|
|
_, ext = os.path.splitext(file_path)
|
|
|
|
|
|
|
|
|
|
|
|
# 先检查是否有.gz后缀
|
|
|
|
|
|
if ext.lower() == '.gz':
|
|
|
|
|
|
# 再检查去掉.gz后的后缀是否为.tar
|
|
|
|
|
|
base, ext2 = os.path.splitext(os.path.splitext(file_path)[0])
|
|
|
|
|
|
return ext2.lower() == '.tar'
|
|
|
|
|
|
|
|
|
|
|
|
return False
|
2025-08-21 18:19:04 +08:00
|
|
|
|
|
|
|
|
|
|
def upload_file(self):
|
|
|
|
|
|
"""打开文件选择对话框并处理选中的文件"""
|
|
|
|
|
|
# 打开文件选择对话框
|
|
|
|
|
|
# 参数说明:
|
|
|
|
|
|
# - self:父窗口
|
|
|
|
|
|
# - "选择文件":对话框标题
|
|
|
|
|
|
# - os.getcwd():默认打开路径(当前工作目录)
|
|
|
|
|
|
# - "所有文件 (*);;文本文件 (*.txt);;图片文件 (*.png *.jpg)":文件筛选器
|
|
|
|
|
|
file_path, file_type = QFileDialog.getOpenFileName(
|
|
|
|
|
|
self,
|
|
|
|
|
|
"选择文件",
|
|
|
|
|
|
os.getcwd(),
|
|
|
|
|
|
"所有文件 (*);;日志压缩文件 (*.gz)"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 判断用户是否选择了文件(取消选择时file_path为空)
|
|
|
|
|
|
if file_path:
|
|
|
|
|
|
self.process_uploaded_file(file_path)
|
|
|
|
|
|
# else:
|
|
|
|
|
|
# self.text_edit.append("已取消文件选择")
|
|
|
|
|
|
|
|
|
|
|
|
def process_uploaded_file(self, file_path):
|
2025-08-22 21:19:53 +08:00
|
|
|
|
"""处理上传的文件"""
|
|
|
|
|
|
if not self.is_valid_file_format(file_path):
|
|
|
|
|
|
self.show_error_message(file_path)
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2025-08-21 18:19:04 +08:00
|
|
|
|
try:
|
|
|
|
|
|
# 获取文件信息
|
|
|
|
|
|
file_name = os.path.basename(file_path)
|
|
|
|
|
|
file_size = os.path.getsize(file_path)
|
|
|
|
|
|
file_size_kb = file_size / 1024 # 转换为KB
|
|
|
|
|
|
|
|
|
|
|
|
# 显示文件信息
|
|
|
|
|
|
info = f"已上传文件:\n"
|
|
|
|
|
|
info += f"文件名:{file_name}\n"
|
|
|
|
|
|
info += f"路径:{file_path}\n"
|
|
|
|
|
|
info += f"大小:{file_size_kb:.2f} KB\n"
|
|
|
|
|
|
info += "-" * 50 + "\n"
|
|
|
|
|
|
|
|
|
|
|
|
self.textBrowser_console.insertPlainText(info)
|
|
|
|
|
|
|
|
|
|
|
|
# 这里可以添加实际的文件处理逻辑:
|
|
|
|
|
|
service.send_log_to_cache(file_path)
|
|
|
|
|
|
service.start_diagnose(self.parseStatus)
|
|
|
|
|
|
|
|
|
|
|
|
if self.parseStatus.baseinfo_status:
|
|
|
|
|
|
baseinfo_str = service.get_baseinfo_str()
|
|
|
|
|
|
self.textBrowser_info.insertPlainText(baseinfo_str)
|
|
|
|
|
|
|
|
|
|
|
|
if self.parseStatus.sensorhistory_status:
|
|
|
|
|
|
self.display_pic(service.get_sensorhistory_path("all"))
|
2025-08-22 21:19:53 +08:00
|
|
|
|
|
|
|
|
|
|
if self.parseStatus.parseidl_status:
|
|
|
|
|
|
if not service.is_idl_alert_empty():
|
|
|
|
|
|
alert_json = service.get_idl_alert_json()
|
|
|
|
|
|
self.fill_tableView_alert(alert_json)
|
|
|
|
|
|
|
|
|
|
|
|
if self.parseStatus.eventline_status:
|
|
|
|
|
|
event_json = service.get_timeline_event_json()
|
|
|
|
|
|
self.timeline_content.load_events_from_json(event_json)
|
2025-08-21 18:19:04 +08:00
|
|
|
|
|
|
|
|
|
|
self.textBrowser_console.insertPlainText("完成文件解析\n")
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
QMessageBox.critical(self, "处理失败", f"文件处理出错:{str(e)}")
|
2025-08-22 21:19:53 +08:00
|
|
|
|
|
|
|
|
|
|
def fill_tableView_alert(self, json_data):
|
|
|
|
|
|
"""从JSON数据更新表格内容"""
|
|
|
|
|
|
# 清空现有数据
|
|
|
|
|
|
self.model_alert.clear()
|
|
|
|
|
|
|
|
|
|
|
|
# 根据JSON数据结构设置表头和内容
|
|
|
|
|
|
if isinstance(json_data, list):
|
|
|
|
|
|
# 处理列表类型的JSON(如多条记录)
|
|
|
|
|
|
if json_data and isinstance(json_data[0], dict):
|
|
|
|
|
|
# 使用第一条记录的键作为表头
|
|
|
|
|
|
headers = json_data[0].keys()
|
|
|
|
|
|
self.model_alert.setHorizontalHeaderLabels(headers)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加所有行数据
|
|
|
|
|
|
for item in json_data:
|
|
|
|
|
|
row_items = []
|
|
|
|
|
|
for key in headers:
|
|
|
|
|
|
# 将值转换为字符串显示
|
|
|
|
|
|
value = str(item.get(key, ""))
|
|
|
|
|
|
row_items.append(QStandardItem(value))
|
|
|
|
|
|
self.model_alert.appendRow(row_items)
|
|
|
|
|
|
elif isinstance(json_data, dict):
|
|
|
|
|
|
# 处理字典类型的JSON(键值对)
|
|
|
|
|
|
self.model_alert.setHorizontalHeaderLabels(["键", "值"])
|
|
|
|
|
|
for key, value in json_data.items():
|
|
|
|
|
|
key_item = QStandardItem(str(key))
|
|
|
|
|
|
value_item = QStandardItem(str(value))
|
|
|
|
|
|
# 设置单元格不可编辑
|
|
|
|
|
|
key_item.setEditable(False)
|
|
|
|
|
|
value_item.setEditable(False)
|
|
|
|
|
|
self.model_alert.appendRow([key_item, value_item])
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 处理简单类型的JSON
|
|
|
|
|
|
self.model_alert.setHorizontalHeaderLabels(["数据"])
|
|
|
|
|
|
self.model_alert.appendRow([QStandardItem(str(json_data))])
|
|
|
|
|
|
|
|
|
|
|
|
# 调整列宽以适应内容
|
|
|
|
|
|
self.tableView_alert.horizontalHeader().setSectionResizeMode(
|
|
|
|
|
|
self.tableView_alert.horizontalHeader().ResizeToContents
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-08-21 18:19:04 +08:00
|
|
|
|
def on_combo_changed(self, selected_text):
|
|
|
|
|
|
"""下拉列表选项变化时切换图片"""
|
|
|
|
|
|
self.display_pic(service.get_sensorhistory_path(self.comboBoxTextDict[selected_text]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def display_pic(self, image_path):
|
|
|
|
|
|
"""在QGraphicsView中显示图片"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
# 清空场景中已有的内容
|
|
|
|
|
|
self.scene.clear()
|
|
|
|
|
|
|
|
|
|
|
|
# 加载图片并创建图形项
|
|
|
|
|
|
pixmap = QPixmap(image_path)
|
|
|
|
|
|
if pixmap.isNull():
|
|
|
|
|
|
raise Exception("无法加载图片文件")
|
|
|
|
|
|
|
|
|
|
|
|
# 创建图片项并添加到场景
|
|
|
|
|
|
self.pixmap_item = QGraphicsPixmapItem(pixmap)
|
|
|
|
|
|
self.scene.addItem(self.pixmap_item)
|
|
|
|
|
|
|
|
|
|
|
|
# 初始显示时让图片居中
|
|
|
|
|
|
self.graphicsView.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
QMessageBox.critical(self, "错误", f"显示图片失败:{str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
def zoom_with_mouse_wheel(self, event):
|
|
|
|
|
|
"""鼠标滚轮缩放图片"""
|
|
|
|
|
|
if self.pixmap_item: # 只有存在图片时才允许缩放
|
|
|
|
|
|
# 获取当前鼠标位置作为缩放中心
|
|
|
|
|
|
mouse_pos = event.pos()
|
|
|
|
|
|
scene_pos = self.graphicsView.mapToScene(mouse_pos)
|
|
|
|
|
|
|
|
|
|
|
|
# 缩放因子(滚轮向前放大,向后缩小)
|
|
|
|
|
|
scale_factor = 1.1 if event.angleDelta().y() > 0 else 0.9
|
|
|
|
|
|
|
|
|
|
|
|
# 缩放视图
|
|
|
|
|
|
self.graphicsView.scale(scale_factor, scale_factor)
|
|
|
|
|
|
|
|
|
|
|
|
# 缩放后将鼠标位置保持在原场景位置(避免缩放时画面跳动)
|
|
|
|
|
|
new_mouse_pos = self.graphicsView.mapFromScene(scene_pos)
|
|
|
|
|
|
delta = new_mouse_pos - mouse_pos
|
|
|
|
|
|
self.graphicsView.horizontalScrollBar().setValue(
|
|
|
|
|
|
self.graphicsView.horizontalScrollBar().value() - delta.x()
|
|
|
|
|
|
)
|
|
|
|
|
|
self.graphicsView.verticalScrollBar().setValue(
|
|
|
|
|
|
self.graphicsView.verticalScrollBar().value() - delta.y()
|
|
|
|
|
|
)
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 若无图片,忽略滚轮事件
|
|
|
|
|
|
super(type(self.graphicsView), self.graphicsView).wheelEvent(event)
|
|
|
|
|
|
|
|
|
|
|
|
def resizeEvent(self, event):
|
|
|
|
|
|
"""窗口大小改变时,自适应调整图片显示"""
|
|
|
|
|
|
if self.pixmap_item and not self.graphicsView.transform().isIdentity():
|
|
|
|
|
|
# 仅在未手动缩放时自动适应窗口
|
|
|
|
|
|
self.graphicsView.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)
|
|
|
|
|
|
super().resizeEvent(event)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
service.app_cache_init()
|
|
|
|
|
|
# 创建应用实例
|
|
|
|
|
|
app = QApplication(sys.argv)
|
|
|
|
|
|
# 创建主窗口并显示
|
|
|
|
|
|
window = MainWindow()
|
|
|
|
|
|
window.show()
|
|
|
|
|
|
# 进入应用主循环
|
|
|
|
|
|
sys.exit(app.exec_())
|