From 3ea89a1786e85cc94e1987bb6c2177e1e1972523 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lo=C3=AFc=20Lehnhoff?= <loic.lehnhoff@gmail.com>
Date: Thu, 20 Jul 2023 21:02:25 +0200
Subject: [PATCH] Fixed duplication of x-coordinates (time) Added popup save
 window before closing

---
 .gitignore                                    |   2 +
 args.py                                       |   8 +-
 functions.py                                  |   3 -
 interface.py                                  | 275 +++++++++++-------
 .../__pycache__/__init__.cpython-39.pyc       | Bin 0 -> 164 bytes
 .../__pycache__/line_clicker.cpython-39.pyc   | Bin 0 -> 19117 bytes
 line_clicker/line_clicker.py                  |  27 +-
 outputs/SCW1807_20200713_064554-contours.json |  32 +-
 post_annotation.py                            |  19 +-
 9 files changed, 221 insertions(+), 145 deletions(-)
 create mode 100644 line_clicker/__pycache__/__init__.cpython-39.pyc
 create mode 100644 line_clicker/__pycache__/line_clicker.cpython-39.pyc

diff --git a/.gitignore b/.gitignore
index 1b60786..ba372b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,4 @@
 
 audio_examples/SCW1807_20200713_064545.wav
+line_clicker/__pycache__
+__pycache__
diff --git a/args.py b/args.py
index 32eeed4..36389f1 100644
--- a/args.py
+++ b/args.py
@@ -1,5 +1,6 @@
 ##### IMPORTATIONS #####
 import os
+import sys
 import argparse
 from argparse import RawTextHelpFormatter
 
@@ -36,8 +37,9 @@ def fetch_inputs():
 	parser = argparse.ArgumentParser(
 		formatter_class=RawTextHelpFormatter,
 		description=
-		("This script requires LIBRARIES."
-		"\nAnd it does things, TO BE DESCRIBED.")
+		("This script requires libraries that can be installed with: \n"
+		"'$ pip install -r requirements.txt'"
+		"Read README.md to get an overview of this software.")
 	)
 
 	parser.add_argument(
@@ -122,7 +124,7 @@ def fetch_inputs():
 			f"\nInputError: Max number of contours cannot exceed 50, got {contour}.")
 	except Exception as e:
 		print(e)
-		exit()
+		sys.exit(1)
 
 	return (explore, contour, resampl, outputs, modify_file, from_wav)
 
diff --git a/functions.py b/functions.py
index 669efca..80d4566 100644
--- a/functions.py
+++ b/functions.py
@@ -6,9 +6,6 @@ import numpy as np
 from librosa import load, amplitude_to_db, stft, pcen
 from scipy.signal import resample
 
-from line_clicker.line_clicker import to_curve
-
-
 ##### FUNCTIONS #####
 def save_dict(dictionary, folder, name):
     """
diff --git a/interface.py b/interface.py
index 75e1def..2d982ad 100644
--- a/interface.py
+++ b/interface.py
@@ -82,116 +82,166 @@ class FileExplorer(object):
                     ('All files', '*.*')
                     ))
 
-class App(object):
+
+class Popup(object):
     """
-    A Class to construct an contours annotation tool for audio data.
-    
-    ...
+    A Class that opens a popup asking if the user wants to save its work
+    before leaving.
 
     Parameters
     ----------
-    DIR : str
-        Path to a folder in which the file explorer will be opened.
-    DIR_OUT : str
-        Path to a folder where the contours will be saved.
-    MAX_C : int
-        Maximum number of contours that can be drawn at once.
-    NEW_SR : int
-        Resampling rate of the audio recording.
-    WAVEFILE : str
-        Path to the audio recording to be opened. Should be a '.wav' file.
-    coords_to_modify : dict
-        Coordinates of points (from a previous annotation) that can be used
-        as input to add modifications.
+    options : list
+        The list of options that will show in popup.
+    prompt : str
+        The prompt that will be shown on top of the buttons.
+    """
 
-    Attributes
-    ----------
-    _default_bounds : list of float
-        Boundaries for the matplotlib canvas.
-        (Default value is loaded from 'PARAMETERS.py' file).
-    _default_clipping : int
-        Default clipping value for spectrogram, in dB.
-        (Default value is loaded from 'PARAMETERS.py' file).
-    _default_cmap : str
-        Name of a matplotlib.pyplot color map.
-        (Default value is loaded from 'PARAMETERS.py' file).
-    _default_height : int
-        Default height of the window in which the app will run, in pixels.
-        (Default value is loaded from 'PARAMETERS.py' file).
-    _default_hop_length : int
-        Default hop length for spectrogram, in samples.
-        (Default value is loaded from 'PARAMETERS.py' file).
-    _default_left_panel_width : int
-        Default width for the left panel of the window of the app.
-        (Default value is loaded from 'PARAMETERS.py' file).
-    _default_nfft : int
-        Default fft size for spectrogram, in samples.
-        (Default value is loaded from 'PARAMETERS.py' file).
-    _default_width: int
-        (Default) width of the window in which the app will run, in pixels.
-        (Default value is loaded from 'PARAMETERS.py' file).
-
-    canvas : matplotlib object
-        Interface to include matplotlib plot in tkinter canvas.
-    CHECK_bspline : tkinter int variable
-        User checkbox about plotting curves or not.
-    CLIP_IN : tkinter float variable (Double)
-        User input for clipping value.
-    FFT_IN : tkinter int variable
-        User input for fft.
-    figure, axis, data_showed : matplotlib objects
-        Objects used to show matplotlib.pyplot plot.
-    HOP_IN : tkinter int variable
-        User input for hop length.
-    klicker : mpl_point_clicker instance
-        Adds widgets to matplotlib plot that allows to draw contours.
-    NAME0 : int
-        Number used to name the first contour available for annotation.
-    NAME1 : int
-        Number used to name the last contour available for annotation.
-    OPTIONS : tkinter list variable
-        User listbox to select category item.
-    root : tkinter Tk instance
-        Initialises tkinter interpreter and creates root window.
-    spectrogram : numpy array
-        Spectrogram of the waveform.
-    waveform :  numpy array
-        Waveform of the audio recording.
-
-    Other attributes, buttons and labels have self explenatory names.
+    def __init__(self, options, prompt):
+        self.options = options
+        self.prompt = prompt
+        self.popup_window()  # start function auto
 
-    Methods
-    -------    
-    bspline_activation():
-        Activates/deactivates the visualisation of lines as curves.
-    create_canvas():
-        Creates matplotlib figure to show spectrogram in tkinter canvas.
-    entry_setup():
-        Creates variables that save inputs from the user in the entry fields.
-    get_key_pressed(event):
-        Updates plot and tkinter interface 
-        when a key is pressed to add a new category.
-    layout():
-        Lays the main structure of the tkinter window.
-    link_select(event):
-        Changes the focus to be on a new category, corresponding to
-        the selected item in listbox widget.
-    load_audio():
-        Loads audio data. Waveform and spectrogram.
-    select_file():
-        Opens a file explorer window to select a new wavefile. 
-        Saves contours if a new file is selected.
-        Updates the canvas to show the new spectrogram.
-    setup():
-        Loads default variables to local variables.
-    submit():
-        Loads user inputs to local variables.
-    switch(self)
-        Updates spectrogram displayed to PCEN (and conversely).
-    _frame_listbox_scroll():
-        Just a callable part of layout()
-    _quit():
-        Saves contours and closes the app.
+    def popup_window(self):
+        """
+        Creates the windown and the layout for buttons/text.
+        """
+        self.root = Toplevel()
+        Label(self.root, text=self.prompt).grid(row=0, columnspan=len(self.options))
+
+        for i, option in enumerate(self.options):
+            Button(
+                self.root,
+                text=option, 
+                command=lambda x=option: self.button_pressed(event=x)
+                ).grid(row=1, column=i)
+
+        self.root.mainloop()
+    
+    def button_pressed(self, event):
+        """
+        Saves the button that is pressed to self.pressed.
+        Then shuts down the window.
+
+        Parameters
+        ----------
+        event : str
+            The text from the selected option.
+        """
+        self.pressed = event
+        self.root.quit()
+        self.root.destroy()
+
+
+class App(object):
+    """
+        A Class to construct an contours annotation tool for audio data.
+        
+        ...
+
+        Parameters
+        ----------
+        DIR : str
+            Path to a folder in which the file explorer will be opened.
+        DIR_OUT : str
+            Path to a folder where the contours will be saved.
+        MAX_C : int
+            Maximum number of contours that can be drawn at once.
+        NEW_SR : int
+            Resampling rate of the audio recording.
+        WAVEFILE : str
+            Path to the audio recording to be opened. Should be a '.wav' file.
+        coords_to_modify : dict
+            Coordinates of points (from a previous annotation) that can be used
+            as input to add modifications.
+
+        Attributes
+        ----------
+        _default_bounds : list of float
+            Boundaries for the matplotlib canvas.
+            (Default value is loaded from 'PARAMETERS.py' file).
+        _default_clipping : int
+            Default clipping value for spectrogram, in dB.
+            (Default value is loaded from 'PARAMETERS.py' file).
+        _default_cmap : str
+            Name of a matplotlib.pyplot color map.
+            (Default value is loaded from 'PARAMETERS.py' file).
+        _default_height : int
+            Default height of the window in which the app will run, in pixels.
+            (Default value is loaded from 'PARAMETERS.py' file).
+        _default_hop_length : int
+            Default hop length for spectrogram, in samples.
+            (Default value is loaded from 'PARAMETERS.py' file).
+        _default_left_panel_width : int
+            Default width for the left panel of the window of the app.
+            (Default value is loaded from 'PARAMETERS.py' file).
+        _default_nfft : int
+            Default fft size for spectrogram, in samples.
+            (Default value is loaded from 'PARAMETERS.py' file).
+        _default_width: int
+            (Default) width of the window in which the app will run, in pixels.
+            (Default value is loaded from 'PARAMETERS.py' file).
+
+        canvas : matplotlib object
+            Interface to include matplotlib plot in tkinter canvas.
+        CHECK_bspline : tkinter int variable
+            User checkbox about plotting curves or not.
+        CLIP_IN : tkinter float variable (Double)
+            User input for clipping value.
+        FFT_IN : tkinter int variable
+            User input for fft.
+        figure, axis, data_showed : matplotlib objects
+            Objects used to show matplotlib.pyplot plot.
+        HOP_IN : tkinter int variable
+            User input for hop length.
+        klicker : mpl_point_clicker instance
+            Adds widgets to matplotlib plot that allows to draw contours.
+        NAME0 : int
+            Number used to name the first contour available for annotation.
+        NAME1 : int
+            Number used to name the last contour available for annotation.
+        OPTIONS : tkinter list variable
+            User listbox to select category item.
+        root : tkinter Tk instance
+            Initialises tkinter interpreter and creates root window.
+        spectrogram : numpy array
+            Spectrogram of the waveform.
+        waveform :  numpy array
+            Waveform of the audio recording.
+
+        Other attributes, buttons and labels have self explenatory names.
+
+        Methods
+        -------    
+        bspline_activation():
+            Activates/deactivates the visualisation of lines as curves.
+        create_canvas():
+            Creates matplotlib figure to show spectrogram in tkinter canvas.
+        entry_setup():
+            Creates variables that save inputs from the user in the entry fields.
+        get_key_pressed(event):
+            Updates plot and tkinter interface 
+            when a key is pressed to add a new category.
+        layout():
+            Lays the main structure of the tkinter window.
+        link_select(event):
+            Changes the focus to be on a new category, corresponding to
+            the selected item in listbox widget.
+        load_audio():
+            Loads audio data. Waveform and spectrogram.
+        select_file():
+            Opens a file explorer window to select a new wavefile. 
+            Saves contours if a new file is selected.
+            Updates the canvas to show the new spectrogram.
+        setup():
+            Loads default variables to local variables.
+        submit():
+            Loads user inputs to local variables.
+        switch(self)
+            Updates spectrogram displayed to PCEN (and conversely).
+        _frame_listbox_scroll():
+            Just a callable part of layout()
+        _quit():
+            Saves contours and closes the app.
     """
 
     from parameters import (_default_width, _default_height, _default_hop_length, 
@@ -243,8 +293,21 @@ class App(object):
         self.figure.canvas.mpl_disconnect(self.klicker.key_press) 
         self.root.bind('<Key>', self.get_key_pressed)
 
+        # just to be sure
+        self.root.protocol("WM_DELETE_WINDOW", self.on_close)
+
         self.root.mainloop()
 
+    def on_close(self):
+        save = Popup(prompt="Save and exit?", options=["Yes", "No", "Cancel"]).pressed
+        if save == "Yes":
+            self._quit()
+        if save == "No":
+            self.root.quit()
+            self.root.destroy()
+        else:
+            pass
+
     def bspline_activation(self):
         """
         Activates/deactivates the visualisation of lines as curves.
@@ -554,7 +617,7 @@ class App(object):
                 os.path.basename(self.WAVEFILE)[:-4]+"-contours.json")
             self.WAVEFILE = new_wavefile
 
-            # display loading scree
+            # display loading screen
             self.loading_screen.grid(row=1, column=1, rowspan=14)
             self.canvas.get_tk_widget().destroy()
 
@@ -578,7 +641,7 @@ class App(object):
             self.entry_setup()
             self.layout()
             self.loading_screen.grid_forget()
-
+        
     def setup(self):
         """
         A function to create variables based on default values
diff --git a/line_clicker/__pycache__/__init__.cpython-39.pyc b/line_clicker/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d7f4827996913926ebbfe4cdca728bac46115bac
GIT binary patch
literal 164
zcmYe~<>g{vU|{$lx;z;~KL!!Vn2~{j!GVE+p_qk%fgyz<m_d`#ZzV$!NEku<^3>1B
z&rQ|O$<IvIcS$Ts)OYds3Gndra|?2H)ek65%E?StNXySjNi8bY52$nubJWkt%u9_=
m&dE&9PA$@pkI&4@EQycTE2zB1VUwGmQks)$2eRTb$SD8<?I$Jx

literal 0
HcmV?d00001

diff --git a/line_clicker/__pycache__/line_clicker.cpython-39.pyc b/line_clicker/__pycache__/line_clicker.cpython-39.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..4fdac3d06e156ecb3508baef0ebe5e9db968a698
GIT binary patch
literal 19117
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
D0sxC(

literal 0
HcmV?d00001

diff --git a/line_clicker/line_clicker.py b/line_clicker/line_clicker.py
index 8b68908..687ed06 100644
--- a/line_clicker/line_clicker.py
+++ b/line_clicker/line_clicker.py
@@ -364,8 +364,8 @@ class clicker(object):
 		"""
 		if self.legend_labels[self.current_line] in list(self.coords.keys()):
 			# does this point already exists ?
-			if [x,y] in self.coords[self.legend_labels[self.current_line]]:
-				warnings.warn("This point already exists!", UserWarning, stacklevel=2)
+			if x in np.array(self.coords[self.legend_labels[self.current_line]])[:,0]:
+				warnings.warn("Cannot place two points at the same timestamp!", UserWarning, stacklevel=2)
 			else:
 				# where should it be inserted ?
 				here = np.where(np.array(self.coords[self.legend_labels[self.current_line]])[:,0] > x)[0]
@@ -592,15 +592,20 @@ class clicker(object):
 		if (self.currently_pressed and 
 			event.xdata != None and 
 			event.ydata != None):
-			# update coords
-			current_lines = self.figure_lines[self.current_line].get_data()
-			current_lines[0][self.index] = event.xdata
-			current_lines[1][self.index] = event.ydata
-			self.coords[self.legend_labels[self.current_line]][self.index] = [event.xdata, event.ydata]
-
-			# update plot
-			self.update_lines()
-			self.figure.canvas.draw()
+			
+			# does this point already exists ?
+			if event.xdata in np.array(self.coords[self.legend_labels[self.current_line]])[:,0]:
+				warnings.warn("Cannot place two points at the same timestamp!", UserWarning, stacklevel=2)
+			else:
+				# update coords
+				current_lines = self.figure_lines[self.current_line].get_data()
+				current_lines[0][self.index] = event.xdata
+				current_lines[1][self.index] = event.ydata
+				self.coords[self.legend_labels[self.current_line]][self.index] = [event.xdata, event.ydata]
+
+				# update plot
+				self.update_lines()
+				self.figure.canvas.draw()
 
 	def set_legend(self):
 		"""
diff --git a/outputs/SCW1807_20200713_064554-contours.json b/outputs/SCW1807_20200713_064554-contours.json
index 9ee2ef6..55c8c0c 100644
--- a/outputs/SCW1807_20200713_064554-contours.json
+++ b/outputs/SCW1807_20200713_064554-contours.json
@@ -101,8 +101,8 @@
             9632.580796877977
         ],
         [
-            1.1769355956020835,
-            9171.762083884514
+            1.1794070626717919,
+            9479.66604006841
         ],
         [
             1.2234946472488883,
@@ -187,28 +187,32 @@
             15162.405352799564
         ],
         [
-            1.446383724281465,
-            16496.35425883328
+            1.4413724760470132,
+            16287.542250441644
         ],
         [
-            1.44676171875,
-            17700.91851851852
+            1.4490057835548567,
+            19078.485781877196
         ],
         [
-            1.44676171875,
-            19269.57037037037
+            1.4515679818356721,
+            19196.707818930037
         ],
         [
-            1.4532515624999998,
-            19254.340740740743
+            1.4733184911158894,
+            18661.618655692724
         ],
         [
-            1.4662312499999999,
-            19117.274074074077
+            1.4937878542675378,
+            17396.273242381794
         ],
         [
-            1.4857007812499998,
-            18751.762962962963
+            1.507527807781656,
+            16956.60405592277
+        ],
+        [
+            1.5294099559708072,
+            16899.255901167242
         ]
     ],
     "Line3": [
diff --git a/post_annotation.py b/post_annotation.py
index e0dc130..f3fd29d 100644
--- a/post_annotation.py
+++ b/post_annotation.py
@@ -126,10 +126,13 @@ class Results(object):
 
 		for idx, key in enumerate(list(self.coords.keys())):
 			if mode=="curves":
-				cx, cy = to_curve(
-					np.array(self.coords[key])[:,0],
-					np.array(self.coords[key])[:,1],
-					kind="quadratic")
+				if len(np.array(self.coords[key])[:,0])>2:
+					cx, cy = to_curve(
+						np.array(self.coords[key])[:,0],
+						np.array(self.coords[key])[:,1],
+						kind="quadratic")
+				else:
+					cx, cy = np.array(self.coords[key])[:,0], np.array(self.coords[key])[:,1]
 				ax.plot(cx, cy, color=self.colors[idx%len(self.colors)])
 
 			else:
@@ -150,10 +153,10 @@ class Results(object):
 		fig, ax = self.display_image(img)
 
 		for idx, key in enumerate(list(self.coords.keys())):
-			min_min = (min(np.array(annot_data.coords[key])[:,0]), 
-				min(np.array(annot_data.coords[key])[:,1]))
-			max_max = (max(np.array(annot_data.coords[key])[:,0]), 
-				max(np.array(annot_data.coords[key])[:,1]))
+			min_min = (min(np.array(self.coords[key])[:,0]), 
+				min(np.array(self.coords[key])[:,1]))
+			max_max = (max(np.array(self.coords[key])[:,0]), 
+				max(np.array(self.coords[key])[:,1]))
 
 			min_min = (min_min[0]-tol*min_min[0], min_min[1]-tol*min_min[1])
 			max_max = (max_max[0]+tol*max_max[0], max_max[1]+tol*max_max[1])
-- 
GitLab