import sys import os from PyQt5.QtWidgets import (QApplication, QMainWindow, QFileDialog, QTextBrowser, QVBoxLayout, QMessageBox, QTextEdit, QGraphicsScene, QGraphicsPixmapItem) from PyQt5.QtCore import Qt, QMimeData from PyQt5.QtGui import QPixmap from PyQt5.QtGui import QPainter # 单独导入QPainter用于抗锯齿设置 from PyQt5.QtGui import QStandardItemModel, QStandardItem, QDragEnterEvent, QDropEvent from MainWindow_ui import Ui_MainWindow # 导入转换后的UI类 import service import timelineEvent 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() self.init_tableView_alert_model() self.initUI() self.reInitTimeLineTab() 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" } 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) # 这是你已经写好的上传函数 def init_textbrowser_style(self): # 设置样式表(全局文本样式) self.textBrowser_info.setStyleSheet(""" QTextBrowser { font-family: 'SimHei'; font-size: 24px; 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 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 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): """处理上传的文件""" if not self.is_valid_file_format(file_path): self.show_error_message(file_path) return 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")) 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) self.textBrowser_console.insertPlainText("完成文件解析\n") except Exception as e: QMessageBox.critical(self, "处理失败", f"文件处理出错:{str(e)}") 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 ) 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_())