cell manager closing cell等待事件

本文介绍了一种Exadata等待事件,该事件在存储单元关闭过程中正常出现。如果查询在此期间等待该事件,则表明存储单元正在关闭中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

This is an Exadata wait event that occurs during the shutdown of a storage cell. This is normal activity associated with shutdown processing.  

Solutions

If there are queries waiting on this event, it may indicate that your storage cell is in progress of shutting down and the query is forced to wait.
检查以下代码并优化输出完整代码:import pandas as pd import matplotlib.pyplot as plt import matplotlib.font_manager as fm import plotly.express as px import plotly.graph_objects as go import os import tkinter as tk from tkinter import filedialog, ttk, messagebox import threading import datetime import numpy as np from openpyxl import Workbook from openpyxl.drawing.image import Image from io import BytesIO import platform import tempfile # 尝试导入tkinterdnd2用于文件拖放 USING_DND = False try: import tkinterdnd2 as tkdnd # 测试是否能访问tkdnd命名空间 root = tk.Tk() try: root.tk.call('package', 'require', 'tkdnd') USING_DND = True except tk.TclError: pass root.destroy() except ImportError: pass # 设置中文字体 plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"] # 为Plotly创建字体字符串 PLOTLY_FONT = "SimHei, WenQuanYi Micro Hei, Heiti TC" class App: def __init__(self, root): self.root = root self.dnd_supported = USING_DND # 如果使用tkinterdnd2,设置根窗口 if self.dnd_supported: try: self.root = tkdnd.DndTkRoot() except AttributeError: # 对于不支持DndTkRoot的版本 self.root = tk.Tk() tkdnd.DND_FILES = 1 tkdnd.DND_ALL = 1 self.root.title("设备状态数据分析工具") self.root.geometry("800x600") self.root.configure(bg="#f0f0f0") # 确保窗口关闭时线程能正确退出 self.root.protocol("WM_DELETE_WINDOW", self.on_closing) self.is_running = False self.temp_files = [] # 用于跟踪临时文件 # 创建UI组件 self.create_widgets() def create_widgets(self): # 标题 title_label = tk.Label( self.root, text="设备状态数据分析工具", font=("SimHei", 20, "bold"), bg="#f0f0f0", pady=20 ) title_label.pack() # 文件选择区域 file_frame = tk.Frame(self.root, bg="#e0e0e0", relief="solid", bd=2) file_frame.pack(fill="x", padx=50, pady=10) # 添加按钮和标签 button_frame = tk.Frame(file_frame, bg="#e0e0e0") button_frame.pack(fill="x", expand=True, padx=10, pady=10) select_button = tk.Button( button_frame, text="选择文件", font=("SimHei", 12), command=self.select_file, bg="#4CAF50", fg="white", padx=10, pady=5 ) select_button.pack(side="left") self.file_path_var = tk.StringVar() self.file_path_var.set("未选择文件") file_path_label = tk.Label( button_frame, textvariable=self.file_path_var, font=("SimHei", 10), bg="#e0e0e0", wraplength=400 ) file_path_label.pack(side="left", padx=10, fill="x", expand=True, anchor="w") # 拖放说明 dnd_status = self.dnd_supported drop_label = tk.Label( file_frame, text="或拖放CSV/Excel文件到此处", font=("SimHei", 10, "italic"), bg="#e0e0e0", fg="#666666" ) drop_label.pack(pady=5) # 尝试绑定拖放事件 try: if dnd_status: file_frame.drop_target_register(tkdnd.DND_FILES) file_frame.dnd_bind('<<Drop>>', self.on_drop) except Exception as e: # 如果绑定失败,禁用拖放功能 dnd_status = False drop_label.config( text="拖放功能不可用,请使用选择文件按钮", fg="#FF0000" ) if not dnd_status: drop_label.config( text="拖放功能不可用,请使用选择文件按钮", fg="#FF0000" ) # 进度条 progress_frame = tk.Frame(self.root, bg="#f0f0f0") progress_frame.pack(fill="x", padx=50, pady=10) progress_label = tk.Label( progress_frame, text="处理进度:", font=("SimHei", 12), bg="#f0f0f0" ) progress_label.pack(side="left") self.progress_var = tk.DoubleVar() self.progress_bar = ttk.Progressbar( progress_frame, variable=self.progress_var, length=500, mode="determinate" ) self.progress_bar.pack(side="left", padx=10) # 状态标签 self.status_var = tk.StringVar() self.status_var.set("等待文件...") status_label = tk.Label( self.root, textvariable=self.status_var, font=("SimHei", 12), bg="#f0f0f0", fg="#333333" ) status_label.pack(pady=10) # 日志区域 log_frame = tk.Frame(self.root, bg="#f0f0f0") log_frame.pack(fill="both", expand=True, padx=50, pady=10) log_label = tk.Label( log_frame, text="处理日志:", font=("SimHei", 12), bg="#f0f0f0" ) log_label.pack(anchor="w") self.log_text = tk.Text( log_frame, wrap="word", font=("SimHei", 10), bg="#ffffff", bd=1, relief="solid" ) self.log_text.pack(fill="both", expand=True, pady=5) scrollbar = tk.Scrollbar(self.log_text) scrollbar.pack(side="right", fill="y") self.log_text.config(yscrollcommand=scrollbar.set) scrollbar.config(command=self.log_text.yview) # 底部信息 footer_label = tk.Label( self.root, text="处理结果将保存为HTML文件和Excel图表", font=("SimHei", 10), bg="#f0f0f0", fg="#666666", pady=10 ) footer_label.pack(side="bottom") def select_file(self): """打开文件选择对话框""" file_path = filedialog.askopenfilename( filetypes=[("Excel files", "*.xlsx"), ("CSV files", "*.csv"), ("All files", "*.*")] ) if file_path: self.file_path_var.set(file_path) self.process_file(file_path) def on_drop(self, event): """处理文件拖放事件""" # 获取拖放的文件路径 file_path = event.data.strip('{}') # 处理可能的多文件拖放,只取第一个文件 if isinstance(file_path, str) and " " in file_path: file_path = file_path.split()[0] if (file_path.lower().endswith('.csv') or file_path.lower().endswith('.xlsx')) and os.path.exists(file_path): self.file_path_var.set(file_path) self.process_file(file_path) else: self.log("错误: 请拖放有效的CSV或Excel(.xlsx)文件") def process_file(self, file_path): """处理文件(在单独线程中运行)""" if self.is_running: self.log("警告: 已有任务在运行中") return self.is_running = True self.log(f"开始处理文件: {file_path}") self.status_var.set("正在处理...") self.progress_var.set(0) # 在单独线程中处理文件 thread = threading.Thread(target=self._process_file_thread, args=(file_path,)) thread.daemon = True thread.start() def _process_file_thread(self, file_path): """文件处理线程函数""" try: # 阶段1: 读取文件 self.progress_var.set(10) self.status_var.set("读取文件...") self.log("正在读取文件...") try: if file_path.lower().endswith('.csv'): df = pd.read_csv(file_path, header=0) self.log("成功读取CSV文件") elif file_path.lower().endswith('.xlsx'): df = pd.read_excel(file_path, header=0) self.log("成功读取Excel文件") else: self.log(f"错误: 不支持的文件格式。请使用CSV或.xlsx格式的Excel文件") self.status_var.set("处理失败") self.is_running = False return except Exception as e: self.log(f"错误: 读取文件失败 - {str(e)}") self.status_var.set("处理失败") self.is_running = False return self.progress_var.set(20) self.log("文件读取成功") self.log(f"数据基本信息:") self.log_df_info(df) # 阶段2: 数据预处理 self.status_var.set("数据预处理...") self.log("正在进行数据预处理...") # 检查关键列是否存在 required_columns = ['时间', '设备名称', '状态', '类型'] # 新增"类型"列 missing_columns = [col for col in required_columns if col not in df.columns] if missing_columns: self.log(f"错误:数据中缺少以下列: {', '.join(missing_columns)}") self.log(f"可用列名: {', '.join(df.columns.tolist())}") self.status_var.set("处理失败") self.is_running = False return # 转换时间列(匹配实际格式) try: # 优先尝试实际数据中的格式(短横线日期+秒) df['时间'] = pd.to_datetime(df['时间'], format='%Y-%m-%d %H:%M:%S') self.log(f"成功使用格式 '%Y-%m-%d %H:%M:%S' 解析时间列") except ValueError: # 其次尝试斜杠日期+秒 try: df['时间'] = pd.to_datetime(df['时间'], format='%Y/%m/%d %H:%M:%S') self.log(f"成功使用格式 '%Y/%m/%d %H:%M:%S' 解析时间列") except ValueError: # 再尝试不含秒的格式 try: df['时间'] = pd.to_datetime(df['时间'], format='%Y-%m-%d %H:%M') self.log(f"成功使用格式 '%Y-%m-%d %H:%M' 解析时间列") except ValueError: self.log("警告: 无法使用预设格式解析时间列,尝试自动解析") df['时间'] = pd.to_datetime(df['时间']) # 自动解析 # 状态值映射函数 def map_status(value): if pd.isna(value): return None elif '↑' in str(value) or '吸起' in str(value) or '开' in str(value): return 1 # 吸起状态 elif '↓' in str(value) or '落下' in str(value) or '关' in str(value): return 0 # 落下状态 else: self.log(f"警告:发现未知状态值 '{value}',已映射为 None") return None # 映射状态值并创建新列 df['状态值'] = df['状态'].apply(map_status) # 检查是否有有效的状态值 valid_status_count = df['状态值'].count() if valid_status_count == 0: self.log("错误:没有找到有效的状态值(↑/吸起 或 ↓/落下)") self.status_var.set("处理失败") self.is_running = False return # 处理缺失值 if df['状态值'].isna().sum() > 0: self.log(f"警告:状态值列包含 {df['状态值'].isna().sum()} 个缺失值,已使用前向填充") df['状态值'] = df['状态值'].fillna(method='ffill') # 按设备、类型和时间排序(新增类型排序) df = df.sort_values(['类型', '设备名称', '时间']) self.progress_var.set(40) self.log("数据预处理完成") self.log(f"设备类型分布: {df['类型'].value_counts().to_dict()}") # 打印类型分布 # 阶段3: 生成趋势图(按类型区分样式) self.status_var.set("生成趋势图...") self.log("正在生成趋势图...") fig = go.Figure() all_device_durations = {} # 定义类型样式映射(联锁:实线;自采PED继电器:虚线) type_style = { '联锁': {'dash': 'solid', 'color': 'green'}, '自采PED继电器': {'dash': 'dash', 'color': 'blue'} } # 按类型分组处理设备 for (type_name, device_name), device_df in df.groupby(['类型', '设备名称']): device_df = device_df.sort_values('时间') x = [] y = [] hover_text = [] current_start_time = None device_durations = [] if not device_df.empty: first_time = device_df['时间'].iloc[0] first_status = device_df['状态值'].iloc[0] current_start_time = first_time # 初始化当前状态的开始时间 # 添加第一个点 x.append(first_time) y.append(first_status) status_text = "吸起" if first_status == 1 else "落下" hover_text.append(f"{status_text}<br>开始时间: {first_time.strftime('%Y-%m-%d %H:%M:%S')}") # 处理剩余的点 for i in range(1, len(device_df)): prev_time = device_df['时间'].iloc[i - 1] current_time = device_df['时间'].iloc[i] current_status = device_df['状态值'].iloc[i] prev_status = device_df['状态值'].iloc[i - 1] # 如果状态变化,计算持续时间并添加到悬停文本 if current_status != prev_status: duration = current_time - current_start_time duration_seconds = duration.total_seconds() # 格式化持续时间,根据长短选择合适的单位 if duration_seconds < 60: duration_text = f"{duration_seconds:.1f}秒" elif duration_seconds < 3600: duration_text = f"{duration_seconds / 60:.1f}分钟" else: duration_text = f"{duration_seconds / 3600:.2f}小时" # 添加水平线(保持上一个状态直到当前时间点) x.append(current_time) y.append(prev_status) status_text = "吸起" if prev_status == 1 else "落下" hover_text.append( f"{status_text}<br>开始时间: {current_start_time.strftime('%Y-%m-%d %H:%M:%S')}<br>持续时间: {duration_text}") # 记录状态持续时间 device_durations.append({ '状态': status_text, '开始时间': current_start_time, '结束时间': current_time, '持续时间(秒)': duration_seconds, '持续时间文本': duration_text }) # 添加垂直线(状态变化) x.append(current_time) y.append(current_status) status_text = "吸起" if current_status == 1 else "落下" hover_text.append( f"{status_text}<br>开始时间: {current_time.strftime('%Y-%m-%d %H:%M:%S')}") # 更新当前状态的开始时间 current_start_time = current_time else: # 如果状态未变化,只添加水平线 x.append(current_time) y.append(current_status) status_text = "吸起" if current_status == 1 else "落下" hover_text.append( f"{status_text}<br>开始时间: {current_start_time.strftime('%Y-%m-%d %H:%M:%S')}") # 处理最后一个状态的持续时间(到数据结束) if current_start_time is not None: last_time = device_df['时间'].iloc[-1] duration = last_time - current_start_time duration_seconds = duration.total_seconds() if duration_seconds < 60: duration_text = f"{duration_seconds:.1f}秒" elif duration_seconds < 3600: duration_text = f"{duration_seconds / 60:.1f}分钟" else: duration_text = f"{duration_seconds / 3600:.2f}小时" # 记录最后一个状态的持续时间 device_durations.append({ '状态': "吸起" if device_df['状态值'].iloc[-1] == 1 else "落下", '开始时间': current_start_time, '结束时间': last_time, '持续时间(秒)': duration_seconds, '持续时间文本': duration_text }) # 保存该设备的所有状态持续时间 all_device_durations[device_name] = device_durations # 获取设备类型对应的样式 style = type_style.get(type_name, {'dash': 'solid', 'color': 'gray'}) if type_name not in type_style: self.log(f"警告:未知设备类型 '{type_name}',使用默认样式") # 添加矩形图轨迹,包含悬停文本,使用类型样式 fig.add_trace(go.Scatter( x=x, y=y, mode='lines', name=f"{type_name}-{device_name}", # 在图例中显示类型 fill='tozeroy', line=dict(width=2, dash=style['dash'], color=style['color']), opacity=0.7, hovertemplate='%{text}<extra></extra>', text=hover_text )) # 优化图表显示 fig.update_layout( hovermode="x unified", xaxis_title="时间", yaxis_title="状态值", legend_title="设备类型-名称", font=dict(family=PLOTLY_FONT, size=12), title="设备状态随时间变化(按类型区分)" ) # 添加水平线显示状态含义 fig.add_hline(y=1, line_dash="dash", line_color="green", annotation_text="吸起(↑)") fig.add_hline(y=0, line_dash="dash", line_color="red", annotation_text="落下(↓)") # 添加图例说明类型样式 fig.add_annotation( x=0.05, y=0.05, xref="paper", yref="paper", text="类型样式说明:<br>联锁 - 绿色实线<br>自采PED继电器 - 蓝色虚线", showarrow=False, font=dict(family=PLOTLY_FONT, size=10), bgcolor="rgba(255,255,255,0.8)", bordercolor="rgba(0,0,0,0.2)", borderwidth=1 ) # 保存图表为HTML文件 html_path = os.path.splitext(file_path)[0] + '_状态趋势图.html' fig.write_html(html_path) self.log(f"时间序列趋势图(按类型区分)已保存至: {html_path}") # 打印每个设备的状态持续时间 self.log("\n各设备状态持续时间详情:") for device_name, durations in all_device_durations.items(): device_type = df[df['设备名称'] == device_name]['类型'].iloc[0] self.log(f"\n设备: {device_type}-{device_name}") for i, duration in enumerate(durations): self.log( f"{i + 1}. {duration['状态']}: 从 {duration['开始时间'].strftime('%Y-%m-%d %H:%M:%S')} 到 {duration['结束时间'].strftime('%Y-%m-%d %H:%M:%S')}, 持续时间: {duration['持续时间文本']}") self.progress_var.set(50) # 阶段4: 生成状态变化连续性分析图表(按类型分组) self.status_var.set("生成状态变化连续性分析...") self.log("正在生成状态变化连续性分析...") # 按设备和类型分组并计算状态变化次数 device_status_changes = df.groupby(['类型', '设备名称'])['状态值'].apply( lambda x: x.diff().abs().sum()).reset_index(name='状态变化次数') # 计算每个设备的数据时间范围 time_range = df.groupby(['类型', '设备名称'])['时间'].agg(['min', 'max']) time_range['duration'] = time_range['max'] - time_range['min'] time_range['duration_days'] = time_range['duration'].dt.total_seconds() / (24 * 3600) # 计算状态变化频率(每天变化次数) device_status_changes = pd.merge(device_status_changes, time_range[['duration_days']], left_on=['类型', '设备名称'], right_index=True) device_status_changes['状态变化频率(次/天)'] = device_status_changes['状态变化次数'] / \ device_status_changes['duration_days'] # 创建状态变化次数柱状图(按类型分组) fig_bar_changes = px.bar( device_status_changes, x='设备名称', y='状态变化次数', color='类型', title='各设备状态变化次数统计(按类型分组)', labels={'状态变化次数': '状态变化次数', '设备名称': '设备名称', '类型': '设备类型'} ) # 优化柱状图显示 fig_bar_changes.update_layout( xaxis_title="设备名称", yaxis_title="状态变化次数", legend_title="设备类型", font=dict(family=PLOTLY_FONT, size=12), barmode='group' ) # 保存状态变化次数柱状图为HTML文件 bar_changes_html_path = os.path.splitext(file_path)[0] + '_状态变化次数柱状图.html' fig_bar_changes.write_html(bar_changes_html_path) self.log(f"状态变化次数柱状图(按类型分组)已保存至: {bar_changes_html_path}") # 创建状态变化频率柱状图(按类型分组) fig_bar_frequency = px.bar( device_status_changes, x='设备名称', y='状态变化频率(次/天)', color='类型', title='各设备状态变化频率统计(按类型分组)', labels={'状态变化频率(次/天)': '状态变化频率(次/天)', '设备名称': '设备名称', '类型': '设备类型'} ) # 优化柱状图显示 fig_bar_frequency.update_layout( xaxis_title="设备名称", yaxis_title="状态变化频率(次/天)", legend_title="设备类型", font=dict(family=PLOTLY_FONT, size=12), barmode='group' ) # 保存状态变化频率柱状图为HTML文件 bar_frequency_html_path = os.path.splitext(file_path)[0] + '_状态变化频率柱状图.html' fig_bar_frequency.write_html(bar_frequency_html_path) self.log(f"状态变化频率柱状图(按类型分组)已保存至: {bar_frequency_html_path}") # 计算状态持续时间 self.log("正在计算状态持续时间...") status_durations = [] for (type_name, device_name), device_df in df.groupby(['类型', '设备名称']): # 确保数据按时间排序 device_df = device_df.sort_values('时间') # 初始化当前状态和开始时间 if not device_df.empty: current_status = device_df['状态值'].iloc[0] start_time = device_df['时间'].iloc[0] # 遍历每一行计算状态持续时间 for i in range(1, len(device_df)): new_status = device_df['状态值'].iloc[i] end_time = device_df['时间'].iloc[i] # 如果状态变化,记录持续时间 if new_status != current_status: duration = (end_time - start_time).total_seconds() / 3600 # 转换为小时 status_durations.append({ '设备名称': device_name, '类型': type_name, # 新增类型信息 '状态': '吸起' if current_status == 1 else '落下', '持续时间(小时)': duration }) # 更新当前状态和开始时间 current_status = new_status start_time = end_time # 转换为DataFrame if status_durations: status_durations_df = pd.DataFrame(status_durations) # 计算每个设备的平均开启和关闭时间 avg_durations = status_durations_df.groupby(['类型', '设备名称', '状态'])['持续时间(小时)'].mean().reset_index() # 创建状态持续时间柱状图(按类型分组) fig_bar_duration = px.bar( avg_durations, x='设备名称', y='持续时间(小时)', color='状态', facet_row='类型', # 按类型分行显示 barmode='group', title='各设备平均状态持续时间(按类型分组)', labels={'持续时间(小时)': '平均持续时间(小时)', '设备名称': '设备名称', '状态': '状态', '类型': '设备类型'} ) # 优化柱状图显示 fig_bar_duration.update_layout( xaxis_title="设备名称", yaxis_title="平均持续时间(小时)", legend_title="状态", font=dict(family=PLOTLY_FONT, size=12) ) # 保存状态持续时间柱状图为HTML文件 bar_duration_html_path = os.path.splitext(file_path)[0] + '_状态持续时间柱状图.html' fig_bar_duration.write_html(bar_duration_html_path) self.log(f"状态持续时间柱状图(按类型分组)已保存至: {bar_duration_html_path}") else: self.log("警告: 无法计算状态持续时间,数据可能不足") bar_duration_html_path = None self.progress_var.set(70) # 阶段5: 生成时间序列柱状图,展示状态变化随时间的分布(按类型区分) self.status_var.set("生成时间序列柱状图...") self.log("正在生成时间序列柱状图...") # 创建一个新的DataFrame来存储状态变化点 change_points = [] for (type_name, device_name), device_df in df.groupby(['类型', '设备名称']): # 确保数据按时间排序 device_df = device_df.sort_values('时间') # 记录状态变化点 if len(device_df) > 1: for i in range(1, len(device_df)): if device_df['状态值'].iloc[i] != device_df['状态值'].iloc[i - 1]: change_points.append({ '设备名称': device_name, '类型': type_name, # 新增类型信息 '时间': device_df['时间'].iloc[i], '状态': '吸起' if device_df['状态值'].iloc[i] == 1 else '落下', '变化类型': '吸起' if device_df['状态值'].iloc[i] > device_df['状态值'].iloc[i - 1] else '落下' }) if change_points: # 转换为DataFrame change_points_df = pd.DataFrame(change_points) # 提取小时信息,用于分析状态变化的时间分布 change_points_df['小时'] = change_points_df['时间'].dt.hour # 计算每个小时的状态变化次数(按类型分组) hourly_changes = change_points_df.groupby(['小时', '类型', '变化类型']).size().reset_index(name='变化次数') # 创建时间序列柱状图(按类型分组) fig_time_series = px.bar( hourly_changes, x='小时', y='变化次数', color='变化类型', facet_row='类型', # 按类型分行显示 barmode='group', title='状态变化的时间分布(按类型分组)', labels={'小时': '小时', '变化次数': '状态变化次数', '变化类型': '变化类型', '类型': '设备类型'}, category_orders={'小时': list(range(24))} ) # 优化图表显示 fig_time_series.update_layout( xaxis_title="小时", yaxis_title="状态变化次数", legend_title="变化类型", font=dict(family=PLOTLY_FONT, size=12) ) # 保存时间序列柱状图为HTML文件 time_series_html_path = os.path.splitext(file_path)[0] + '_状态变化时间分布.html' fig_time_series.write_html(time_series_html_path) self.log(f"状态变化时间分布柱状图(按类型分组)已保存至: {time_series_html_path}") else: self.log("警告: 无法生成状态变化时间分布,数据可能不足") time_series_html_path = None self.progress_var.set(80) # 阶段6: 生成Excel报告(按类型分组) self.status_var.set("生成Excel报告...") self.log("正在生成Excel报告...") # 尝试保存趋势图为图像 img_path = None try: # 使用系统临时文件夹创建临时图像 temp_dir = tempfile.gettempdir() img_path = os.path.join(temp_dir, os.path.basename(os.path.splitext(file_path)[0]) + '_临时图像.png') self.temp_files.append(img_path) # 跟踪临时文件 # 保存趋势图为图像 fig.write_image(img_path) save_image_success = True except Exception as e: self.log(f"警告: 无法保存图像到Excel - {str(e)}") self.log("提示: 请安装kaleido包以支持图像导出: pip install -U kaleido") save_image_success = False # 创建Excel工作簿 wb = Workbook() ws = wb.active ws.title = "设备状态分析" # 添加标题 ws['A1'] = "设备状态数据分析报告" ws['A1'].font = {'name': 'SimHei', 'size': 16, 'bold': True} # 添加生成时间 ws['A2'] = f"生成时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" ws['A2'].font = {'name': 'SimHei', 'size': 10} # 添加源文件信息 ws['A3'] = f"源文件: {os.path.basename(file_path)}" ws['A3'].font = {'name': 'SimHei', 'size': 10} # 添加趋势图(如果成功保存图像) if save_image_success: ws['A5'] = "设备状态随时间变化趋势图(按类型区分)" ws['A5'].font = {'name': 'SimHei', 'size': 12, 'bold': True} img = Image(img_path) # 调整图像大小 img.width = 600 img.height = 400 ws.add_image(img, 'A6') else: ws['A5'] = "设备状态随时间变化趋势图(图像导出失败)" ws['A5'].font = {'name': 'SimHei', 'size': 12, 'bold': True} ws['A6'] = "请安装kaleido包以支持图像导出: pip install -U kaleido" ws['A6'].font = {'name': 'SimHei', 'size': 10, 'color': 'FF0000'} # 添加状态变化次数数据(按类型分组) ws['A26'] = "各设备状态变化次数统计(按类型分组)" ws['A26'].font = {'name': 'SimHei', 'size': 12, 'bold': True} # 添加表头 ws['A27'] = "设备类型" ws['B27'] = "设备名称" ws['C27'] = "状态变化次数" ws['D27'] = "状态变化频率(次/天)" ws['A27'].font = {'name': 'SimHei', 'bold': True} ws['B27'].font = {'name': 'SimHei', 'bold': True} ws['C27'].font = {'name': 'SimHei', 'bold': True} # 添加数据 for row_idx, row_data in enumerate(device_status_changes.values, 28): for col_idx, value in enumerate(row_data, 1): if col_idx == 3: # 状态变化频率保留两位小数 ws.cell(row=row_idx, column=col_idx, value=round(value, 2)) else: ws.cell(row=row_idx, column=col_idx, value=value) # 添加状态持续时间数据(如果有) if status_durations: ws['E26'] = "各设备平均状态持续时间" ws['E26'].font = {'name': 'SimHei', 'size': 12, 'bold': True} # 添加表头 ws['E27'] = "设备名称" ws['F27'] = "状态" ws['G27'] = "平均持续时间(小时)" ws['E27'].font = {'name': 'SimHei', 'bold': True} ws['F27'].font = {'name': 'SimHei', 'bold': True} ws['G27'].font = {'name': 'SimHei', 'bold': True} # 添加数据 for row_idx, row_data in enumerate(avg_durations.values, 28): for col_idx, value in enumerate(row_data, 5): if col_idx == 7: # 平均持续时间保留两位小数 ws.cell(row=row_idx, column=col_idx, value=round(value, 2)) else: ws.cell(row=row_idx, column=col_idx, value=value) # 保存Excel文件 excel_path = os.path.splitext(file_path)[0] + '_分析报告.xlsx' wb.save(excel_path) self.log(f"Excel报告已保存至: {excel_path}") self.progress_var.set(100) self.status_var.set("处理完成") self.log("处理完成!") # 打开结果文件夹 result_folder = os.path.dirname(file_path) if platform.system() == "Windows": os.startfile(result_folder) elif platform.system() == "Darwin": # macOS os.system(f'open "{result_folder}"') else: # Linux os.system(f'xdg-open "{result_folder}"') except Exception as e: self.log(f"发生错误: {str(e)}") self.status_var.set("处理失败") finally: # 清理临时文件 self.cleanup_temp_files() self.is_running = False def cleanup_temp_files(self): """清理临时文件""" for file_path in self.temp_files: if os.path.exists(file_path): try: os.remove(file_path) except Exception as e: self.log(f"警告: 无法删除临时文件 {file_path}: {str(e)}") self.temp_files = [] def log_df_info(self, df): """将DataFrame信息添加到日志""" # 捕获info()的输出 import io buffer = io.StringIO() df.info(buf=buffer) info = buffer.getvalue() buffer.close() # 将信息添加到日志 self.log(info) # 添加前几行数据 self.log("\n数据前几行信息:") self.log(df.head().to_string()) def log(self, message): """添加日志消息""" self.root.after(0, lambda: self._append_log(message)) def _append_log(self, message): """实际添加日志消息的方法""" self.log_text.insert("end", message + "\n") self.log_text.see("end") def on_closing(self): """处理窗口关闭事件""" if self.is_running: if messagebox.askyesno("确认", "正在处理数据,确定要退出吗?"): self.cleanup_temp_files() # 确保清理临时文件 self.root.destroy() else: self.cleanup_temp_files() # 确保清理临时文件 self.root.destroy() if __name__ == "__main__": root = tk.Tk() # 如果使用tkinterdnd2,替换根窗口 if USING_DND: root.destroy() try: root = tkdnd.DndTkRoot() except AttributeError: # 回退到普通Tk根窗口 root = tk.Tk() # 设置拖放类型常量 setattr(tkdnd, 'DND_FILES', 'DND_FILES') setattr(tkdnd, 'DND_ALL', 'DND_ALL') app = App(root) root.mainloop()
07-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值