Python Tkinter交互式绘图异常:始终绑定首个分析数据集
质谱仪数据处理程序交互异常问题
我开发了一款质谱仪数据处理程序,核心功能如下:
- 展示分析原始数据
- 支持用户点击交互移除异常值,移除后自动重新计算数据、趋势线及统计值
- 提供按钮切换至上一个/下一个分析数据,进行异常值检查
异常现象:序列中的首个分析数据可完美运行,但切换至其他分析数据(如点击“Next”按钮后),尝试移除异常值时,绘图会回退到首个分析数据,且修改的是首个数据集的数据,而非当前查看的数据集——交互逻辑似乎始终与数组中的首个绘图元素绑定(仅查看首个数据时正常)。
交互逻辑说明
1. main.py初始化流程
主程序调用setup_interactive_plot()后启动窗口循环:
from import_raw import get_raw_data from select_sequences import filter_data from interactive_plot import setup_interactive_plot, interactive_update import matplotlib import matplotlib.pyplot as plt import matplotlib.backends.backend_tkagg as tkagg import tkinter as tk # next button def on_next(current_plot_index): current_plot_index = (current_plot_index + 1) % len(filtered_data) interactive_update(filtered_data[current_plot_index], figure, canvas, stats_frame) update_buttons() # previous button def on_previous(current_plot_index): current_plot_index = (current_plot_index - 1) % len(filtered_data) interactive_update(filtered_data[current_plot_index], figure, canvas, stats_frame) update_buttons() # finish button (to do) def on_finish(): window.quit() # replace with call to data reduction script # exit button def on_exit(): window.quit() # remove previous button on first index # replace next with finish on last index def update_buttons(): prev_button.pack_forget() next_button.pack_forget() finish_button.pack_forget() if current_plot_index > 0: prev_button.pack(side=tk.LEFT) if current_plot_index == len(filtered_data) - 1: finish_button.pack(side=tk.RIGHT) else: next_button.pack(side=tk.RIGHT) ### main code below ### matplotlib.use('TkAgg') # forces TkAgg backend to matplotlib (for MacOSX development) # get and filter raw data global filtered_data all_data, sequence_data = get_raw_data() # get all raw data and group into sequences filtered_data = filter_data(all_data, sequence_data) # filter out only the selected sequences # initiate GUI window = tk.Tk() window.title('HeMan - Alphachron Data Reduction') # define and pack main frame main_frame = tk.Frame(window) main_frame.pack(fill=tk.BOTH, expand=True) # define and pack frame for statistics panel on the left stats_frame = tk.Frame(main_frame, borderwidth=2, relief=tk.SUNKEN) stats_frame.pack(side=tk.LEFT, fill=tk.Y) # define and pack frame for data plot and buttons on the right right_frame = tk.Frame(main_frame) right_frame.pack(side=tk.RIGHT, fill=tk.X, expand=True) # define and pack frame for buttons button_frame = tk.Frame(right_frame) button_frame.pack(side=tk.BOTTOM, fill=tk.X) # define and pack frame for data data_frame = tk.Frame(right_frame) data_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True) # initialize and pack the data frame global canvas figure = plt.figure(figsize=(15,8)) canvas = tkagg.FigureCanvasTkAgg(figure, master=data_frame) canvas.draw() canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True) # set current_plot_index current_plot_index = 0 # initialize and pack buttons global prev_button, exit_button, next_button, finish_button button_options = {'width': 10, 'height': 2} exit_button = tk.Button(button_frame, text="Exit", command=lambda: on_exit(), **button_options) prev_button = tk.Button(button_frame, text="Previous", command=lambda: on_previous(current_plot_index), **button_options) next_button = tk.Button(button_frame, text="Next", command=lambda: on_next(current_plot_index), **button_options) finish_button = tk.Button(button_frame, text="Finish", command=lambda: on_finish(), **button_options) prev_button.pack(side=tk.LEFT) exit_button.pack(side=tk.LEFT) next_button.pack(side=tk.RIGHT) finish_button.pack(side=tk.RIGHT) # update the panes update_buttons() setup_interactive_plot(filtered_data[current_plot_index], figure, canvas, stats_frame) # main window loop window.mainloop()
2. interactive_plot.py:初始化交互绘图
setup_interactive_plot()负责生成图形、写入统计面板,并绑定点击交互事件:
def setup_interactive_plot(data_entry, fig, canvas, stats_frame): plot_raw_data(data_entry, fig, canvas) # draw raw data to data frame write_stats_frame(data_entry, stats_frame) # write stats to stats frame # interactivity fig.canvas.mpl_connect('button_press_event', lambda event: on_click(event, data_entry, fig, canvas, stats_frame)) # draw the raw data plots on the data frame def plot_raw_data(data_entry, fig, canvas): update_plot_and_trendlines(data_entry, fig) # generate the figure plt.tight_layout() # Adjust spacing to prevent labels overlapping plt.suptitle(f"He {data_entry.helium_number}: {data_entry.analysis_label}") plt.subplots_adjust(top=0.92) fig.canvas.draw() canvas.draw()
3. 点击交互逻辑
on_click()函数处理点击事件,切换数据点的激活状态,并触发更新:
# what to do when the user clicks on the plot def on_click(event, data_entry, figure, canvas, stats_frame): click_coords = (event.xdata, event.ydata) # click coordinates # check whether the click landed inside a plot and if so, get the plot and closest_index try: mass = event.inaxes.get_title() # get axis title of click plot closest_index = find_closest_index(data_entry.raw_data, click_coords, mass) # closest index to click except AttributeError: return # click outside of bounds, do nothing # toggle the clicked datum between 1 (active) and 0 (inactive) data_entry.data_status[mass].iloc[closest_index] = (data_entry.data_status[mass].iloc[closest_index] + 1) % 2 if mass in ['3 amu', '4 amu']: # if any datum is excluded from 3 amu or 4 amu, also exclude it from the 4/3 Ratio data_entry.data_status['4/3 Ratio'].iloc[closest_index] = (data_entry.data_status['4/3 Ratio'].iloc[closest_index] + 1) % 2 interactive_update(data_entry, figure, canvas, stats_frame)
4. 交互更新函数
interactive_update()负责清除旧图形、绘制新图形并更新统计面板:
# run all interactive update features in one function def interactive_update(data_entry, fig, canvas, stats_frame): fig.clf() plot_raw_data(data_entry, fig, canvas) # draw raw data on data frame canvas write_stats_frame(data_entry, stats_frame) # write stats to stats frame
5. 切换数据逻辑
点击“Next”或“Previous”按钮时,同样调用interactive_update()切换数据集:
# next button def on_next(current_plot_index): current_plot_index = (current_plot_index + 1) % len(filtered_data) interactive_update(filtered_data[current_plot_index], figure, canvas, stats_frame) update_buttons() # previous button def on_previous(current_plot_index): current_plot_index = (current_plot_index - 1) % len(filtered_data) interactive_update(filtered_data[current_plot_index], figure, canvas, stats_frame) update_buttons()
怀疑方向
怀疑是全局变量使用不当等低级错误导致,但目前无法定位具体问题。
内容的提问来源于stack exchange,提问作者ohshitgorillas
相关产品推荐
相关产品推荐

