Это отлично работает для первого анализа в каждой последовательности, но когда я пытаюсь Если удалить данные из любого другого анализа (например, после нажатия кнопки «Далее»), график вернется к первому анализу и удалит данные из этого набора данных, а не из того, над которым я работал. Кажется, я застрял при взаимодействии с первым элементом графика в массиве, а не с тем, который просматриваю (если только я не просматриваю первый).
Вот логика интерактивности :
- main.py вызывает функцию setup_interacive_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()
- в Interactive_plot.py запускает функциюplot_raw_data(), чтобы сгенерировать фигуру, отформатировать ее, поместить на холст tk и записать соответствующую панель статистики. Затем он настраивает интерактивность on_click с помощью команды fig.canvas.mpl_connect().
Код: Выделить всё
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()
- проверяет, попал ли клик в окно графика; если да, то он находит ближайший к клику элемент данных и «деактивирует» его, устанавливая соответствующую запись data_entry.data_status[mass] с 1 на 0 или наоборот.
Код: Выделить всё
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) - завершается вызовом интерактивного_update(), который очищает фигуру, рисует новую с помощьюplot_raw_data() и обновляет фрейм статистики новыми статистическими данными.
Код: Выделить всё
on_click()
Код: Выделить всё
# 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
- interactive_update() также вызывается, когда я нажимаю следующую или предыдущую кнопку, определенную в main.py:
Код: Выделить всё
# 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()
Вот полные сценарии в их нынешнем виде (для удобства чтения я удалил некоторые ненужные функции и утверждения):
- main.py
- import_raw.py
- interactive_plot.py
- plot_data.py
Подробнее здесь: https://stackoverflow.com/questions/784 ... on-tkinter