Ext之comboBox 本地数据字典

本文介绍了一个关于车辆状态选择框的前端配置示例,该选择框使用ExtJS库实现,并预设了两种状态:启用和停用。通过本地存储方式展示选项,适用于简单的状态切换场景。
部署运行你感兴趣的模型镜像
//车辆状态(值固定,写在前台页面,不用后台数据字典)	
var carState = new Ext.form.ComboBox({
hiddenName:'car.status',
allowBlank:false,
width : 165,
editable:false,
fieldLabel:'<font color="red">*</font>车辆状态',
triggerAction: 'all',
lazyRender:true,
mode: 'local',
store: new Ext.data.ArrayStore({
fields: [
'idValue',
'displayText'
],
data: [['0', '停用'], ['1','启用']]
}),
valueField: 'idValue',
displayField: 'displayText'
});



--

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

import tkinter as tk from tkinter import filedialog, ttk, messagebox import pandas as pd import os import math from datetime import datetime import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib import ticker import numpy as np # 设置matplotlib中文字体 plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"] class F0Calculator: def __init__(self, root): self.root = root self.root.title("F0计算工具(完整显示所有曲线)") self.root.geometry("1400x900") self.root.resizable(True, True) # 设置中文字体 self.style = ttk.Style() self.style.configure("TLabel", font=("SimHei", 9)) self.style.configure("TButton", font=("SimHei", 9)) self.style.configure("TEntry", font=("SimHei", 9)) self.style.configure("Treeview", font=("SimHei", 9)) # 数据存储 self.file_data = {} self.current_file = None self.x_values = None self.curve_data = {} # 存储所有曲线,异常曲线值为None self.curve_vars = {} # 存储曲线复选框变量 self.curve_checkboxes = {} # 存储曲线复选框控件引用 self.results = [] # 起始列设置(默认从第1列开始) self.start_col = tk.IntVar(value=1) # TXT文件分隔符设置 self.delimiter = tk.StringVar(value="\t") # 默认制表符分隔 # 计算范围设置 self.calc_start_x = tk.StringVar(value="") self.calc_end_x = tk.StringVar(value="") # 错误提示控制 self.show_errors = tk.BooleanVar(value=True) # 默认显示错误提示 # 图表相关变量 self.fig = None self.canvas = None self.ax = None self.lines = [] self.selected_curve = None self.coord_text = None self.range_rect = None # 用于显示计算范围的矩形 self.cursor_line_x = None # X光标线 self.cursor_line_y = None # Y光标线 self.cursor_point = None # 光标在曲线上的点 # 创建界面 self.create_widgets() def create_widgets(self): # 主窗口分割 self.main_paned = ttk.PanedWindow(self.root, orient=tk.VERTICAL) self.main_paned.pack(fill=tk.BOTH, expand=True, padx=2, pady=2) # 上方控制区域 self.controls_frame = ttk.Frame(self.main_paned) self.main_paned.add(self.controls_frame, weight=5) # 下方内容区域 self.content_paned = ttk.PanedWindow(self.main_paned, orient=tk.VERTICAL) self.main_paned.add(self.content_paned, weight=16) # 图表区域 self.chart_frame = ttk.LabelFrame(self.content_paned, text="数据图表") self.content_paned.add(self.chart_frame, weight=14) # 结果区域 self.result_frame = ttk.LabelFrame(self.content_paned, text="计算结果") self.content_paned.add(self.result_frame, weight=2) # 构建控制区域 self.build_controls_frame() # 初始化结果表格 self.init_result_table() # 状态栏 self.status_var = tk.StringVar(value="就绪") status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W, font=("SimHei", 9)) status_bar.pack(side=tk.BOTTOM, fill=tk.X) def build_controls_frame(self): # 调整网格布局为3列 self.controls_frame.grid_columnconfigure(0, weight=1) self.controls_frame.grid_columnconfigure(1, weight=1) self.controls_frame.grid_columnconfigure(2, weight=0) for i in range(5): self.controls_frame.grid_rowconfigure(i, weight=1) # 数据文件夹选择 folder_frame = ttk.LabelFrame(self.controls_frame, text="数据文件夹", padding=(2, 1)) folder_frame.grid(row=0, column=0, sticky="ew", padx=2, pady=0) ttk.Label(folder_frame, text="文件夹:").pack(side=tk.LEFT, padx=2) self.folder_path_var = tk.StringVar() ttk.Entry(folder_frame, textvariable=self.folder_path_var, width=30).pack(side=tk.LEFT, padx=2, fill=tk.X) ttk.Button(folder_frame, text="浏览", command=self.browse_folder).pack(side=tk.LEFT, padx=2) # 文件切换 file_frame = ttk.LabelFrame(self.controls_frame, text="文件切换", padding=(2, 1)) file_frame.grid(row=0, column=1, sticky="ew", padx=2, pady=0) ttk.Label(file_frame, text="选择文件:").pack(side=tk.LEFT, padx=2) self.file_combobox = ttk.Combobox(file_frame, state="disabled", width=30) self.file_combobox.pack(side=tk.LEFT, padx=2, fill=tk.X) self.file_combobox.bind("<<ComboboxSelected>>", self.on_file_selected) # 起始列选择框和错误提示控制 start_col_frame = ttk.LabelFrame(self.controls_frame, text="数据设置", padding=(2, 1)) start_col_frame.grid(row=0, column=2, sticky="ew", padx=2, pady=0) # 起始列设置 ttk.Label(start_col_frame, text="起始列:").pack(side=tk.LEFT, padx=2) ttk.Entry(start_col_frame, textvariable=self.start_col, width=5).pack(side=tk.LEFT, padx=2) ttk.Label(start_col_frame, text="(从1开始)").pack(side=tk.LEFT, padx=1) # 错误提示复选框 ttk.Checkbutton(start_col_frame, text="显示错误提示", variable=self.show_errors).pack(side=tk.LEFT, padx=5) # TXT文件设置 txt_frame = ttk.LabelFrame(self.controls_frame, text="TXT文件设置", padding=(2, 1)) txt_frame.grid(row=1, column=0, columnspan=3, sticky="ew", padx=2, pady=0) ttk.Label(txt_frame, text="分隔符:").pack(side=tk.LEFT, padx=5) self.delimiter_combobox = ttk.Combobox(txt_frame, values=["制表符", "逗号", "空格", "其他"], width=8, state="readonly") self.delimiter_combobox.current(0) # 默认选择制表符 self.delimiter_combobox.bind("<<ComboboxSelected>>", self.on_delimiter_changed) ttk.Label(txt_frame, text="自定义:").pack(side=tk.LEFT, padx=2) self.custom_delimiter = ttk.Entry(txt_frame, textvariable=self.delimiter, width=3) self.custom_delimiter.pack(side=tk.LEFT, padx=2) ttk.Label(txt_frame, text="(仅当选择'其他'时生效)").pack(side=tk.LEFT, padx=2) # 图表控制(跨3列) chart_ctrl_frame = ttk.LabelFrame(self.controls_frame, text="图表控制", padding=(2, 1)) chart_ctrl_frame.grid(row=2, column=0, columnspan=3, sticky="ew", padx=2, pady=0) ttk.Label(chart_ctrl_frame, text="显示曲线:").pack(side=tk.LEFT, padx=2) # 曲线复选框滚动区域(确保能显示所有曲线) scroll_frame = ttk.Frame(chart_ctrl_frame) scroll_frame.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=2) # 确保滚动条正常工作的核心设置 self.canvas_check = tk.Canvas(scroll_frame, height=30) self.hscroll = ttk.Scrollbar(scroll_frame, orient=tk.HORIZONTAL, command=self.canvas_check.xview) self.curve_check_frame = ttk.Frame(self.canvas_check) # 绑定事件确保滚动区域正确更新 self.curve_check_frame.bind( "<Configure>", lambda e: self.canvas_check.configure( scrollregion=self.canvas_check.bbox("all") ) ) # 将框架放入Canvas self.canvas_check.create_window((0, 0), window=self.curve_check_frame, anchor=tk.NW) self.canvas_check.configure(xscrollcommand=self.hscroll.set) # 布局Canvas和滚动条 self.canvas_check.pack(side=tk.TOP, fill=tk.X, expand=True) self.hscroll.pack(side=tk.BOTTOM, fill=tk.X) # 曲线选择控制按钮 ttk.Button(chart_ctrl_frame, text="全选", command=self.select_all_curves).pack(side=tk.LEFT, padx=2) ttk.Button(chart_ctrl_frame, text="反选", command=self.invert_selection).pack(side=tk.LEFT, padx=2) # 筛选功能 ttk.Label(chart_ctrl_frame, text="筛选:").pack(side=tk.LEFT, padx=5) self.curve_filter = tk.StringVar() ttk.Entry(chart_ctrl_frame, textvariable=self.curve_filter, width=10).pack(side=tk.LEFT, padx=2) ttk.Button(chart_ctrl_frame, text="查找", command=self.filter_curves).pack(side=tk.LEFT, padx=2) ttk.Button(chart_ctrl_frame, text="清除", command=self.clear_filter).pack(side=tk.LEFT, padx=2) # 坐标轴设置(跨3列) axis_frame = ttk.LabelFrame(self.controls_frame, text="坐标轴设置", padding=(2, 1)) axis_frame.grid(row=3, column=0, columnspan=3, sticky="ew", padx=2, pady=0) # X轴范围 ttk.Label(axis_frame, text="X:").pack(side=tk.LEFT, padx=2) self.x_min_var = tk.StringVar() ttk.Entry(axis_frame, textvariable=self.x_min_var, width=10).pack(side=tk.LEFT, padx=1) ttk.Label(axis_frame, text="-").pack(side=tk.LEFT) self.x_max_var = tk.StringVar() ttk.Entry(axis_frame, textvariable=self.x_max_var, width=10).pack(side=tk.LEFT, padx=1) # Y轴范围 ttk.Label(axis_frame, text="Y:").pack(side=tk.LEFT, padx=5) self.y_min_var = tk.StringVar() ttk.Entry(axis_frame, textvariable=self.y_min_var, width=10).pack(side=tk.LEFT, padx=1) ttk.Label(axis_frame, text="-").pack(side=tk.LEFT) self.y_max_var = tk.StringVar() ttk.Entry(axis_frame, textvariable=self.y_max_var, width=10).pack(side=tk.LEFT, padx=1) ttk.Button(axis_frame, text="应用", command=self.apply_axis_limits).pack(side=tk.LEFT, padx=5) ttk.Button(axis_frame, text="重置", command=self.reset_axis_limits).pack(side=tk.LEFT, padx=2) # 计算范围和操作区(跨3列) calc_frame = ttk.LabelFrame(self.controls_frame, text="计算设置", padding=(2, 1)) calc_frame.grid(row=4, column=0, columnspan=3, sticky="ew", padx=2, pady=0) # 计算范围设置 ttk.Label(calc_frame, text="计算范围 X:").pack(side=tk.LEFT, padx=5) ttk.Entry(calc_frame, textvariable=self.calc_start_x, width=10).pack(side=tk.LEFT, padx=1) ttk.Label(calc_frame, text="-").pack(side=tk.LEFT) ttk.Entry(calc_frame, textvariable=self.calc_end_x, width=10).pack(side=tk.LEFT, padx=1) # 衰减值设置 ttk.Label(calc_frame, text="衰减值(dB):").pack(side=tk.LEFT, padx=5) self.db_var = tk.StringVar(value="-0.3") ttk.Entry(calc_frame, textvariable=self.db_var, width=8).pack(side=tk.LEFT, padx=2) # 光标跟踪 ttk.Label(calc_frame, text="跟踪曲线:").pack(side=tk.LEFT, padx=5) self.track_curve_combobox = ttk.Combobox(calc_frame, state="disabled", width=10) self.track_curve_combobox.pack(side=tk.LEFT, padx=2) self.track_curve_combobox.bind("<<ComboboxSelected>>", self.on_track_curve_selected) self.coord_display = ttk.Label(calc_frame, text="X=?, Y=?", width=30) self.coord_display.pack(side=tk.LEFT, padx=5) # 操作按钮 ttk.Button(calc_frame, text="计算", command=self.calculate).pack(side=tk.LEFT, padx=2) ttk.Button(calc_frame, text="导出", command=self.export_results).pack(side=tk.LEFT, padx=2) ttk.Button(calc_frame, text="绘图", command=self.plot_chart).pack(side=tk.LEFT, padx=2) def on_delimiter_changed(self, event): """处理分隔符选择变化""" selection = self.delimiter_combobox.get() if selection == "制表符": self.delimiter.set("\t") self.custom_delimiter.configure(state="disabled") elif selection == "逗号": self.delimiter.set(",") self.custom_delimiter.configure(state="disabled") elif selection == "空格": self.delimiter.set(" ") self.custom_delimiter.configure(state="disabled") elif selection == "其他": self.custom_delimiter.configure(state="enabled") def init_result_table(self): # 创建结果表格 columns = ("curve", "ymax", "target_y", "factor", "x1", "x2", "f0") self.result_tree = ttk.Treeview(self.result_frame, columns=columns, show="headings", height=3) # 设置列标题 self.result_tree.heading("curve", text="曲线编号") self.result_tree.heading("ymax", text="Y最大值") self.result_tree.heading("target_y", text="目标Y值") self.result_tree.heading("factor", text="计算系数(%)") self.result_tree.heading("x1", text="上升段X值") self.result_tree.heading("x2", text="下降段X值") self.result_tree.heading("f0", text="F0 (Hz)") # 调整列宽 self.result_tree.column("curve", width=80, anchor=tk.CENTER) self.result_tree.column("ymax", width=100, anchor=tk.CENTER) self.result_tree.column("target_y", width=100, anchor=tk.CENTER) self.result_tree.column("factor", width=100, anchor=tk.CENTER) self.result_tree.column("x1", width=120, anchor=tk.CENTER) self.result_tree.column("x2", width=120, anchor=tk.CENTER) self.result_tree.column("f0", width=100, anchor=tk.CENTER) # 添加滚动条 vscrollbar = ttk.Scrollbar(self.result_frame, orient=tk.VERTICAL, command=self.result_tree.yview) self.result_tree.configure(yscroll=vscrollbar.set) hscrollbar = ttk.Scrollbar(self.result_frame, orient=tk.HORIZONTAL, command=self.result_tree.xview) self.result_tree.configure(xscroll=hscrollbar.set) # 布局 hscrollbar.pack(side=tk.BOTTOM, fill=tk.X) vscrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.result_tree.pack(fill=tk.BOTH, expand=True) def browse_folder(self): """浏览并选择包含数据文件的文件夹""" folder_path = filedialog.askdirectory(title="选择数据文件夹") if folder_path: self.folder_path_var.set(folder_path) self.load_folder_data(folder_path) def load_folder_data(self, folder_path): """加载文件夹中所有支持格式的文件数据,确保读取所有曲线""" try: self.status_var.set(f"正在加载文件夹: {os.path.basename(folder_path)}") self.root.update() # 清空之前的数据 self.file_data = {} self.file_combobox['values'] = [] self.file_combobox['state'] = "disabled" self.clear_chart() self.clear_results() self.track_curve_combobox['values'] = [] self.track_curve_combobox['state'] = "disabled" # 清空计算范围 self.calc_start_x.set("") self.calc_end_x.set("") # 获取文件夹中所有支持格式的文件 supported_files = [] for file in os.listdir(folder_path): if file.endswith(('.xls', '.xlsx', '.txt')): supported_files.append(file) if not supported_files: if self.show_errors.get(): messagebox.showwarning("警告", "所选文件夹中未找到支持的文件(.xls, .xlsx, .txt)") self.status_var.set("就绪") return # 获取起始列设置(转换为0-based索引) start_col = self.start_col.get() - 1 if start_col < 0: if self.show_errors.get(): messagebox.showwarning("警告", "起始列不能小于1,已自动调整为1") start_col = 0 self.start_col.set(1) # 加载每个文件的数据 for file in supported_files: file_path = os.path.join(folder_path, file) try: # 根据文件扩展名选择合适的读取方法和引擎 ext = os.path.splitext(file)[1].lower() # 关键修复:读取所有行,不设置任何行数限制 if ext == '.xlsx': # .xlsx格式专用openpyxl引擎,读取所有行 data = pd.read_excel(file_path, header=None, engine='openpyxl') elif ext == '.xls': # 尝试用xlrd读取所有行 try: data = pd.read_excel(file_path, header=None, engine='xlrd') except Exception as e: if self.show_errors.get(): messagebox.showwarning("警告", f"{file} 无法用xlrd读取,尝试用CSV解析: {str(e)}") try: # 尝试用CSV方式解析xls文件 delimiter = self.delimiter.get() if not delimiter.strip(): delimiter = None # 自动检测分隔符 data = pd.read_csv( file_path, header=None, sep=delimiter, engine='python', skip_blank_lines=True ) except Exception as e_csv: raise Exception(f"无法用xlrd或CSV解析文件: {e_csv}") elif ext == '.txt': # 读取TXT文件,确保读取所有行 delimiter = self.delimiter.get() if not delimiter.strip(): delimiter = None # 让pandas自动检测空格分隔 data = pd.read_csv( file_path, header=None, sep=delimiter, engine='python', skip_blank_lines=True, low_memory=False # 禁用低内存模式,避免数据截断 ) # 验证数据格式 if len(data) < 2: if self.show_errors.get(): messagebox.showwarning("警告", f"{file} 至少需要包含两行数据,已跳过") continue # 验证起始列是否有效 if start_col >= data.shape[1]: if self.show_errors.get(): messagebox.showwarning("警告", f"{file} 没有足够的列,已使用最后一列作为起始列") start_col = max(0, data.shape[1] - 1) # 第一行为X值(从起始列开始) x_values = data.iloc[0, start_col:].dropna().values # 读取所有行作为曲线数据(从第二行开始,不限制行数) curve_data = {} # 遍历所有行,不设置上限 for i in range(1, len(data)): curve_name = f"曲线{i}" try: # 读取完整的Y值,不限制数量 y_values = data.iloc[i, start_col:].dropna().values if len(y_values) == len(x_values): curve_data[curve_name] = y_values else: # 数据异常,保留曲线位置但值为None curve_data[curve_name] = None except Exception as e: # 数据异常,保留曲线位置但值为None curve_data[curve_name] = None if curve_data: # 只保存有有效曲线数据的文件 self.file_data[file] = { 'x_values': x_values, 'curve_data': curve_data } self.status_var.set(f"已加载 {file},共 {len(curve_data)} 条曲线") self.root.update() # 实时更新状态 except Exception as e: if self.show_errors.get(): messagebox.showwarning("警告", f"加载 {file} 时出错: {str(e)}, 已跳过") except Exception as e: if self.show_errors.get(): messagebox.showerror("错误", f"加载文件夹时出错: {str(e)}") self.status_var.set("加载文件夹失败") return if not self.file_data: if self.show_errors.get(): messagebox.showwarning("警告", "未成功加载任何文件的数据") self.status_var.set("就绪") return # 更新下拉菜单 self.file_combobox['values'] = list(self.file_data.keys()) self.file_combobox['state'] = "readonly" # 自动选择第一个文件 if self.file_data: first_file = next(iter(self.file_data.keys())) self.file_combobox.current(0) self.on_file_selected(None) total_curves = sum(len(file_info['curve_data']) for file_info in self.file_data.values()) self.status_var.set(f"已加载 {len(self.file_data)} 个文件,共 {total_curves} 条曲线") def on_file_selected(self, event): """当选择不同文件时更新数据和界面,确保显示所有曲线""" selected_file = self.file_combobox.get() if not selected_file or selected_file not in self.file_data: return self.current_file = selected_file file_info = self.file_data[selected_file] # 更新当前文件的数据 self.x_values = file_info['x_values'] self.curve_data = file_info['curve_data'] # 包含所有曲线,异常曲线值为None # 清除之前的复选框和引用 for widget in self.curve_check_frame.winfo_children(): widget.destroy() self.curve_vars = {} self.curve_checkboxes = {} # 清空复选框引用字典 # 显示加载进度 total_curves = len(self.curve_data) self.status_var.set(f"正在加载 {selected_file} 的 {total_curves} 条曲线...") self.root.update() # 关键修复:创建所有曲线的复选框,不设置数量限制 for i, curve_name in enumerate(self.curve_data.keys()): # 每创建50个复选框更新一次界面,避免假死 if i % 50 == 0 and i > 0: self.status_var.set(f"已加载 {i}/{total_curves} 条曲线...") self.root.update() # 异常曲线默认不勾选 default_state = self.curve_data[curve_name] is not None self.curve_vars[curve_name] = tk.BooleanVar(value=default_state) # 异常曲线显示为灰色 if self.curve_data[curve_name] is None: check = ttk.Checkbutton(self.curve_check_frame, text=curve_name, variable=self.curve_vars[curve_name], state=tk.DISABLED) check.pack(side=tk.LEFT, padx=3, pady=1) check.configure(style="Disabled.TCheckbutton") self.style.configure("Disabled.TCheckbutton", foreground="gray") self.curve_checkboxes[curve_name] = check else: check = ttk.Checkbutton(self.curve_check_frame, text=curve_name, variable=self.curve_vars[curve_name], command=self.plot_chart) check.pack(side=tk.LEFT, padx=3, pady=1) self.curve_checkboxes[curve_name] = check # 确保滚动区域正确更新以显示所有曲线 self.curve_check_frame.update_idletasks() self.canvas_check.configure(scrollregion=self.canvas_check.bbox("all")) # 更新跟踪曲线下拉菜单(只包含有效曲线) valid_curves = [name for name, data in self.curve_data.items() if data is not None] self.track_curve_combobox['values'] = valid_curves self.track_curve_combobox['state'] = "readonly" if valid_curves else "disabled" if valid_curves: self.track_curve_combobox.current(0) self.selected_curve = valid_curves[0] else: self.selected_curve = None # 清除之前的结果和图表 self.clear_results() self.clear_chart() # 填充轴范围的默认值 if self.x_values is not None and len(self.x_values) > 0: self.x_min_var.set(f"{min(self.x_values):.2f}") self.x_max_var.set(f"{max(self.x_values):.2f}") # 设置默认计算范围为全范围 self.calc_start_x.set(f"{min(self.x_values):.2f}") self.calc_end_x.set(f"{max(self.x_values):.2f}") # 计算Y值的默认范围 valid_y_values = [] for y_values in self.curve_data.values(): if y_values is not None: valid_y_values.extend(y_values) if valid_y_values: self.y_min_var.set(f"{min(valid_y_values):.4f}") self.y_max_var.set(f"{max(valid_y_values):.4f}") # 统计异常曲线数量 invalid_curves = sum(1 for data in self.curve_data.values() if data is None) status_text = f"已加载文件: {selected_file},共 {total_curves} 条曲线" if invalid_curves > 0: status_text += f"(其中 {invalid_curves} 条曲线数据异常)" self.status_var.set(status_text) def on_track_curve_selected(self, event): """选择要跟踪的曲线""" self.selected_curve = self.track_curve_combobox.get() if self.ax and self.selected_curve in self.curve_data and self.curve_data[self.selected_curve] is not None: self.set_cursor_behavior() self.status_var.set(f"已选择跟踪曲线: {self.selected_curve}") def select_all_curves(self): """全选所有有效曲线""" valid_curves = [ curve for curve, data in self.curve_data.items() if data is not None and curve in self.curve_checkboxes and self.curve_checkboxes[curve].cget("state") != tk.DISABLED ] # 分批处理,避免界面卡顿 batch_size = 100 for i in range(0, len(valid_curves), batch_size): batch = valid_curves[i:i+batch_size] for curve in batch: self.curve_vars[curve].set(True) self.root.update() self.plot_chart() self.status_var.set(f"已全选 {len(valid_curves)} 条有效曲线") def invert_selection(self): """反选所有有效曲线""" valid_curves = [ curve for curve, data in self.curve_data.items() if data is not None and curve in self.curve_checkboxes and self.curve_checkboxes[curve].cget("state") != tk.DISABLED ] if not valid_curves: if self.show_errors.get(): messagebox.showinfo("提示", "没有可反选的有效曲线") return # 分批处理,避免界面卡顿 batch_size = 100 for i in range(0, len(valid_curves), batch_size): batch = valid_curves[i:i+batch_size] for curve in batch: current_state = self.curve_vars[curve].get() self.curve_vars[curve].set(not current_state) self.root.update() self.plot_chart() self.status_var.set(f"已反选 {len(valid_curves)} 条有效曲线") def filter_curves(self): """根据关键词筛选曲线""" filter_text = self.curve_filter.get().strip().lower() if not filter_text: self.clear_filter() return # 筛选曲线 for curve, check in self.curve_checkboxes.items(): if filter_text in curve.lower(): check.lift() # 显示在前面 else: check.lower() # 隐藏到后面 self.status_var.set(f"已筛选完成,显示包含 '{filter_text}' 的曲线") def clear_filter(self): """清除筛选,显示所有曲线""" self.curve_filter.set("") # 按曲线编号排序显示 sorted_curves = sorted(self.curve_checkboxes.keys(), key=lambda x: int(x.replace("曲线", ""))) for curve in sorted_curves: self.curve_checkboxes[curve].lift() self.status_var.set(f"已显示所有 {len(sorted_curves)} 条曲线") def apply_axis_limits(self): """应用用户设置的坐标轴范围""" if not self.ax: if self.show_errors.get(): messagebox.showwarning("警告", "请先绘制图表") return try: # 应用X轴范围 if self.x_min_var.get() and self.x_max_var.get(): x_min = float(self.x_min_var.get()) x_max = float(self.x_max_var.get()) if x_min < x_max: self.ax.set_xlim(x_min, x_max) else: if self.show_errors.get(): messagebox.showwarning("警告", "X轴起始点必须小于截止点") return # 应用Y轴范围 if self.y_min_var.get() and self.y_max_var.get(): y_min = float(self.y_min_var.get()) y_max = float(self.y_max_var.get()) if y_min < y_max: self.ax.set_ylim(y_min, y_max) else: if self.show_errors.get(): messagebox.showwarning("警告", "Y轴起始点必须小于截止点") return # 更新计算范围的显示 self.update_range_visualization() self.canvas.draw() self.status_var.set("已应用自定义坐标轴范围") except ValueError: if self.show_errors.get(): messagebox.showerror("错误", "请输入有效的数字作为坐标轴范围") except Exception as e: if self.show_errors.get(): messagebox.showerror("错误", f"应用坐标轴范围时出错: {str(e)}") def reset_axis_limits(self): """重置坐标轴范围为自动计算""" if not self.ax: if self.show_errors.get(): messagebox.showwarning("警告", "请先绘制图表") return try: self.ax.relim() self.ax.autoscale_view() x_min, x_max = self.ax.get_xlim() y_min, y_max = self.ax.get_ylim() self.x_min_var.set(f"{x_min:.2f}") self.x_max_var.set(f"{x_max:.2f}") self.y_min_var.set(f"{y_min:.4f}") self.y_max_var.set(f"{y_max:.4f}") # 更新计算范围的显示 self.update_range_visualization() self.canvas.draw() self.status_var.set("已重置坐标轴范围为自动计算") except Exception as e: if self.show_errors.get(): messagebox.showerror("错误", f"重置坐标轴范围时出错: {str(e)}") def update_range_visualization(self): """在图表上可视化显示计算范围""" if not self.ax or self.x_values is None or len(self.x_values) == 0: return try: # 移除之前的范围矩形 if self.range_rect: self.range_rect.remove() self.range_rect = None # 获取计算范围 start_x = float(self.calc_start_x.get()) end_x = float(self.calc_end_x.get()) if start_x >= end_x: return # 获取Y轴范围 y_min, y_max = self.ax.get_ylim() # 创建半透明矩形表示计算范围 self.range_rect = self.ax.fill_between( [start_x, end_x], y_min, y_max, color='gray', alpha=0.2 ) except (ValueError, TypeError): pass def find_x_for_y_in_range(self, x_values, y_values, target_y, start_x, end_x): """在指定X范围内查找目标Y值对应的两个X值""" # 筛选出在计算范围内的数据点 mask = (x_values >= start_x) & (x_values <= end_x) filtered_x = x_values[mask] filtered_y = y_values[mask] if len(filtered_x) < 2: return None, None # 范围内数据点不足 上升段_x = None 下降段_x = None # 遍历筛选后的点对,寻找上升段 for i in range(len(filtered_y) - 1): y1, y2 = filtered_y[i], filtered_y[i + 1] x1, x2 = filtered_x[i], filtered_x[i + 1] if y1 < target_y < y2: 上升段_x = x1 + (target_y - y1) * (x2 - x1) / (y2 - y1) break # 遍历筛选后的点对,寻找下降段 for i in range(len(filtered_y) - 1): y1, y2 = filtered_y[i], filtered_y[i + 1] x1, x2 = filtered_x[i], filtered_x[i + 1] if y1 > target_y > y2: 下降段_x = x1 + (target_y - y1) * (x2 - x1) / (y2 - y1) break return 上升段_x, 下降段_x def calculate(self): """计算当前选中文件在指定范围内的结果""" if not self.current_file or not self.curve_data: if self.show_errors.get(): messagebox.showwarning("警告", "请先加载数据文件") return try: db_value = float(self.db_var.get()) start_x = float(self.calc_start_x.get()) end_x = float(self.calc_end_x.get()) if start_x >= end_x: if self.show_errors.get(): messagebox.showerror("错误", "起始计算点必须小于截止计算点") return except ValueError: if self.show_errors.get(): messagebox.showerror("错误", "请输入有效的数字作为衰减值和计算范围") return self.clear_results() total_curves = len(self.curve_data) self.status_var.set(f"正在计算 {self.current_file}(1/{total_curves})...") self.root.update() for i, (curve_name, y_values) in enumerate(self.curve_data.items()): # 每计算10条曲线更新一次进度 if i % 10 == 0 and i > 0: self.status_var.set(f"正在计算 {self.current_file}({i+1}/{total_curves})...") self.root.update() # 处理异常曲线 if y_values is None: self.result_tree.insert("", tk.END, values=(curve_name, "N/A", "N/A", "N/A", "数据异常", "数据异常", "N/A")) continue # 筛选计算范围内的数据点 mask = (self.x_values >= start_x) & (self.x_values <= end_x) filtered_x = self.x_values[mask] filtered_y = y_values[mask] if len(filtered_x) < 2: self.result_tree.insert("", tk.END, values=(curve_name, "N/A", "N/A", "N/A", "范围数据不足", "范围数据不足", "N/A")) continue # 在计算范围内寻找Y最大值 y_max = max(filtered_y) factor = math.pow(10, db_value / 20) target_y = y_max * factor # 在计算范围内查找目标Y值对应的X值 x1, x2 = self.find_x_for_y_in_range(self.x_values, y_values, target_y, start_x, end_x) f0 = None if x1 is not None and x2 is not None: try: f0 = math.sqrt(x1 * x2) except: f0 = "计算错误" y_max_str = f"{y_max:.4f}" target_y_str = f"{target_y:.4f}" factor_percent = factor * 100 factor_str = f"{factor_percent:.2f}%" x1_str = f"{x1:.4f}" if x1 is not None else "未找到" x2_str = f"{x2:.4f}" if x2 is not None else "未找到" f0_str = f"{f0:.4f}" if f0 and f0 != "计算错误" else f0 if f0 else "未找到" self.result_tree.insert("", tk.END, values=(curve_name, y_max_str, target_y_str, factor_str, x1_str, x2_str, f0_str)) self.results.append({ "文件名": self.current_file, "曲线编号": curve_name, "Y最大值": y_max_str, "目标Y值": target_y_str, "计算系数": factor_str, "上升段X值": x1_str, "下降段X值": x2_str, "F0 (Hz)": f0_str, "计算范围X": f"{start_x:.2f}-{end_x:.2f}" }) # 统计计算结果 valid = sum(1 for data in self.curve_data.values() if data is not None) self.status_var.set(f"计算完成,共 {total_curves} 条曲线({valid} 条有效)") def export_results(self): """导出当前文件的计算结果""" if not self.results: if self.show_errors.get(): messagebox.showwarning("警告", "没有可导出的结果,请先进行计算") return try: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") current_filename = os.path.splitext(self.current_file)[0] default_filename = f"{current_filename}_计算结果_{timestamp}" file_path = filedialog.asksaveasfilename( defaultextension=".xlsx", filetypes=[ ("Excel文件", "*.xlsx"), ("CSV文件", "*.csv"), ("文本文件", "*.txt") ], initialfile=default_filename ) if not file_path: return df = pd.DataFrame(self.results) ext = os.path.splitext(file_path)[1].lower() if ext == '.xlsx': with pd.ExcelWriter(file_path, engine='openpyxl') as writer: df.to_excel(writer, index=False) worksheet = writer.sheets['Sheet1'] if '计算系数' in df.columns: col_idx = df.columns.get_loc('计算系数') + 1 for row in worksheet.iter_rows(min_col=col_idx, max_col=col_idx): for cell in row: if cell.value and '%' in str(cell.value): try: value = float(str(cell.value).replace('%', '')) / 100 cell.value = value cell.number_format = '0.00%' except: continue elif ext == '.csv': df.to_csv(file_path, index=False, encoding='utf-8-sig') elif ext == '.txt': df.to_csv(file_path, index=False, sep='\t', encoding='utf-8-sig') self.status_var.set(f"结果已导出至: {file_path}") if self.show_errors.get(): messagebox.showinfo("成功", f"结果已成功导出至:\n{file_path}") except Exception as e: if self.show_errors.get(): messagebox.showerror("错误", f"导出文件时出错: {str(e)}") self.status_var.set("导出文件失败") def clear_chart(self): """清除当前图表""" if self.canvas: self.canvas.get_tk_widget().destroy() self.fig = None self.canvas = None self.ax = None self.lines = [] self.coord_text = None self.range_rect = None self.cursor_line_x = None self.cursor_line_y = None self.cursor_point = None def clear_results(self): """清除当前结果""" for item in self.result_tree.get_children(): self.result_tree.delete(item) self.results = [] def on_hover(self, event): """处理鼠标悬停事件,使光标锁定在选定的曲线上""" if not self.ax or not event.inaxes or event.inaxes != self.ax: return if not self.selected_curve or self.selected_curve not in self.curve_data or self.curve_data[ self.selected_curve] is None: return # 获取当前选中曲线的数据 x_values = self.x_values y_values = self.curve_data[self.selected_curve] # 找到与鼠标X位置最接近的点 x_mouse = event.xdata idx = np.argmin(np.abs(x_values - x_mouse)) x_nearest = x_values[idx] y_nearest = y_values[idx] # 更新坐标显示 self.coord_display.config(text=f"X={x_nearest:.4f}, Y={y_nearest:.4f}") # 更新坐标文本框 if self.coord_text: self.coord_text.set_text(f'X: {x_nearest:.4f}\nY: {y_nearest:.4f}') self.coord_text.set_position((x_nearest, y_nearest)) # 更新光标线位置 if self.cursor_line_x: self.cursor_line_x.set_xdata([x_nearest, x_nearest]) if self.cursor_line_y: self.cursor_line_y.set_ydata([y_nearest, y_nearest]) # 更新光标点位置 if self.cursor_point: self.cursor_point.set_data([x_nearest], [y_nearest]) self.canvas.draw_idle() def set_cursor_behavior(self): """设置光标行为,使其锁定在选定的曲线上""" if not self.ax or not self.selected_curve or self.selected_curve not in self.curve_data or self.curve_data[ self.selected_curve] is None: return # 移除现有的光标元素 if self.cursor_line_x: self.cursor_line_x.remove() if self.cursor_line_y: self.cursor_line_y.remove() if self.cursor_point: self.cursor_point.remove() # 创建新的光标线(十字线) self.cursor_line_x = self.ax.axvline(color='red', linewidth=1, linestyle='--', alpha=0.7) self.cursor_line_y = self.ax.axhline(color='red', linewidth=1, linestyle='--', alpha=0.7) # 创建光标点 self.cursor_point, = self.ax.plot([], [], 'ro', markersize=6, alpha=0.8) # 创建坐标文本框 if not self.coord_text: self.coord_text = self.ax.text(0.05, 0.95, '', transform=self.ax.transAxes, verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5)) else: self.coord_text.set_text('') # 绑定鼠标移动事件 self.canvas.mpl_connect('motion_notify_event', self.on_hover) def plot_chart(self): """绘制图表""" if not self.current_file or not self.curve_data: if self.show_errors.get(): messagebox.showwarning("警告", "请先加载数据文件") return self.clear_chart() # 创建图表 self.fig, self.ax = plt.subplots(figsize=(15, 9), dpi=100) # 获取要显示的曲线 curves_to_plot = [ curve for curve, var in self.curve_vars.items() if var.get() and self.curve_data[curve] is not None ] if not curves_to_plot and self.show_errors.get(): messagebox.showwarning("警告", "没有可显示的有效曲线,请检查曲线选择") return # 显示绘图进度 total = len(curves_to_plot) self.status_var.set(f"正在绘制图表(1/{total})...") self.root.update() # 定义颜色列表 colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'tab:orange', 'tab:pink', 'tab:purple', 'tab:brown', 'tab:gray', 'tab:olive', 'tab:cyan'] # 绘制每条曲线 for i, curve_name in enumerate(curves_to_plot): # 每绘制20条曲线更新一次进度 if i % 20 == 0 and i > 0: self.status_var.set(f"正在绘制图表({i+1}/{total})...") self.root.update() y_values = self.curve_data[curve_name] color = colors[i % len(colors)] linewidth = 3 if curve_name == self.selected_curve else 2 line, = self.ax.plot(self.x_values, y_values, color=color, linewidth=linewidth, marker='', linestyle='-', label=curve_name) self.lines.append(line) # 设置X轴为对数坐标 self.ax.set_xscale('log') self.ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:.0f}')) self.ax.xaxis.set_minor_formatter(ticker.StrMethodFormatter('{x:.0f}')) # 应用用户设置的轴范围 try: if self.x_min_var.get() and self.x_max_var.get(): x_min = float(self.x_min_var.get()) x_max = float(self.x_max_var.get()) if x_min < x_max: self.ax.set_xlim(x_min, x_max) if self.y_min_var.get() and self.y_max_var.get(): y_min = float(self.y_min_var.get()) y_max = float(self.y_max_var.get()) if y_min < y_max: self.ax.set_ylim(y_min, y_max) except: pass # 显示计算范围 self.update_range_visualization() # 设置轴标签和标题 self.ax.set_xlabel('X值 (对数坐标)', fontsize=9, labelpad=5) self.ax.set_ylabel('Y值', fontsize=9) self.ax.set_title(f'{self.current_file} 的XY曲线关系图', fontsize=10, pad=10) # 添加网格线 self.ax.grid(True, which='both', linestyle='--', alpha=0.7) # 添加图例(曲线数量过多时简化显示) if len(curves_to_plot) <= 20: self.ax.legend(loc='upper right', fontsize=8, bbox_to_anchor=(1.08, 1.15), borderaxespad=0.1) else: self.ax.text(1.02, 1.0, f'共显示 {len(curves_to_plot)} 条曲线', transform=self.ax.transAxes, fontsize=8, verticalalignment='top', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8)) # 优化刻度 self.ax.tick_params(axis='x', rotation=0, pad=5, labelsize=9) # 整体布局优化 plt.subplots_adjust( top=0.9, bottom=0.18, left=0.07, right=0.90 ) # 将图表嵌入Tkinter self.canvas = FigureCanvasTkAgg(self.fig, master=self.chart_frame) canvas_widget = self.canvas.get_tk_widget() canvas_widget.pack(fill=tk.BOTH, expand=True) self.canvas.draw() # 设置光标和交互行为 self.set_cursor_behavior() # 显示图表状态 total_curves = len(self.curve_data) self.status_var.set(f"已绘制图表,共 {total_curves} 条曲线,显示 {len(curves_to_plot)} 条有效曲线") if __name__ == "__main__": root = tk.Tk() app = F0Calculator(root) root.mainloop() 这段代码中,实际导入数据举例比如导入了500个数据,曲线数量正常,“跟踪曲线”数量也正常,但是“图表控制”下的拖动条拖到最后也只显示了453个曲线名称,排查一下哪里的问题
09-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值