Source code for DiatomTrack.gui.save

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_metadata_video(self): """ Save video and dataframe as metadata in custom track video file. """ if self.video.split('.')[-1] == 'avi': with ( open(self.video, 'rb') as vf, open(os.path.join( self.destination_dir.get(), self.file_name.get() )+'.csv', 'r') as df, open(os.path.join( self.destination_dir.get(), self.file_name.get() )+'.tvf', 'wb') as of ): # Write the video content of.write(vf.read()) of.write(b'\n===TRACK=DATA===\n') of.write(df.read().encode('utf-8')) else: with open(self.video, 'rb') as vf: data = vf.read() video = data.split(b'\n===TRACK=DATA===\n')[0] with ( open(os.path.join( self.destination_dir.get(), self.file_name.get() )+'.tvf', 'wb') as of, open(os.path.join( self.destination_dir.get(), self.file_name.get() )+'.csv', 'r') as df ): of.write(video) of.write(b'\n===TRACK=DATA===\n') of.write(df.read().encode('utf-8'))
[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()