import os
import tkinter as tk
from sys import exit
from time import time
from tkinter import ttk, messagebox, filedialog
[docs]class SaveData:
"""
The class containing the window for final saving of data.
"""
def __init__(
self, destination_directory, name, video_file, dataframe,
original_dataframe, start_time, counts):
"""
Initialize the underlying data and parameters, and the window.
Parameters
----------
destination_directory : str
The directory to save to.
name : str
The name of the experiment.
video_file : str
The name of the video file.
dataframe : Pandas dataframe
The dataframe containg all data.
original_dataframe : Pandas dataframe
The dataframe containing all data before editing.
start_time : float
The start time of the process.
counts : int
Number of manual editing operations.
Returns
-------
None.
"""
self.dir = destination_directory.replace('/', '\\')
self.name = name.replace('/', '\\')
self.video = video_file
self.df = dataframe
self.odf = original_dataframe
self.st = start_time
self.counts = counts
# Window
self.master = tk.Tk()
self.master.protocol("WM_DELETE_WINDOW", self.close_window)
self.master.title("Save data")
self.master.lift()
self.master.focus_force()
# Variables
self.destination_dir = tk.StringVar(value=destination_directory)
self.file_name = tk.StringVar(value=name)
self.original_var = tk.BooleanVar(value=True)
self.statistics_var = tk.BooleanVar(value=False)
# Inputs
tk.Label(
self.master, text="Additional save options:"
).grid(row=0, column=0, columnspan=2, pady=10, padx=10, sticky='w')
tk.Label(
self.master, text="Save in:"
).grid(row=1, column=0, padx=10, pady=5, sticky='w')
tk.Entry(
self.master, textvariable=self.destination_dir, width=50
).grid(row=1, column=1, padx=10, pady=5)
tk.Button(
self.master, text="Browse...", command=self.select_saving
).grid(row=1, column=2, padx=10, pady=5)
tk.Label(
self.master, text="Save as:"
).grid(row=2, column=0, padx=10, pady=5, sticky='w')
tk.Entry(
self.master, textvariable=self.file_name, width=50
).grid(row=2, column=1, padx=10, pady=5)
tk.Label(
self.master, text="Save original track data"
).grid(row=3, column=0, padx=10, pady=5, sticky='w')
checkbox_original = ttk.Checkbutton(
self.master, variable=self.original_var)
checkbox_original.grid(row=3, column=1, padx=10, pady=5)
tk.Label(
self.master, text="Save editing statistics"
).grid(row=4, column=0, padx=10, pady=5, sticky='w')
checkbox_statistics = ttk.Checkbutton(
self.master, variable=self.statistics_var)
checkbox_statistics.grid(row=4, column=1, padx=10, pady=5)
# Save button
self.save_button = tk.Button(
self.master, text="Proceed", width=70, fg='red', command=self.save)
self.save_button.grid(row=5, column=0, columnspan=3, pady=10)
# Start
self.master.mainloop()
[docs] def select_saving(self):
"""
Select directory to save in.
"""
dir_ = filedialog.askdirectory()
if dir_:
self.destination_dir.set(dir_)
[docs] def parse(self):
"""
Parse and check input parameters.
"""
destination_dir = self.destination_dir.get()
save_as_name = self.file_name.get()
if not os.path.isdir(destination_dir):
messagebox.showerror("Error", "Invalid destination directory!")
return False
if not save_as_name:
messagebox.showerror("Error", "Save as file name is required!")
return False
return True
[docs] def save_df(self):
"""
Save dataframe as csv and loop to next step.
"""
self.df.to_csv(os.path.join(
self.destination_dir.get().replace('/', '\\'), self.file_name.get()
)+'.csv')
if self.original_var.get():
self.save_button.config(text="Saving original track data...")
self.master.after(10, self.save_odf)
elif self.statistics_var.get():
self.save_button.config(text="Saving editing statistics...")
self.master.after(100, self.save_statistics)
else:
self.save_button.config(text="Saving track video file...")
self.master.after(10, self.save_tvf)
[docs] def save_odf(self):
"""
Save original dataframe as csv and loop to next step.
"""
if self.original_var.get():
self.odf.to_csv(os.path.join(
self.destination_dir.get().replace('/', '\\'),
self.file_name.get()
)+'_original.csv')
if self.statistics_var.get():
self.save_button.config(text="Saving editing statistics...")
self.master.after(100, self.save_statistics)
else:
self.save_button.config(text="Saving track video file...")
self.master.after(10, self.save_tvf)
[docs] def save_statistics(self):
"""
Save process statistics and loop to next step.
"""
if self.statistics_var.get():
df_filtered = self.df[self.df['show']==1]
odf_filtered = self.odf.loc[df_filtered.index]
columns = self.df.columns.intersection(self.odf.columns)
mask = df_filtered[columns] != odf_filtered
differences = mask.sum()
accuracy = differences / len(self.df)
with open(
os.path.join(
self.destination_dir.get().replace('/', '\\'),
self.file_name.get()
)+'_assignment_accuracy.txt', 'a'
) as f:
f.write(f"Experiment: {self.name}\n")
f.write(f"Time taken for process: {time()-self.st} seconds\n")
f.write(
f"Time taken for automated process: \
{self.at-self.st} seconds\n")
f.write(
f"Tree accuracy: {accuracy['tree']} \
({differences['tree']}/{len(df_filtered)})\n")
f.write(
f"ID accuracy: {accuracy['id']} \
({differences['id']}/{len(df_filtered)})\n")
f.write(
f"Age assignment accuracy: {accuracy['side']} \
({differences['side']}/{len(df_filtered)})\n")
f.write(
f"Parent accuracy: {accuracy['parent']} \
({differences['parent']}/{len(df_filtered)})\n")
f.write(
f"Start point accuracy: {accuracy['start']} \
({differences['start']}/{len(df_filtered)})\n")
f.write(
f"Number of manual frustule flips: {self.counts[0]})\n")
f.write(f"Number of manual cell flips: {self.counts[1]})\n")
f.write(
f"Number of manual tracklet splits: {self.counts[2]})\n")
f.write(
f"Number of manual tracklet merges: {self.counts[3]})\n")
f.write(
f"Number of manual tracklet moves along tree(s): \
{self.counts[4]})\n")
f.write(
f"Number of manual tracklet swaps: {self.counts[5]})\n")
f.write(
f"Number of ignored detections: \
{len(self.df[self.df['show']==0])}/{len(self.df)})\n")
self.save_button.config(text="Saving track video file...")
self.master.after(10, self.save_tvf)
[docs] def save_tvf(self):
"""
Save track video file and close window (and program).
"""
self.save_metadata_video()
self.master.destroy()
[docs] def save(self):
"""
Core function of the window activated upon button press. Activate the
saving process.
"""
if self.parse():
self.save_button.config(
text="Saving track data...", relief='sunken')
self.df['x_prev'] = self.df.groupby('id')['x'].shift(1)
self.df['y_prev'] = self.df.groupby('id')['y'].shift(1)
self.df['move_distance'] = (
(self.df['x'] - self.df['x_prev'])**2 +
(self.df['y'] - self.df['y_prev'])**2
).pow(0.5)
self.df.drop(['x_prev', 'y_prev'], axis=1, inplace=True)
self.df['move_distance'] = self.df['move_distance'].fillna(0)
self.df['frame_prev'] = self.df.groupby('id')['frame'].shift(1)
self.df['frame_diff'] = self.df['frame'] - self.df['frame_prev']
self.df['norm_distance'] = (
self.df['move_distance'] / self.df['frame_diff'])
self.df['norm_distance'] = self.df['norm_distance'].fillna(0)
self.df.drop(['frame_prev', 'frame_diff'], axis=1, inplace=True)
self.master.after(10, self.save_df)
[docs] def close_window(self):
"""
Confirm abrupt exit.
"""
if messagebox.askyesno(
"Confirm Exit",
"Are you sure you want to exit without saving?"):
self.master.destroy()
self.window_exception()
[docs] def window_exception(self):
"""
Exit immediately.
"""
messagebox.showerror(
"Window closed.",
"Window was closed unexpectedly. The program terminates now.")
exit()