https://github.com/soskuthy/formant_edit
Raw File
Tip revision: 6b278d5c1e2cb971d524b767562e70e5d4176749 authored by soskuthy on 16 August 2015, 20:13:07 UTC
Update README.txt
Tip revision: 6b278d5
formant_check.py
#!/usr/bin/env python
"""
Formant Editor, Version 0.8.2d
Copyright (C) 2014, Marton Soskuthy

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Last modified: 17/05/2015
"""
from __future__ import division
from Tkinter import *
from random import shuffle
from operator import itemgetter
import os
import inspect
import shutil
import codecs
import platform
import subprocess
import cProfile
import pickle
import tkFileDialog
import sys
import pdb
import re
import ast
from time import time
from math import log
from copy import copy


script_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
if script_dir not in sys.path:
    sys.path.append(script_dir)

from custom_python.csv_custom import readCSV, writeCSV
from custom_python import tkSimpleDialog
from custom_snack import tkSnack



class formantMonitor:

    ##################
    #                #
    # INITIALISATION #
    #                #
    ##################
    
    def __init__ (self, master, script_dir):

        # figure out OS

        self.platform = platform.system()
        self.os_release = platform.release()

        # set up directories

        self.master = master
        self.script_dir = script_dir

        # set up status bar parameters

        self.status_bar_location_size = 30
        self.status_fade_time = 10000

        # set up zoom/movement parameters

        self.dpi = 150
        self.zoom_amount = 0.5
        self.scroll_amount = 0.25

        # set up selection variables

        self.selected_boundaries = []
        self.selected_points = []
        self.drag_data = {"x": 0, "y": 0, "item_id": None}
        self.select_anchor_x = -1
        self.select_anchor_y = -1

        # set up display parameters

        self.boundary_colour = "blue"
        self.boundary_selected_colour = "darkblue"
        self.tag_colour = "#%02x%02x%02x" % (255, 0, 0)
        self.tag_selected_colour = "#%02x%02x%02x" % (160, 0, 0)
        self.trajectory_colour = "#29f"
        self.regular_from = (18, 84, 238)
        self.regular_to = (176, 226, 255) 
        self.selected_from = (self.regular_from[0] * 0.6, self.regular_from[1] * 0.6, self.regular_from[2] * 0.6)
        self.selected_to = (self.regular_to[0] * 0.6, self.regular_to[1] * 0.6, self.regular_to[2] * 0.6) 
        self.boundary_text_colour = "#%02x%02x%02x" % self.regular_from
        
        self.boundary_width = 6
        self.trajectory_width = 10
        self.formant_outline_width = 2

        self.formant_line_colour = "red"
        self.formant_line_width = 1

        # set up play parameters
        
        self.play_cursor_width = 2
        self.play_cursor_colour = "red"
        self.play_cursor_delay = 25 # ms
        self.sound = tkSnack.Sound()
        self.play_selection_start_x = -1
        self.play_selection_end_x = -1
        self.play_selection_on = False
        
        # set up formant/spectrogram parameters

        self.analysis_parameter_list = ["spectrogram_window_length",
                                        "spectrogram_max_freq",
                                        "spectrogram_brightness",
                                        "formant_window_length",
                                        "formant_pre_emph",
                                        "formant_max_freq",
                                        "formant_number_of_formants",
                                        "formant_use_number"]
        self.formant_parameters = ["formant_window_length",
                                   "formant_pre_emph",
                                   "formant_max_freq",
                                   "formant_number_of_formants"]
        self.display_parameters = ["spectrogram_window_length",
                                   "spectrogram_max_freq",
                                   "spectrogram_brightness",
                                   "formant_use_number"]
        self.spectrogram_window_length = 0.005
        self.spectrogram_max_freq = 5000
        self.spectrogram_brightness = 0
        self.formant_window_length = 0.025
        self.formant_pre_emph = 50
        self.formant_max_freq = 5000
        self.formant_number_of_measurements = 11
        self.formant_number_of_formants = 5
        self.formant_use_number = 3
        self.formant_minimum_separation = 100

        self.spectrogram_brightening_amount = 50
        
        self.available_formants = 0

        # to avoid problems with key debouncing

        self.afterId = None

        # initialisation parameters

        self.spectrogram_initialised = False
        self.frd = ""
        self.current_csv = ""
        self.filter_expression = ""

        # export parameters

        self.export_format = "measurements"
        self.export_number_of_formants = self.formant_use_number
        
        # set up GUI

        self.current_redrawn_formant = 0
        self.hide_measurements = False
        
        self.maximise()
        self.outer_frame = Frame(master)
        self.outer_frame.pack(fill=BOTH, expand=1,pady=20,padx=20)
        self.createCoords()
        self.createMenu()
        self.createFileList()
        self.createImage()
        self.createFormantBox()
        self.createButtonBox()
        self.createMetadataViewer()

        # read/create config file

        self.readConfig()

        self.prepTempDir()

    def readConfig (self):
        config_path = os.path.join(self.script_dir, "init.cfg")
        if os.path.exists(config_path):
            f = codecs.open(config_path, 'rb')
            parameters = pickle.load(f)
            f.close()
            for parameter in parameters:
                vars(self)[parameter] = parameters[parameter]
            self.configTest()
        else:
            parameters = {"program_folder":self.script_dir, "temporary_folder":os.path.join(self.script_dir, 'tmp'), "praat_path":''}
            self.config(parameters)

    def configWrapper (self):
        self.config({"program_folder":self.program_folder, "temporary_folder":self.temporary_folder, "praat_path":self.praat_path})

    def config (self, parameters):
        new_parameters = ConfigWindow(self.master, title="Configuration settings", parameters=parameters).output
        for parameter in new_parameters:
            vars(self)[parameter] = parameters[parameter]
        self.config_works = self.configTest()
        if self.config_works:
            config_path = os.path.join(self.script_dir, "init.cfg")
            f = codecs.open(config_path, 'wb')
            pickle.dump(parameters, f)
            f.close()
        

    def configTest (self):
        for i in [0,4]:
            self.filemenu.entryconfig(i, state=DISABLED)
        if not os.path.exists(os.path.join(self.program_folder, "praat")):
            self.updateStatusFading("Invalid program folder.")
            return False
        if not os.path.exists(self.temporary_folder):
            self.updateStatusFading("Invalid temporary folder.")
            return False
        if not self.praatRunScript("praat_test.praat", []):
            self.updateStatusFading("Praat unavailable (try changing the preferences).")
            return False
        for i in [0,4]:
            self.filemenu.entryconfig(i, state=NORMAL)
        return True
        
    ##################
    #                #
    #      GUI       #
    #                #
    ##################

        
    def maximise (self):
        w, h = root.winfo_screenwidth(), root.winfo_screenheight()
        self.master.geometry("%dx%d+%d+%d" % (w * 0.9, h * 0.9, w * 0.05, h * 0.05))

    def createMenu (self):
        # items that should only be active when an FRD file is loaded
        self.filemenu_only_when_loaded = [1,2,5]
        self.filemenu_numbers = [0,1,2,4,5,7,8]
        self.menubar = Menu(self.master)
        self.filemenu = Menu(self.menubar, tearoff=0)
        self.filemenu.add_command(label="Open...", command=self.open)
        self.filemenu.add_command(label="Save", command=self.save, state=DISABLED)
        self.filemenu.add_command(label="Save as...", command=self.saveAs, state=DISABLED)
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Import CSV...", command=self.importCSV)
        self.filemenu.add_command(label="Export CSV...", command=self.exportCSV, state=DISABLED)
        self.filemenu.add_separator()
        self.filemenu.add_command(label="Preferences...", command=self.configWrapper)
        self.filemenu.add_command(label="Exit", command=self.master.destroy)
        self.menubar.add_cascade(label="File", menu=self.filemenu)
        self.master.config(menu=self.menubar)

    def createCoords (self):
        self.vertical_main_divider = 0.75
        self.horizontal_main_divider = 0.8

    def createFileList (self):
        self.file_list = Listbox(self.outer_frame, selectmode=SINGLE, exportselection=0)
        self.file_list_scrollbar = Scrollbar(self.outer_frame, orient=VERTICAL)
        self.file_list_scrollbar.place(relx=1,rely=0,relheight=0.45,anchor=NE)
        self.master.update()
        self.file_list.place(relx=self.horizontal_main_divider, rely=0, relwidth=1 - self.horizontal_main_divider - (self.file_list_scrollbar.winfo_width() / self.outer_frame.winfo_width()), relheight=0.45, anchor=NW)
        self.master.update()
        file_list_bottom = self.file_list.winfo_height() / self.outer_frame.winfo_height()
        self.file_list_button_box = Frame(self.outer_frame)
        self.file_list_button_box.place(relx=self.horizontal_main_divider, rely=file_list_bottom, relwidth=1 - self.horizontal_main_divider, relheight=0.1, anchor=NW)
        self.discard_button = Button(self.file_list_button_box, text="Discard", command=self.discard)
        self.filter_button = Button(self.file_list_button_box, text="Filter", command=self.filter)
        self.file_list_progress_label = Label(self.file_list_button_box, text="")
        self.discard_button.pack(side=LEFT)
        self.filter_button.pack(side=LEFT)
        self.file_list_progress_label.pack(side=LEFT)
        self.file_list.bind("<<ListboxSelect>>", self.chooseFile)
        self.file_list.bind("<Button-1>", self.fileListButtonDown)
        self.file_list.bind("<Up>", self.fileListUp)
        self.file_list.bind("<Down>", self.fileListDown)
        self.file_list.bind("a", self.playWhole)
        self.file_list.bind("i", self.playSelection)
        self.file_list.config(yscrollcommand=self.file_list_scrollbar.set)
        self.file_list_scrollbar.config(command=self.file_list.yview)

    def createMetadataViewer (self):
        self.master.update()
        file_list_button_box_bottom = (self.file_list_button_box.winfo_y() + self.file_list_button_box.winfo_height()) / self.outer_frame.winfo_height()
        self.metadata_viewer_box = Frame(self.outer_frame)
        self.metadata_viewer_box.place(relx=1, rely=file_list_button_box_bottom, relheight=1-file_list_button_box_bottom, relwidth=1-self.horizontal_main_divider, anchor=NE)
        self.metadata_viewer = Listbox(self.metadata_viewer_box, state=DISABLED, selectmode=SINGLE, exportselection=0)
        self.metadata_viewer_scrollbar = Scrollbar(self.metadata_viewer_box, orient=VERTICAL)
        self.metadata_viewer_add_button = Button(self.metadata_viewer_box, text="Add", command=self.addMetadataField)
        self.metadata_viewer_remove_button = Button(self.metadata_viewer_box, text="Remove", command=self.removeMetadataField)
        self.metadata_viewer_up_button = Button(self.metadata_viewer_box, text=u"\u2191", command=self.upMetadataField)
        self.metadata_viewer_down_button = Button(self.metadata_viewer_box, text=u"\u2193", command=self.downMetadataField)
        self.metadata_viewer_add_button.place(relx=0,rely=1,anchor=SW)
        self.master.update()
        self.metadata_viewer_remove_button.place(relx=self.metadata_viewer_add_button.winfo_width()/self.metadata_viewer_box.winfo_width(),rely=1,anchor=SW)
        self.master.update()
        self.metadata_viewer_up_button.place(relx=(self.metadata_viewer_add_button.winfo_width()+self.metadata_viewer_remove_button.winfo_width())/self.metadata_viewer_box.winfo_width(),rely=1,anchor=SW)
        self.master.update()
        self.metadata_viewer_down_button.place(relx=(self.metadata_viewer_add_button.winfo_width()+self.metadata_viewer_remove_button.winfo_width()+self.metadata_viewer_up_button.winfo_width())/self.metadata_viewer_box.winfo_width(),rely=1,anchor=SW)
        self.metadata_viewer_scrollbar.place(relx=1,rely=0,relheight=1-(self.metadata_viewer_add_button.winfo_height()/self.metadata_viewer_box.winfo_height()),anchor=NE)
        self.master.update()
        self.metadata_viewer.place(relx=0, rely=0, relwidth=1 - (self.metadata_viewer_scrollbar.winfo_width() / self.metadata_viewer_box.winfo_width()),relheight=1-(self.metadata_viewer_add_button.winfo_height()/self.metadata_viewer_box.winfo_height()),anchor=NW)
        self.metadata_viewer.config(yscrollcommand=self.metadata_viewer_scrollbar.set)
        self.metadata_viewer_scrollbar.config(command=self.metadata_viewer.yview)
        # binding for double click
        self.metadata_viewer.bind("<Double-Button-1>", self.doubleClickMetadataViewer)
        

    def createImage (self):
        self.image_frame = Frame(self.outer_frame)
        self.image_frame.place(relx=0, rely=0, relwidth=self.horizontal_main_divider - 0.025,
                               relheight=self.vertical_main_divider)
        self.waveform = Canvas(self.image_frame)
        self.spectrogram = Canvas(self.image_frame)
        self.status_bar = Label(self.image_frame, anchor=W)
        self.status_bar.place(relx=0, rely=1, relwidth=1, anchor=SW)
        self.master.update()
        top_of_horizontal_scroll = 1 - (self.status_bar.winfo_height() / self.image_frame.winfo_height())
        bottom_of_waveform = top_of_horizontal_scroll * (1/3)
        self.waveform.place(relx=0,rely=0,relwidth=1,relheight=bottom_of_waveform)
        self.spectrogram.place(relx=0,rely=bottom_of_waveform,relwidth=1,relheight=top_of_horizontal_scroll - bottom_of_waveform)

        # binding for jump from file list
    
        self.file_list.bind("<Tab>", self.moveFocusToSpectrogram)

        # create bindings for drag & drop, selection, draw
        
        self.spectrogram.bind("<Key>", self.keyDown)
        #self.spectrogram.bind("<KeyRelease>", self.keyUp)
        self.spectrogram.bind("<B1-Motion>", self.spectrogramButtonMotion)
        self.spectrogram.bind("<ButtonRelease-1>", self.spectrogramButtonUp)
        self.spectrogram.tag_bind("boundary", "<ButtonPress-1>", self.boundaryDown)
        self.spectrogram.tag_bind("boundary", "<ButtonRelease-1>", self.boundaryUp)
        self.spectrogram.tag_bind("boundary", "<B1-Motion>", self.boundaryMotion)
        self.spectrogram.tag_bind("boundary", "<Shift-ButtonPress-1>", self.Pass)
        self.spectrogram.tag_bind("boundary", "<Shift-ButtonRelease-1>", self.Pass)
        self.spectrogram.tag_bind("boundary", "<Shift-B1-Motion>", self.Pass)
        self.spectrogram.tag_bind("formant", "<ButtonPress-1>", self.formantDown)
        self.spectrogram.tag_bind("formant", "<Shift-ButtonPress-1>", self.shiftFormantDown)
        self.spectrogram.tag_bind("formant", "<ButtonRelease-1>", self.formantUp)
        self.spectrogram.tag_bind("formant", "<Shift-ButtonRelease-1>", self.shiftFormantUp)
        self.spectrogram.tag_bind("formant", "<B1-Motion>", self.formantMotion)
        self.spectrogram.tag_bind("formant", "<Shift-B1-Motion>", self.Pass)
        self.spectrogram.tag_bind("formant_label", "<ButtonPress-1>", self.formantDown)
        self.spectrogram.tag_bind("formant_label", "<Shift-ButtonPress-1>", self.shiftFormantDown)
        self.spectrogram.tag_bind("formant_label", "<ButtonRelease-1>", self.formantUp)
        self.spectrogram.tag_bind("formant_label", "<Shift-ButtonRelease-1>", self.shiftFormantUp)
        self.spectrogram.tag_bind("formant_label", "<B1-Motion>", self.formantMotion)
        self.spectrogram.tag_bind("formant_label", "<Shift-B1-Motion>", self.Pass)
        self.spectrogram.bind("<Shift-ButtonPress-1>", self.selectOn)
        self.spectrogram.bind("<Shift-B1-Motion>", self.selectMotion)
        self.spectrogram.bind("<Shift-ButtonRelease-1>", self.selectOff)
        

        # create bindings for zoom & move

        self.spectrogram.bind("h", self.hide)
        self.spectrogram.bind("x", self.hideThirdFormant)
        self.spectrogram.bind("t", self.tag)
        self.spectrogram.bind("<Up>", self.xZoomIn)
        self.spectrogram.bind("<Down>", self.xZoomOut)
        self.spectrogram.bind("n", self.xZoomToSelection)
        self.spectrogram.bind("r", self.refreshMeasurements)
        self.spectrogram.bind("<Left>", self.xScrollLeft)
        self.spectrogram.bind("<Right>", self.xScrollRight)
        self.spectrogram.bind("<Button-1>", self.spectrogramButtonDown)

        # create bindings for brightness

        self.spectrogram.bind("b", self.brightenSpectrogram)
        self.spectrogram.bind("d", self.darkenSpectrogram)

        # create bindings for playback

        self.spectrogram.bind("a", self.playWhole)
        self.spectrogram.bind("<Tab>", self.playCursor)
        self.spectrogram.bind("i", self.playSelection)

        # create bindings for status bar

        self.spectrogram.bind("<Motion>", self.displayLocation)


    def moveFocusToSpectrogram (self, event=None):
        self.spectrogram.focus_set()
        return "break"

    def createFormantBox (self):
        self.formant_box_outer = Frame(self.outer_frame, relief=RIDGE, borderwidth=2)
        
        self.formant_box_outer.place(relx=0, rely=self.vertical_main_divider + (0.01), relwidth=self.horizontal_main_divider * 0.7, relheight=1-self.vertical_main_divider - 0.01)
        self.formant_box = Frame(self.formant_box_outer)
        self.formant_box.place(relx = 0.5, rely=0.5, anchor="c")
        self.label_formant_max_freq = Label(self.formant_box, text="Max. formant (Hz)")
        self.entry_formant_max_freq = Entry(self.formant_box, width=8)
        self.entry_formant_max_freq.insert(0, self.formant_max_freq)
        self.entry_formant_max_freq.bind("<Return>", self.entryMaxFreqSet)
        self.entry_formant_max_freq.bind("<FocusOut>", self.entryMaxFreqSet)
        
        self.label_formant_number_of_formants = Label(self.formant_box, text="No. of expected formants")
        self.entry_formant_number_of_formants = Entry(self.formant_box, width=8)
        self.entry_formant_number_of_formants.insert(0, self.formant_number_of_formants)
        self.entry_formant_number_of_formants.bind("<Return>", self.entryFormantExpNoSet)
        self.entry_formant_number_of_formants.bind("<FocusOut>", self.entryFormantExpNoSet)
        
        self.label_formant_window_length = Label(self.formant_box, text="Window length (s)")
        self.entry_formant_window_length = Entry(self.formant_box, width=8)
        self.entry_formant_window_length.insert(0, self.formant_window_length)
        self.entry_formant_window_length.bind("<Return>", self.entryWindowLengthSet)
        self.entry_formant_window_length.bind("<FocusOut>", self.entryWindowLengthSet)
        
        self.label_formant_pre_emph = Label(self.formant_box, text="Pre-emphasis from (Hz)")
        self.entry_formant_pre_emph = Entry(self.formant_box, width=8)
        self.entry_formant_pre_emph.insert(0, self.formant_pre_emph)
        self.entry_formant_pre_emph.bind("<Return>", self.entryPreEmphSet)
        self.entry_formant_pre_emph.bind("<FocusOut>", self.entryPreEmphSet)
        
        self.label_formant_use_number = Label(self.formant_box, text="No. of measured formants")
        self.entry_formant_use_number = Entry(self.formant_box, width=4)
        self.entry_formant_use_number.insert(0, self.formant_use_number)
        self.entry_formant_use_number.bind("<Return>", self.entryFormantUseNoSet)
        self.entry_formant_use_number.bind("<FocusOut>", self.entryFormantUseNoSet)
        self.label_available_formants = Label(self.formant_box, text="/ " + str(self.available_formants))

        self.label_spectrogram_max_freq = Label(self.formant_box, text="Max. frequency (Hz)")
        self.entry_spectrogram_max_freq = Entry(self.formant_box, width=8)
        self.entry_spectrogram_max_freq.insert(0, self.spectrogram_max_freq)
        self.entry_spectrogram_max_freq.bind("<Return>", self.entrySpecMaxFreqSet)
        self.entry_spectrogram_max_freq.bind("<FocusOut>", self.entrySpecMaxFreqSet)
                
        self.label_spectrogram_window_length = Label(self.formant_box, text="Window length (s)")
        self.entry_spectrogram_window_length = Entry(self.formant_box, width=8)
        self.entry_spectrogram_window_length.insert(0, self.spectrogram_window_length)
        self.entry_spectrogram_window_length.bind("<Return>", self.entrySpecWindowLengthSet)
        self.entry_spectrogram_window_length.bind("<FocusOut>", self.entrySpecWindowLengthSet)
                
        self.label_spectrogram_brightness = Label(self.formant_box, text="Brightness")
        self.entry_spectrogram_brightness = Entry(self.formant_box, width=8)
        self.entry_spectrogram_brightness.insert(0, self.spectrogram_brightness)
        self.entry_spectrogram_brightness.bind("<Return>", self.entrySpecBrightnessSet)
        self.entry_spectrogram_brightness.bind("<FocusOut>", self.entrySpecBrightnessSet)

        self.check_formant_fixed_on = IntVar()
        self.check_formant_fixed = Checkbutton(self.formant_box, text="Keep constant", variable=self.check_formant_fixed_on, command=self.checkFormantFixedMessage)

        self.check_display_fixed_on = IntVar()
        self.check_display_fixed = Checkbutton(self.formant_box, text="Keep constant", variable=self.check_display_fixed_on, command=self.checkDisplayFixedMessage)

        self.label_formant = Label(self.formant_box, text="Formant measurements:")
        self.label_spectrogram = Label(self.formant_box, text="Display settings:")
        self.label_formant.grid(row=0,column=0, columnspan=2)
        self.label_formant_max_freq.grid(row=1, column=0, sticky=E)
        self.entry_formant_max_freq.grid(row=1, column=1, padx=20)
        self.label_formant_number_of_formants.grid(row=2, column=0, sticky=E)
        self.entry_formant_number_of_formants.grid(row=2, column=1, padx=20)
        self.label_formant_window_length.grid(row=3, column=0, sticky=E)
        self.entry_formant_window_length.grid(row=3, column=1, padx=20)
        self.label_formant_pre_emph.grid(row=4, column=0, sticky=E)
        self.entry_formant_pre_emph.grid(row=4, column=1, padx=20)
        self.check_formant_fixed.grid(row=5, column=0, sticky=E)
        

        self.label_spectrogram.grid(row=0,column=2, columnspan=3)
        self.label_spectrogram_max_freq.grid(row=1, column=2, sticky=E, padx=20)
        self.entry_spectrogram_max_freq.grid(row=1, column=3, columnspan=2)
        self.label_spectrogram_window_length.grid(row=2, column=2, sticky=E, padx=20)
        self.entry_spectrogram_window_length.grid(row=2, column=3, columnspan=2)
        self.label_spectrogram_brightness.grid(row=3, column=2, sticky=E, padx=20)
        self.entry_spectrogram_brightness.grid(row=3, column=3, columnspan=2)
        self.label_formant_use_number.grid(row=4, column=2, sticky=E, padx=20)
        self.entry_formant_use_number.grid(row=4, column=3)
        self.label_available_formants.grid(row=4, column=4)
        self.check_display_fixed.grid(row=5, column=2, sticky=E, padx=20)


    def createButtonBox (self):
        self.button_box_outer = Frame(self.outer_frame)
        self.button_box_outer.place(relx=self.horizontal_main_divider * 0.71, rely=self.vertical_main_divider + 0.01, relwidth=self.horizontal_main_divider * 0.28, relheight=1-self.vertical_main_divider - 0.01)
        self.button_box = Frame(self.button_box_outer)
        self.button_box.place(anchor='c', relx=0.5, rely=0.5)
        self.tag_label = Label(self.button_box, text="Tags:")
        self.tags_outer = Frame(self.button_box)
        self.tag_button_1 = Button(self.tags_outer, text="1", command=lambda: self.tag(1))
        self.tag_button_2 = Button(self.tags_outer, text="2", command=lambda: self.tag(2))
        self.tag_button_3 = Button(self.tags_outer, text="3", command=lambda: self.tag(3))
        self.zoom_in_button = Button(self.button_box, text=u"+ (\u2191)", command=self.xZoomIn)
        self.zoom_out_button = Button(self.button_box, text=u"- (\u2193)", command=self.xZoomOut)
        self.zoom_selection_button = Button(self.button_box, text="sel (n)", command=self.xZoomToSelection)
        self.refresh_formant_button = Button(self.button_box, text="Refresh Formants (r)", command=self.refreshMeasurements)
        self.play_label = Label(self.button_box, text="Play...")
        self.play_whole_button = Button(self.button_box, text="all (a)", command=self.playWhole)
        self.play_view_button = Button(self.button_box, text="cur (tab)", command=self.playCursor)
        self.play_selection_button = Button(self.button_box, text="int (i)", command=self.playSelection)
        self.tag_label.grid(row=0,column=0)
        self.tags_outer.grid(row=0,column=1,columnspan=2)
        self.tag_button_1.grid(row=0,column=0)
        self.tag_button_2.grid(row=0,column=1)
        self.tag_button_3.grid(row=0,column=2)
        self.zoom_in_button.grid(row=1,column=0,sticky=E)
        self.zoom_out_button.grid(row=1,column=1)
        self.zoom_selection_button.grid(row=1,column=2,sticky=W)
        self.refresh_formant_button.grid(row=2,column=0,columnspan=3)
        self.play_label.grid(row=3, column=0, columnspan=3)
        self.play_whole_button.grid(row=4,column=0)
        self.play_view_button.grid(row=4,column=1)
        self.play_selection_button.grid(row=4,column=2)

    def entryMaxFreqSet (self, event=None):
        try:
            v = int(self.entry_formant_max_freq.get())
            if v != self.formant_max_freq:
                self.formant_max_freq = int(self.entry_formant_max_freq.get())
                self.updateStatusFading("Maximum frequency for formant estimation set to " + str(self.formant_max_freq) + ".")
                self.refreshMeasurements()
                if event.char != "??":
                    self.moveFocusToSpectrogram()
        except:
            self.entry_formant_max_freq.delete(0, END)
            self.entry_formant_max_freq.insert(0, self.formant_max_freq)
            self.moveFocusToSpectrogram()

    def entrySpecMaxFreqSet (self, event=None):
        try:
            v = int(self.entry_spectrogram_max_freq.get())
            if v > self.current_sample_rate / 2:
                v = int(self.current_sample_rate / 2)
            if v != self.spectrogram_max_freq:
                self.spectrogram_max_freq = v
                self.updateStatusFading("Maximum frequency for spectrogram set to " + str(self.spectrogram_max_freq) + ".")
                self.yzoom_end = self.spectrogram_max_freq
                self.displaySpectrogram()
            self.entry_spectrogram_max_freq.delete(0, END)
            self.entry_spectrogram_max_freq.insert(0, self.spectrogram_max_freq)
            if event.char != "??":
                self.moveFocusToSpectrogram()
        except:
            self.entry_spectrogram_max_freq.delete(0, END)
            self.entry_spectrogram_max_freq.insert(0, self.spectrogram_max_freq)


    def entryFormantExpNoSet (self, event=None):
        try:
            v = float(self.entry_formant_number_of_formants.get())
            v = ((v + 0.25) // 0.5) * 0.5
            if v != self.formant_number_of_formants:
                self.formant_number_of_formants = v
                self.updateStatusFading("Expected number of formants (i.e. LPC order * 0.5) set to " + str(self.formant_number_of_formants) + ".")
                self.refreshMeasurements()
            self.entry_formant_number_of_formants.delete(0, END)
            self.entry_formant_number_of_formants.insert(0, self.formant_number_of_formants)
            if event.char != "??":
                self.moveFocusToSpectrogram()
        except:
            self.entry_formant_number_of_formants.delete(0, END)
            self.entry_formant_number_of_formants.insert(0, self.formant_number_of_formants)

    def entryWindowLengthSet (self, event=None):
        try:
            v = float(self.entry_formant_window_length.get())
            if v != self.formant_window_length:
                self.formant_window_length = v
                self.updateStatusFading("Window length for formant estimation set to " + str(self.formant_window_length) + ".")
                self.refreshMeasurements()
                if event.char != "??":
                    self.moveFocusToSpectrogram()
        except:
            self.entry_formant_window_length.delete(0, END)
            self.entry_formant_window_length.insert(0, self.formant_window_length)

    def entrySpecWindowLengthSet (self, event=None):
        try:
            v = float(self.entry_spectrogram_window_length.get())
            if v!= self.spectrogram_window_length:
                self.spectrogram_window_length = v
                self.updateStatusFading("Window length for spectrogram set to " + str(self.spectrogram_window_length) + ".")
                self.displaySpectrogram()
                if event.char != "??":
                    self.moveFocusToSpectrogram()
        except:
            self.entry_spectrogram_window_length.delete(0, END)
            self.entry_spectrogram_window_length.insert(0, self.spectrogram_window_length)

    def entryPreEmphSet (self, event=None):
        try:
            v = int(self.entry_formant_pre_emph.get())
            if v != self.formant_pre_emph:
                self.formant_pre_emph = v
                self.updateStatusFading("Pre-emphasis for formant estimation set to " + str(self.formant_pre_emph) + ".")
                self.refreshMeasurements()
                if event.char != "??":
                    self.moveFocusToSpectrogram()
        except:
            self.entry_formant_pre_emph.delete(0, END)
            self.entry_formant_pre_emph.insert(0, self.formant_pre_emph)

    def entryFormantUseNoSet (self, event=None):
        try:
            v = int(self.entry_formant_use_number.get())
            if v != self.formant_use_number:
                self.formant_use_number = v
                self.updateStatusFading("Number of formants set to " + str(self.formant_use_number) + ".")
                self.displaySpectrogram()
                if event.char != "??":
                    self.moveFocusToSpectrogram()
                self.clearSelection()
        except:
            self.entry_formant_use_number.delete(0, END)
            self.entry_formant_use_number.insert(0, self.formant_use_number)

    def entrySpecBrightnessSet (self, event=None):
        try:
            b = float(self.entry_spectrogram_brightness.get())
            if b < -100:
                b = -100
                self.entry_spectrogram_brightness.delete(0, END)
                self.entry_spectrogram_brightness.insert(0, b)
            elif b > 100:
                b = 100
                self.entry_spectrogram_brightness.delete(0, END)
                self.entry_spectrogram_brightness.insert(0, b)
            if b != self.spectrogram_brightness:
                self.spectrogram_brightness = b
                self.updateStatusFading("Spectrogram brightness set to " + str(self.spectrogram_brightness) + ".")
                self.displaySpectrogram()
                if event.char != "??":
                    self.moveFocusToSpectrogram()
        except:
            self.entry_spectrogram_brightness.delete(0, END)
            self.entry_spectrogram_brightness.insert(0, self.spectrogram_brightness)

    def refreshSettings (self):
        index = self.db.wav_dic[self.current_wav]
        for parameter in self.analysis_parameter_list:
            if not (parameter in self.formant_parameters and self.check_formant_fixed_on.get()) and not (parameter in self.display_parameters and self.check_display_fixed_on.get()):
                vars(self)[parameter] = self.db.settings_table[index][self.db.settings_header[parameter]]
                vars(self)["entry_" + parameter].delete(0, END)
                vars(self)["entry_" + parameter].insert(0, vars(self)[parameter])

    def checkDisplayFixedMessage (self):
        if self.check_display_fixed_on.get():
            self.updateStatusFading("Same display settings used for opened items. Previous settings overridden.")
        else:
            self.updateStatusFading("Saved display settings are used.")

    def checkFormantFixedMessage (self):
        if self.check_formant_fixed_on.get():
            self.updateStatusFading("Same formant settings used for opened items. WARNING: This overrides saved formant values.")
        else:
            self.updateStatusFading("Saved formant settings are used. No automatic formant reestimation.")

    def resizeImages (self, event):
        """
        Automatically resize images when window size changes
        """
        self.displayWaveform()
        self.displaySpectrogram()
        
    ##################
    #                #
    # FILE HANDLING  #
    #                #
    ##################

    def prepTempDir (self):
        if not os.path.exists(self.temporary_folder):
            try:
                os.makedirs(self.temporary_folder)
            except:
                raise ValueError("Could not create temporary dictionary. Try changing the path.")

    def createSettings (self):
        settings_header = dict([(self.analysis_parameter_list[i], i) for i in range(len(self.analysis_parameter_list))])
        settings = [vars(self)[parameter] for parameter in self.analysis_parameter_list]
        return(settings_header, settings)

    def importCSV (self):
        if self.importCSVDialogues():
            #try:
                self.frd = ""
                csv = readCSV(self.current_csv, delimiter=self.delimiter)
                settings_header, settings = self.createSettings()
                settings_table = [list(settings) for x in range(len(csv[1]))]
                self.db = Database(self.current_wav_column, self.current_master_folder, csv, (settings_header, settings_table), self.current_files)
                self.db.measurements = self.createMeasurements()
                self.onNewCurrent()
                self.updateStatusFading(os.path.split(self.current_csv)[1] + " successfully imported.")
            #except:
            #    self.frd = ""
            #    self.db = None
            #    self.updateStatusFading("Import CSV failed.")

    def exportCSV (self):
        self.chooseFile()
        if self.file_list.size() < 1:
            self.updateStatusFading("Nothing to export.")
            return False
        export_path = tkFileDialog.asksaveasfilename(title="Export CSV file...")
        if not export_path:
            self.updateStatusFading("Export CSV aborted.")
            return False
        formant_export_no = selectFormantNo(self.master, "Number of formants to write out").no
        if self.export_format == "measurements":
            try:
                table = []
                tags_exist = False
                for wav in self.db.wav_list:
                    index = self.db.wav_dic[wav]
                    start = self.db.measurements[index][0][1]
                    end = self.db.measurements[index][0][2]
                    metadata = self.db.metadata_table[index] + [end - start]
                    max_formant = self.db.settings_table[index][self.db.settings_header["formant_use_number"]]
                    for m in range(len(self.db.measurements[index][1][0])):
                        formants = []
                        tags = []
                        for t in range(formant_export_no):
                            if t > len(self.db.measurements[index][1]) - 1:
                                formants.append(None)
                            elif len(self.db.measurements[index][1][t]) < len(self.db.measurements[index][1][0]):
                                formants.append(None)
                            elif t >= max_formant:
                                formants.append(None)
                            else:
                                formants.append(self.db.measurements[index][1][t][m])
                            if (t,m) in self.db.measurements[index][-1]:
                                tags_exist = True
                                tags.append("".join(sorted([str(x) for x in self.db.measurements[index][-1][(t,m)]])))
                            else:
                                tags.append("")
                        table.append(metadata + [m] + formants + tags)
                header = map(itemgetter(0), sorted(self.db.metadata_header.items(), key=itemgetter(1))) + ['duration', 'measurement_no'] + ['f' + str(n) for n in range(1, formant_export_no + 1)]
                if not tags_exist:
                    table = [x[:-formant_export_no] for x in table]
                else:
                    header += ['f' + str(n) + ".tag" for n in range(1, formant_export_no + 1)]
                writeCSV(export_path, header, table)
                self.updateStatusFading("File exported as " + export_path + ".")
            except:
                self.updateStatusFading("Export CSV failed.")
        elif self.export_format == "slices":
            pass
        
            
    def importCSVDialogues (self):
        self.current_csv = tkFileDialog.askopenfilename(title="Import CSV file...")
        if not self.current_csv:
            return False
        self.current_wav_column, self.delimiter, self.formant_number_of_measurements  = selectColumn(self.master, "Import settings").output
        if not self.current_wav_column:
            return False
        self.current_master_folder = os.path.dirname(self.current_csv)
        self.getFileList()
        return True

    def getFileList (self):
        self.current_files = {}
        for dirpath, dirs, files in os.walk(self.current_master_folder):
            for f in files:
                self.current_files[f.lower()] = os.path.join(dirpath, f)


    def openWav (self, wav):

        #try:
            self.spectrogram_initialised = True
            self.clearSelectionList()

            self.current_wav = wav
            self.db.current_wav = wav
            self.current_path = self.current_files[wav]
            index = self.db.wav_dic[wav]
            self.sound.read(self.current_path)
            self.current_sample_rate = self.sound["frequency"]


            self.refreshSettings()

            self.boundaryMeasurementsToBoundaries(*self.db.measurements[index][0])
            self.xzoom_start = 0
            self.xzoom_end = self.current_dur
            self.yzoom_start = 0
            self.yzoom_end = self.spectrogram_max_freq
            self.play_cursor_x = 0
            self.play_selection_start_x = -1
            self.play_selection_end_x = -1
            self.redraw = 0

            self.formantListToTrajectory(self.db.measurements[index][1], self.db.measurements[index][-1])
            self.createPlayCursor()
            self.createPlaySel()

            self.displayWaveform()
            if self.check_formant_fixed_on.get():
                self.refreshMeasurements()
            else:
                self.displaySpectrogram()
            self.exitDrawMode()
            self.image_frame.bind('<Configure>', self.resizeImages)
            self.displayMetadata()
            self.updateStatusFading("Opened " + self.current_wav + " with a sampling rate of " + str(self.current_sample_rate) + ".")
       # except:
       #     self.onNewCurrent(failed_to_open_wav=True)
       #     self.updateStatusFading("WAV file could not be opened. Perhaps does not exist anymore?")
        

    def chooseFile (self, event=None):
        if int(self.file_list.size()) > 0 and len(self.file_list.curselection()) > 0:
            wav = self.file_list.get(self.file_list.curselection())
            if self.spectrogram_initialised:
                self.db.measurements[self.db.wav_dic[self.current_wav]] = [[self.current_dur, self.boundaries[self.left_boundary].ms, self.boundaries[self.right_boundary].ms], self.trajectoryToFormantList(), self.writeTags()]
                for parameter in self.analysis_parameter_list:
                    self.db.settings_table[self.db.wav_dic[self.current_wav]][self.db.settings_header[parameter]] = vars(self)[parameter]
            if wav != self.current_wav:
                self.openWav(wav)
            self.updateProgressLabel()

    def open (self):
        frds = tkFileDialog.askopenfilename(title="Open single/multiple FRD file(s)...", multiple=True)
        if self.platform == 'Windows' and self.os_release=="7": #and 'PROGRAMFILES(X86)' not in os.environ:
            if "{" in frds:
                frds = re.findall("{(.*?)}", frds)
            else:
                frds = frds.split(" ")
        settings_header_message = ""
        try:
            if frds != [""] and frds != "": # works on windows - mac os x?
                self.current_master_folder = os.path.dirname(frds[0])
                self.getFileList()
                for frd_ind in range(len(frds)):
                    frd = frds[frd_ind]
                    print(frd)
                    try:
                        f = codecs.open(frd, 'rb')
                        self.db_temp = pickle.load(f)
                        f.close()
                        settings_header_message = self.db_temp.checkSettingsHeader(*self.createSettings())
                    except:
                        self.updateStatusFading("Could not open " + os.path.split(frd)[1] + ".")
                        raise ValueError ("Could not open " + os.path.split(frd)[1] + ".")
                    if frd_ind == 0:
                        self.db = self.db_temp
                        self.formant_number_of_measurements = len(self.db.measurements[0][1][0])
                    else:
                        try:
                            self.db.join(self.db_temp)
                        except:
                            self.updateStatusFading("Could not add " + os.path.split(frd)[1] + ". Check metadata!")
                            raise ValueError ("Could not add " + os.path.split(frd)[1] + ". Check metadata!")
                if len(frds) == 1:
                    self.frd = frd
                else:
                    self.frd = ""
                self.filter_expression = ""
                self.onNewCurrent()
                self.updateStatusFading("File opened." + settings_header_message)
        except:
            pass
            

    def onNewCurrent (self, failed_to_open_wav=False):
        """
        This method has two main purposes:
        1) cleaning the current GUI when a new FRD is loaded,
           and initialising a fresh GUI
        2) cleaning the current GUI when there are no files
           left in the @file_list
        """
        self.spectrogram_initialised = False
        self.current_wav = None
        if len(self.db.wav_list) < 1:
            self.db.current_wav = None
        else:
            if self.frd:
                self.master.wm_title("Formant Editor - " + os.path.splitext(os.path.split(self.frd)[1])[0])
            elif self.current_csv:
                self.master.wm_title("Formant Editor - " + os.path.splitext(os.path.split(self.current_csv)[1])[0])
            else:
                self.master.wm_title("Formant Editor - Joined FRD files")
        # compatibility:
        if "current_wav" not in vars(self.db):
            self.db.current_wav = None
        self.current_path = None
        self.xzoom_start = None
        self.yzoom_start = None
        self.xzoom_end = None
        self.yzoom_end = None
        self.boundaries = None
        self.trajectories_list = None
        self.trajectories_dic = None
        self.current_dur = None
        self.play_cursor_x = None
        self.play_selection_start_x = -1
        self.play_selection_end_x = -1
        self.spectrogram.delete(ALL)
        self.waveform.delete(ALL)
        self.file_list.delete(0, END)
        self.emptyMetadataViewer()
        self.exitDrawMode()
        self.file_list_progress_label["text"] = ""
        self.populateFileList()
        for i in self.filemenu_only_when_loaded:
            if self.file_list.size() > 0:
                self.filemenu.entryconfig(i, state=NORMAL)
            else:
                self.filemenu.entryconfig(i, state=DISABLED)
        # opening the sound file that was last edited
        if not failed_to_open_wav:
            if self.db.current_wav:
                self.file_list.selection_set(self.db.wav_dic[self.db.current_wav])
                self.file_list.see(self.db.wav_dic[self.db.current_wav])
                self.chooseFile()

    def save (self):
        if not self.frd:
            self.saveAs()
        else:
            self.chooseFile()
            f = codecs.open(self.frd, 'wb')
            pickle.dump(self.db, f)
            f.close()
            self.updateStatusFading("File saved.")

    def saveAs (self):
        self.chooseFile()
        self.frd = tkFileDialog.asksaveasfilename(title="Save FRD file...")
        if self.frd:
            f = codecs.open(self.frd, 'wb')
            pickle.dump(self.db, f)
            f.close()
            self.master.wm_title("Formant Editor - " + os.path.splitext(os.path.split(self.frd)[1])[0])
            self.updateStatusFading("File saved as " + self.frd + ".")

    ##################
    #                #
    #   FILE LIST    #
    #                #
    ##################

    def populateFileList (self):
        if self.filter_expression == "":
            for wav in self.db.wav_list:
                self.file_list.insert(END, wav)
        else:
            try:
                filter_tree = ast.parse(self.filter_expression, mode="eval")
                filter_tree = RewriteName().visit(filter_tree)
                for wav in self.db.wav_list:
                    index = self.db.wav_dic[wav]
                    if eval(compile(filter_tree, "<ast>", mode="eval")):
                        self.file_list.insert(END, wav)
            except:
                self.updateStatusFading("Could not apply filter to the current project. Check filter syntax!")
                self.file_list.delete(0, END)
                for wav in self.db.wav_list:
                    self.file_list.insert(END, wav)

            
    def updateProgressLabel (self):
        try:
            self.file_list_progress_label["text"] = "   " + str(int(self.file_list.curselection()[0]) + 1) + " / " + str(self.file_list.size())
        except:
            pass


    def fileListButtonDown (self, event):
        if self.file_list.cget("state") != DISABLED:
            self.file_list.focus_set()

    def fileListDown (self, event=None):
        i = self.file_list.curselection()
        if len(i) == 1:
            index = int(i[0])
            if index < (int(self.file_list.size()) - 1):
                self.file_list.selection_clear(index)
                self.file_list.selection_set(index + 1)
                self.file_list.see(index + 1)
                self.chooseFile()

    def fileListUp (self, event=None):
        i = self.file_list.curselection()
        if len(i) == 1:
            index = int(i[0])
            if index > 0:
                self.file_list.selection_clear(index)
                self.file_list.selection_set(index - 1)
                self.file_list.see(index - 1)
                self.chooseFile()

    def discard (self):
        if int(self.file_list.size()) > 0:
            wav = self.file_list.get(self.file_list.curselection())
            index = self.db.wav_dic[wav]
            file_list_index = self.file_list.curselection()
            if int(file_list_index[0]) < (int(self.file_list.size()) - 1):
                self.fileListDown()
            elif int(self.file_list.size()) > 1:
                self.fileListUp()
            self.file_list.delete(file_list_index)
            self.db.metadata_table.pop(index)
            self.db.settings_table.pop(index)
            self.db.measurements.pop(index)
            self.db.wav_list.pop(index)
            self.db.refreshWavDic()
            if int(self.file_list.size()) < 1:
                self.onNewCurrent()
            else:
                self.updateProgressLabel()
                
    def filter (self):
        self.filter_expression = selectFilterExpression(self.master, "Filter expression", parameters=self.filter_expression).output
        self.onNewCurrent()
        

    ##################
    #                #
    #     PRAAT      #
    #                #
    ##################

    def praatRunScript (self, script_name, options):
        output_path = self.temporary_folder + os.sep + "praat.tmp"
        script_path = self.program_folder + os.sep + "praat" + os.sep + script_name
        if self.platform == 'Windows':
            cmd_list = [self.praat_path, "-a", script_path] + options
            cmd = ' '.join(['"' + str(item) + '"' for item in cmd_list]) + ">" + '"' + output_path + '"'
        else:
            cmd_list = [self.praat_path, script_path] + options
            cmd = ' '.join([str(item).replace(' ', r'\ ') for item in cmd_list + [">",  output_path]])
        subprocess.call(cmd, shell=True)
        if os.path.exists(output_path):
            f = codecs.open(output_path, 'r')
            text = f.read()
            f.close()
            os.remove(output_path)
            return text
        else:
            return ''

    ##################
    #                #
    #    METADATA    #
    #                #
    ##################

    def emptyMetadataViewer (self):
        self.metadata_viewer.config(state=NORMAL)
        self.metadata_viewer.delete(0, END)
        self.metadata_viewer.config(state=DISABLED)

    def displayMetadata (self):
        self.metadata_viewer.config(state=NORMAL)
        yview = self.metadata_viewer.yview()
        self.metadata_viewer.delete(0, END)
        for attr in map(itemgetter(0), sorted(self.db.metadata_header.items(), key=itemgetter(1))):
            index = self.db.wav_dic[self.current_wav]
            line = attr + ": " + str(self.db.metadata_table[index][self.db.metadata_header[attr]])
            self.metadata_viewer.insert(END, line)
        if self.metadata_viewer.size():
            self.metadata_viewer.yview('moveto', yview[0])
            
    def addMetadataField (self):
        attr_name, default_value = selectAttrName(self.master, "Attribute name").output
        if attr_name:
            if len(self.metadata_viewer.curselection()) > 0:
                sel_attr_name = self.metadata_viewer.get(self.metadata_viewer.curselection()).split(':')[0]
            else:
                sel_attr_name = self.metadata_viewer.get(0).split(':')[0]
            self.db.addAttribute(attr_name, sel_attr_name, default_value=default_value)
            self.updateStatusFading("New metadata attribute added.")
            self.displayMetadata()
            
    def removeMetadataField (self):
        if int(self.metadata_viewer.size()) > 0 and len(self.metadata_viewer.curselection()) > 0:
            attr_name = self.metadata_viewer.get(self.metadata_viewer.curselection()).split(':')[0]
            if attr_name in self.db.metadata_header:
                self.db.removeAttribute(attr_name)
                self.updateStatusFading('Metadata attribute "' + attr_name + '" removed.')
                self.displayMetadata()
    
    def upMetadataField (self):
        if int(self.metadata_viewer.size()) > 0 and len(self.metadata_viewer.curselection()) > 0:
            sel = int(self.metadata_viewer.curselection()[0])
            if sel != 0:
                attr_name = self.metadata_viewer.get(self.metadata_viewer.curselection()).split(':')[0]
                self.db.upAttribute(attr_name)
                self.updateStatusFading('Metadata attribute "' + attr_name + '" moved up.')
                self.displayMetadata()
                self.metadata_viewer.select_set(sel - 1)
                self.metadata_viewer.activate(sel - 1)
    
    def downMetadataField (self):
        if int(self.metadata_viewer.size()) > 0 and len(self.metadata_viewer.curselection()) > 0:
            sel = int(self.metadata_viewer.curselection()[0])
            if sel != (self.metadata_viewer.size() - 1):
                attr_name = self.metadata_viewer.get(self.metadata_viewer.curselection()).split(':')[0]
                self.db.downAttribute(attr_name)
                self.updateStatusFading('Metadata attribute "' + attr_name + '" moved down.')
                self.displayMetadata()
                self.metadata_viewer.select_set(sel + 1)
                self.metadata_viewer.activate(sel + 1)
                
    def doubleClickMetadataViewer (self, event=None):
        if int(self.metadata_viewer.size()) > 0 and len(self.metadata_viewer.curselection()) > 0:
            self.metadata_field_entry = Entry(self.metadata_viewer, borderwidth=0)
            bbox = self.metadata_viewer.bbox(self.metadata_viewer.curselection())
            self.metadata_field_entry.place(x=bbox[0], y=bbox[1]-2, width=self.metadata_viewer.winfo_width()-2, height=bbox[3]+4)
            self.metadata_field_entry.bind("<Return>", self.setMetadataFieldValue)
            self.metadata_field_entry.bind("<FocusOut>", self.setMetadataFieldValue)
            self.metadata_field_entry.insert(0, self.metadata_viewer.get(self.metadata_viewer.curselection()).split(':')[1][1:])
            self.metadata_field_entry_attr_name = self.metadata_viewer.get(self.metadata_viewer.curselection()).split(':')[0]
            self.metadata_field_entry.focus()
            self.file_list.configure(state=DISABLED)
            self.menubar.entryconfig(0, state=DISABLED)
            
    def setMetadataFieldValue (self, event=None):
        new_value = self.metadata_field_entry.get()
        if self.metadata_field_entry_attr_name in self.db.metadata_header:
            self.db.put(self.current_wav, self.metadata_field_entry_attr_name, new_value)
            self.displayMetadata()
        self.metadata_field_entry.destroy()
        self.file_list.configure(state=NORMAL)
        self.menubar.entryconfig(0, state=NORMAL)
        for i in self.filemenu_numbers:
            self.filemenu.entryconfig(i, state=NORMAL) 
        
            


    ##################
    #                #
    #     PLAY       #
    #                #
    ##################

    def play (self, start, end):
        self.movePlayCursor((start, end, time()))
        self.sound.play(start=int(self.current_sample_rate * start), end=int(self.current_sample_rate * end))

    def movePlayCursor (self, (start, end, start_time)):
        new_time = start + (time() - start_time)
        self.placePlayCursor(new_time)
        if new_time < end:
            self.master.after(self.play_cursor_delay, self.movePlayCursor, (start, end, start_time))
        else:
            self.placePlayCursor(self.play_cursor_x)

    def createPlayCursor (self):
        self.spectrogram.delete("play_cursor")
        self.play_cursor = self.spectrogram.create_line(0,0,0,0, fill=self.play_cursor_colour, width=self.play_cursor_width, tags="play_cursor")
        self.play_cursor_text = self.spectrogram.create_text(0,0,text="",tags="play_cursor", anchor=NW,fill=self.play_cursor_colour)

    def createPlaySel (self):
        self.spectrogram.delete("sel_cursor")
        self.play_selection_start = self.spectrogram.create_line(0,0,0,0, fill=self.play_cursor_colour, width=self.play_cursor_width, dash=[10], tags="sel_cursor")
        self.play_selection_end = self.spectrogram.create_line(0,0,0,0, fill=self.play_cursor_colour, width=self.play_cursor_width, dash=[10], tags="sel_cursor")
        self.play_selection_start_text = self.spectrogram.create_text(0,0,text="", tags="sel_cursor",anchor=NW,fill=self.play_cursor_colour)
        self.play_selection_end_text = self.spectrogram.create_text(0,0,text="", tags="sel_cursor",anchor=NE,fill=self.play_cursor_colour)
        self.play_selection_dur_text = self.spectrogram.create_text(0,0,text="", tags="sel_cursor",anchor=N,fill=self.play_cursor_colour)

    def clickPlayCursor (self, event):
        x = self.xzoom_start + ((event.x / self.spectrogram.winfo_width()) * (self.xzoom_end - self.xzoom_start))
        self.play_cursor_x = x
        self.placePlayCursor(x)

    def placePlayCursor (self, x):
        x_pixel = ((x - self.xzoom_start) / (self.xzoom_end - self.xzoom_start)) * self.spectrogram.winfo_width()
        self.spectrogram.coords(self.play_cursor, (x_pixel, 0, x_pixel, self.spectrogram.winfo_height()))
        self.spectrogram.coords(self.play_cursor_text, x_pixel + 3, 5)
        self.spectrogram.itemconfig(self.play_cursor_text, text="%.3f" % x)
        self.spectrogram.lift("play_cursor")

    def placePlaySel (self):
        x_start = ((self.play_selection_start_x - self.xzoom_start) / (self.xzoom_end - self.xzoom_start)) * self.spectrogram.winfo_width()
        x_end = ((self.play_selection_end_x - self.xzoom_start) / (self.xzoom_end - self.xzoom_start)) * self.spectrogram.winfo_width()
        self.spectrogram.coords(self.play_selection_start, (x_start, 0, x_start, self.spectrogram.winfo_height()))
        self.spectrogram.coords(self.play_selection_end, (x_end, 0, x_end, self.spectrogram.winfo_height()))
        self.spectrogram.coords(self.play_selection_start_text, x_start + 3, 5)
        self.spectrogram.itemconfig(self.play_selection_start_text, text="%.3f" % self.play_selection_start_x)
        self.spectrogram.coords(self.play_selection_dur_text, x_start + ((x_end - x_start) / 2), 5)
        self.spectrogram.itemconfig(self.play_selection_dur_text, text="%.3f" % (self.play_selection_end_x - self.play_selection_start_x))
        self.spectrogram.coords(self.play_selection_end_text, x_end - 3, 5)
        self.spectrogram.itemconfig(self.play_selection_end_text, text="%.3f" % self.play_selection_end_x)
        self.spectrogram.lift("sel_cursor")
    
    def playWhole (self, event=None):
        if self.spectrogram_initialised:
            self.play(0, self.current_dur)

    def playCursor (self, event=None):
        if self.spectrogram_initialised:
            if self.play_selection_start_x != -1:
                self.play(self.play_selection_start_x, self.play_selection_end_x)
            else:
                self.play(self.play_cursor_x, self.xzoom_end)
            return 'break'

    def playSelection (self, event=None):
        if self.spectrogram_initialised:
            self.play(self.boundaries[self.left_boundary].ms, self.boundaries[self.right_boundary].ms)
            
    ##################
    #                #
    # IMAGE HANDLING #
    #                #
    ##################

    def displayWaveform (self):
        if self.spectrogram_initialised:
            self.waveform.delete("waveform")
            self.master.update()
            width = self.waveform.winfo_width()
            height = self.waveform.winfo_height()
            pps = width / (self.xzoom_end - self.xzoom_start)
            start = int(self.current_sample_rate * self.xzoom_start)
            end = int(self.current_sample_rate * self.xzoom_end)
            self.waveform_image = tkSnack.createWaveform(self.waveform, 0, 0, pixelspersecond=pps, height=height, tags="waveform", sound=self.sound, start=start, end=end)
    
    def displaySpectrogram (self):
        if self.spectrogram_initialised:
            self.spectrogram.delete("spectrogram")
            self.yzoom_end = self.spectrogram_max_freq
            self.master.update()
            width = self.spectrogram.winfo_width()
            height = self.spectrogram.winfo_height()
            pps = width / (self.xzoom_end - self.xzoom_start)
            start = int(self.current_sample_rate * self.xzoom_start)
            end = int(self.current_sample_rate * self.xzoom_end)
            winlength = int(self.spectrogram_window_length * self.current_sample_rate)
            fftlength = 2 ** (int(log(winlength, 2)) + 1)
            self.spectrogram_image = tkSnack.createSpectrogram(self.spectrogram, 0, 0, pixelspersecond=pps, height=height, tags="spectrogram", sound=self.sound, start=start, end=end, winlength=winlength, fftlength=fftlength, topfrequency=self.spectrogram_max_freq, brightness=self.spectrogram_brightness)
            self.drawMeasurements()
            self.placePlayCursor(self.play_cursor_x)
            self.placePlaySel()

    def darkenSpectrogram (self, event=None):
        if self.spectrogram_brightness >= -100 + self.spectrogram_brightening_amount:
            self.spectrogram_brightness -= self.spectrogram_brightening_amount
            self.entry_spectrogram_brightness.delete(0, END)
            self.entry_spectrogram_brightness.insert(0, str(self.spectrogram_brightness))
            self.displaySpectrogram()
    
    def brightenSpectrogram (self, event=None):
        if self.spectrogram_brightness <= 100 - self.spectrogram_brightening_amount:
            self.spectrogram_brightness += self.spectrogram_brightening_amount
            self.entry_spectrogram_brightness.delete(0, END)
            self.entry_spectrogram_brightness.insert(0, str(self.spectrogram_brightness))
            self.displaySpectrogram()


    ##################
    #                #
    #   STATUS BAR   #
    #                #
    ##################

    def displayLocation (self, event):
        if self.spectrogram_initialised:
            s = self.xzoom_start + (event.x / self.spectrogram.winfo_width()) * (self.xzoom_end - self.xzoom_start)
            hz = self.yzoom_start + (1 - (event.y / self.spectrogram.winfo_height())) * (self.yzoom_end - self.yzoom_start)
            self.updateLocation(s, hz)

    def displayLocationDrag (self, x, y):
        if self.spectrogram_initialised:
            s = self.xzoom_start + (x / self.spectrogram.winfo_width()) * (self.xzoom_end - self.xzoom_start)
            hz = self.yzoom_start + (1 - (y / self.spectrogram.winfo_height())) * (self.yzoom_end - self.yzoom_start)
            self.updateLocation(s, hz)


    def updateLocation (self, s, hz):
        status_text = self.status_bar.cget("text")
        status = status_text[self.status_bar_location_size:]
        location = "%7.3f s, %5d Hz;" % (s, hz)
        location += " " * (self.status_bar_location_size - len(location))
        self.status_bar.config(text=location+status)

    def updateStatus (self, status):
        status_text = self.status_bar.cget("text")
        location = status_text[:self.status_bar_location_size]
        location += " " * (self.status_bar_location_size - len(location))
        self.status_bar.config(text=location+status)

    def updateStatusFading (self, status):
        self.updateStatus(status)
        self.master.after(self.status_fade_time, self.deleteStatus, status)

    def deleteStatus (self, status):
        status_text = self.status_bar.cget("text")
        status_curr = status_text[self.status_bar_location_size:]
        if status_curr == status:
            location = status_text[:self.status_bar_location_size]
            location += " " * (self.status_bar_location_size - len(location))
            self.status_bar.config(text=location)
            

    ##################
    #                #
    #  MEASUREMENTS  #
    #                #
    ##################

    def createMeasurements (self):
        """
        Establishes boundaries and creates formant measurements for a list
        of files (specified in @db, which is a @Database).
        """
        output = []
        c = 0
        maxx = len(self.db.wav_list)
        for w in self.db.wav_list:
            wav = self.current_files[w]
            textgrid = os.path.splitext(wav)[0] + ".TextGrid"
            dur, left, right = self.createBoundaryMeasurements(textgrid)
            formant_list = self.createFormantList(wav, left, right)
            output.append([[dur, left, right], formant_list, {}])
            c += 1
            self.updateStatus(str(c) + " of " + str(maxx) + " files processed.")
            self.master.update()
        return output
    
    def createBoundaryMeasurements (self, textgrid):
        boundaries = [float(x) for x in self.praatRunScript("get_times.praat", [textgrid]).strip().split('\t')]
        if len(boundaries) != 4:
            raise ValueError("Invalid TextGrid.")
        dur = boundaries[-1] - boundaries[0]
        sel_start = boundaries[1]
        sel_end = boundaries[-2]
        return dur, sel_start, sel_end
    
    def boundaryMeasurementsToBoundaries (self, dur, l, r):
        self.spectrogram.delete("boundary")
        left = Boundary(l,
                     self.spectrogram.create_line(0, 0, 0, 0, fill=self.boundary_colour, width=self.boundary_width, tags="boundary"),
                     None)
        right = Boundary(r,
                      self.spectrogram.create_line(0, 0, 0, 0, fill=self.boundary_colour, width=self.boundary_width, tags="boundary"),
                      left.id)
        self.left_boundary_text = self.spectrogram.create_text(0,0,text="",fill=self.boundary_text_colour,tags="boundary",anchor=SE,activefill="red")
        self.right_boundary_text = self.spectrogram.create_text(0,0,text="",fill=self.boundary_text_colour,tags="boundary",anchor=SW,activefill="red")
        self.mid_boundary_text = self.spectrogram.create_text(0,0,text="",fill=self.boundary_text_colour,tags="boundary",anchor=N,activefill="red")
        left.other = right.id
        self.boundaries = {left.id:left, right.id:right}
        self.current_dur = dur
        self.left_boundary = left.id
        self.right_boundary = right.id

    def boundariesToBoundaryMeasurements (self):
        return self.dur, self.boundaries[self.left_boundary].ms, self.boundaries[self.right_boundary].ms
        
    def createFormantList (self, wav, left, right):
        options = [wav, left, right,
                   self.formant_number_of_measurements, self.formant_number_of_formants,
                   self.formant_max_freq, self.formant_window_length,
                   self.formant_pre_emph]
        txt = self.praatRunScript("get_formant_trajectory.praat", options)
        lines = [x.split() for x in txt.strip().split("\n")]
        output = []
        for p in range(len(lines)):
            line = lines[p]
            for f in range(len(line)):
                if p == 0:
                    output.append([])
                if line[f] == "--undefined--":
                    output[f].append(None)
                else:
                    output[f].append(float(line[f]))
        return self.repairFormantMeasurements(output)
    
    def createColours (self):
        self.regular_range = []
        self.selected_range = []
        self.regular_range = ["#%02x%02x%02x" % (self.regular_from[0] + ((self.regular_to[0] - self.regular_from[0]) / (self.formant_use_number - 1)) * i, self.regular_from[1] + ((self.regular_to[1] - self.regular_from[1]) / (self.formant_use_number - 1)) * i, self.regular_from[2] + ((self.regular_to[2] - self.regular_from[2]) / (self.formant_use_number - 1)) * i) for i in range(self.formant_use_number)]
        self.selected_range = ["#%02x%02x%02x" % (self.selected_from[0] + ((self.selected_to[0] - self.selected_from[0]) / (self.formant_use_number - 1)) * i, self.selected_from[1] + ((self.selected_to[1] - self.selected_from[1]) / (self.formant_use_number - 1)) * i, self.selected_from[2] + ((self.selected_to[2] - self.selected_from[2]) / (self.formant_use_number - 1)) * i) for i in range(self.formant_use_number)]
        self.trajectory_fill_colours = []
        self.trajectory_outline_colours = []
        for f in range(len(self.trajectories_list)):
            current_trajectory = self.trajectories_list[f]
            current_trajectory_fill_colours = []
            current_trajectory_outline_colours = []
            for p in current_trajectory:
                if f >= self.formant_use_number:
                    current_trajectory_fill_colours.append("")
                    current_trajectory_outline_colours.append("")
                else:
                    if p in self.selected_points:
                        current_trajectory_fill_colours.append(self.selected_range[f])
                        if self.trajectories_dic[p].tags:
                            current_trajectory_outline_colours.append(self.tag_selected_colour)
                        else:
                            current_trajectory_outline_colours.append(self.regular_range[f])
                    else:
                        current_trajectory_fill_colours.append(self.regular_range[f])
                        if self.trajectories_dic[p].tags:
                            current_trajectory_outline_colours.append(self.tag_colour)
                        else:
                            current_trajectory_outline_colours.append(self.regular_range[f])
            self.trajectory_fill_colours.append(current_trajectory_fill_colours)
            self.trajectory_outline_colours.append(current_trajectory_outline_colours)
    
    def getNumberOfAvailableFormants (self, formant_list):
        if [] in formant_list:
            self.available_formants = formant_list.index([])
        else:
            self.available_formants = len(formant_list)
        if self.available_formants < self.formant_use_number:
            self.formant_use_number = self.available_formants
            self.entry_formant_use_number.delete(0, END)
            self.entry_formant_use_number.insert(0, self.formant_use_number)
        self.label_available_formants["text"] = "/ " + str(self.available_formants)
        
                       
    def formantListToTrajectory (self, formant_list, tags=None):
        self.clearSelectionList()
        self.spectrogram.delete("formant")
        self.trajectories_dic = {}
        self.trajectories_list = []
        self.getNumberOfAvailableFormants(formant_list)
        for f in range(len(formant_list)):
            line = formant_list[f]
            self.trajectories_list.append([])
            for p in range(len(line)):
                idd = self.spectrogram.create_oval(0,0,0,0, fill="", outline="", tags="formant", width=self.formant_outline_width)
                idd_2 = self.spectrogram.create_text(0,0,text="",fill="red",anchor=CENTER, font="Helvetica 8", tags="formant_label")
                self.trajectories_list[f].append(idd)
                self.trajectories_dic[idd] = Point(line[p], idd, f)
                self.trajectories_dic[idd].tag_id = idd_2
        for f in range(len(self.trajectories_list)):
            for p in range(len(self.trajectories_list[f])):
                if f == 0:
                    below = None
                else:
                    below = self.trajectories_list[f - 1][p]
                if f == len(self.trajectories_list) - 1 or not self.trajectories_list[f + 1]:
                    above = None
                else:
                    above = self.trajectories_list[f + 1][p]
                if p == 0:
                    previous = None
                else:
                    previous = self.trajectories_list[f][p - 1]
                if p == len(self.trajectories_list[f]) - 1:
                    following = None
                else:
                    following = self.trajectories_list[f][p + 1]
                self.trajectories_dic[self.trajectories_list[f][p]].below = below
                self.trajectories_dic[self.trajectories_list[f][p]].above = above
                self.trajectories_dic[self.trajectories_list[f][p]].previous = previous
                self.trajectories_dic[self.trajectories_list[f][p]].following = following
                if tags:
                    if (f,p) in tags:
                        self.trajectories_dic[self.trajectories_list[f][p]].tags = tags[(f,p)]
                        tag_text = ""
                        self.spectrogram.itemconfig(self.trajectories_dic[self.trajectories_list[f][p]].tag_id, text=tag_text)

    def trajectoryToFormantList (self):
        """
        Trajectories are the structures used by the program to represent the current
        formant trajectories. They include pointers to graphical objects.
        Formant lists store the same data in a much simpler format without pointers
        to graphical objects.
        """
        output = []
        for trajectory in self.trajectories_list:
            output.append([])
            for measurement in trajectory:
                output[-1].append(self.trajectories_dic[measurement].hz)
        return output

    def repairFormantMeasurements (self, trajectories_list):
        """
        Praat sometimes skips a formant measurement and returns
        --undefined--; this method fills in the gaps left by
        such undefined values
        """
        for t in range(len(trajectories_list)):
            trajectory = trajectories_list[t]
            lastt = None
            nextt = None
            move_on = False
            for p in range(len(trajectory)):
                none_counter = 0
                value = trajectory[p]
                if value == None and p == len(trajectory) - 1:
                    trajectory[p] = lastt
                elif value == None:
                    for p2 in range(p + 1, len(trajectory)):
                        value2 = trajectory[p2]
                        if value2 != None:
                            nextt = value2
                            none_counter = p2 - p
                            break
                        elif p2 == len(trajectory) - 1:
                            if lastt != None:
                                nextt = lastt
                                none_counter = p2 - p
                                break
                            else:
                                trajectories_list[t] = []
                                move_on = True
                                break
                    if not move_on:
                        if lastt == None:
                            lastt = nextt
                        start = lastt
                        incr = (nextt - lastt) / (none_counter + 1)
                        for p2 in range(p, p + none_counter):
                            trajectory[p2] = start + incr * (p2 - p + 1)
                    else:
                        break
                else:
                    lastt = value
                # check if smaller than prev formants
                if t > 0:
                    if len(trajectories_list[t - 1]) > 0:
                        if trajectories_list[t][p] < trajectories_list[t-1][p] + self.formant_minimum_separation:
                            trajectories_list[t][p] = trajectories_list[t-1][p] + self.formant_minimum_separation
        return trajectories_list

    def refreshMeasurements (self, event=None):
        if self.spectrogram_initialised:
            formant_list = self.createFormantList(self.current_path, self.boundaries[self.left_boundary].ms, self.boundaries[self.right_boundary].ms)
            self.formantListToTrajectory(formant_list)
            self.displaySpectrogram()

            
    ##################
    #                #
    #    DRAWING     #
    #                #
    ##################

    def drawMeasurements (self):
        if self.current_redrawn_formant:
            self.exitDrawMode()
        for b in self.boundaries:
            boundary = self.boundaries[b]
            x1 = ((boundary.ms - self.xzoom_start) / (self.xzoom_end - self.xzoom_start)) * self.spectrogram.winfo_width()
            x2 = x1
            y1 = 0
            y2 = self.spectrogram.winfo_height()
            self.spectrogram.coords(boundary.id, x1, y1, x2, y2)
        self.placeBoundaryText()
        self.spectrogram.tag_raise("boundary")
        self.createColours()
        for t in range(len(self.trajectories_list)):
            trajectory = self.trajectories_list[t]
            for p in range(len(trajectory)):
                fill_colour = self.trajectory_fill_colours[t][p]
                outline_colour = self.trajectory_outline_colours[t][p]
                point = self.trajectories_dic[trajectory[p]]
                #self.spectrogram.itemconfig(point.id, outline=outline_colour)
                self.spectrogram.itemconfig(point.id, fill=fill_colour)
                self.spectrogram.itemconfig(self.trajectories_dic[point.id].tag_id, text=self.createTagText(point.id))    
                x_time = self.boundaries[self.left_boundary].ms + p * ((self.boundaries[self.right_boundary].ms - self.boundaries[self.left_boundary].ms) / (len(trajectory) - 1))
                x1 = (((x_time - self.xzoom_start) / (self.xzoom_end - self.xzoom_start)) * self.spectrogram.winfo_width()) - (self.trajectory_width / 2)
                x2 = x1 + self.trajectory_width
                y1 = ((1 - ((point.hz - self.yzoom_start) / (self.yzoom_end - self.yzoom_start))) * self.spectrogram.winfo_height()) - (self.trajectory_width / 2)
                y2 = y1 + self.trajectory_width
                self.spectrogram.coords(point.id, x1, y1, x2, y2)
                self.spectrogram.coords(self.trajectories_dic[point.id].tag_id, (x1 + x2)/2, (y1 + y2)/2)
                self.spectrogram.lift(point.id)
                self.spectrogram.lift(self.trajectories_dic[point.id].tag_id)
                if t >= self.formant_use_number:
                    self.spectrogram.itemconfig(point.id, state=HIDDEN)
                    self.spectrogram.itemconfig(self.trajectories_dic[point.id].tag_id, state=HIDDEN)
                else:
                    self.spectrogram.itemconfig(point.id, state=NORMAL)
                    self.spectrogram.itemconfig(self.trajectories_dic[point.id].tag_id, state=NORMAL)
                    

    def placeBoundaryText (self):
        yloc = self.spectrogram.winfo_height() - 5
        x_left = self.spectrogram.coords(self.left_boundary)[0]
        x_right = self.spectrogram.coords(self.right_boundary)[0]
        ms_left = self.boundaries[self.left_boundary].ms
        ms_right = self.boundaries[self.right_boundary].ms
        self.spectrogram.itemconfig(self.left_boundary_text, text="%.3f" % ms_left)
        self.spectrogram.coords(self.left_boundary_text, x_left - 5, yloc)
        self.spectrogram.itemconfig(self.right_boundary_text, text="%.3f" % ms_right)
        self.spectrogram.coords(self.right_boundary_text, x_right + 5, yloc)
        self.spectrogram.itemconfig(self.mid_boundary_text, text="dur=%.3f" % (ms_right - ms_left))
        self.spectrogram.coords(self.mid_boundary_text, x_left + ((x_right - x_left) / 2), 5)

    def hide (self, event=None):
        if self.hide_measurements:
            self.hideOff()
        else:
            self.hideOn()
    
    def hideOn (self):
        self.hide_measurements = True
        self.spectrogram.itemconfig("boundary", state=HIDDEN)
        self.spectrogram.itemconfig("formant", state=HIDDEN)

    def hideOff (self):
        self.hide_measurements = False
        self.spectrogram.itemconfig("boundary", state=NORMAL)
        self.spectrogram.itemconfig("formant", state=NORMAL)
        

    def hideThirdFormant (self, event=None):
        if self.formant_use_number > 2:
            self.formant_use_number = 2
            self.entry_formant_use_number.delete(0, END)
            self.entry_formant_use_number.insert(0, str(self.formant_use_number))
            self.displaySpectrogram()

    ##################
    #                #
    # SEL,FOCUS,DRAW #
    #                #
    ##################

    def spectrogramButtonDown (self, event=None):
        if self.spectrogram_initialised:
            self.spectrogram.focus_set()
            closest = self.spectrogram.find_overlapping(event.x - 3, event.y - 3, event.x + 3, event.y + 3)
            if not self.current_redrawn_formant:
                for item in list(closest):
                    if item in self.boundaries or item in self.trajectories_dic:
                        return
                self.clearSelection()
            if self.current_redrawn_formant and not self.hide_measurements:
                self.redraw = self.current_redrawn_formant
                self.drag_data["x"] = event.x
                self.drag_data["y"] = event.y
            elif not self.current_redrawn_formant:
                self.clickPlayCursor(event)
                self.play_selection_start_x = -1
                self.play_selection_end_x = -1
                self.play_selection_on = True

    def clearSelectionList (self, event=None):
        self.selected_points = []
        
        

    def clearSelection (self, event=None):
        self.clearSelectionList()
        self.drawMeasurements()
        
    def selectOn (self, event=None):
        if self.spectrogram_initialised:
            self.spectrogram.focus_set()
            if not self.current_redrawn_formant:
                self.mouse_has_moved = False
                self.select_anchor_x = event.x
                self.select_anchor_y = event.y
                self.selection_box = self.spectrogram.create_rectangle(event.x, event.y, event.x, event.y, fill="", outline="darkgreen", width=self.play_cursor_width)
            

    def selectMotion (self, event=None):
        if self.spectrogram_initialised and self.select_anchor_x != -1:
            self.mouse_has_moved = True
            self.spectrogram.coords(self.selection_box, self.select_anchor_x, self.select_anchor_y, event.x, event.y)

    def selectOff (self, event=None):
        if self.spectrogram_initialised and not self.current_redrawn_formant and self.mouse_has_moved:
            inside_rectangle = self.spectrogram.find_overlapping(self.select_anchor_x, self.select_anchor_y, event.x, event.y)
            for item in list(inside_rectangle):
                if item in self.trajectories_dic:
                    if item not in self.selected_points:
                        self.selected_points.append(item)
            self.drawMeasurements()
        self.spectrogram.delete(self.selection_box)
        self.selection_box = -1
        self.select_anchor_x = -1
        self.select_anchor_y = -1
            
            

    ##################
    #                #
    # REDRAW,PLAYSEL #
    #                #
    ##################

    # this is a bit complicated -- to avoid issues with key debouncing


    def keyDown (self, event):
        print event.keysym
        if event.keysym in map(str, range(1, self.formant_use_number + 1)) and not self.play_selection_on:
            # in case another formant is already being redrawn...
            if self.current_redrawn_formant:
                if self.current_redrawn_formant != int(event.keysym):
                    self.exitDrawMode()
                    self.drawMeasurements()
                    self.current_redrawn_formant = int(event.keysym)
                    self.circlesToLines(self.current_redrawn_formant)
                else:
                    self.exitDrawMode()
                    self.drawMeasurements()
            else:
                self.clearSelection()
                self.current_redrawn_formant = int(event.keysym)
                self.circlesToLines(self.current_redrawn_formant)
        else:
            self.exitDrawMode()
            self.drawMeasurements()
    # old code using key hold instead of key press

            #if self.platform == 'Windows':
            #    if not self.current_redrawn_formant:
            #       self.current_redrawn_formant = int(event.keysym)
            #       self.circlesToLines(self.current_redrawn_formant)
            #else:
            #    if self.afterId:
            #        self.master.after_cancel(self.afterId)
            #        self.afterId = None
            #    else:
            #        self.current_redrawn_formant = int(event.keysym)
            #        self.circlesToLines(self.current_redrawn_formant)
                    
    #def keyUp (self, event):
    #    if event.keysym in map(str, range(1, self.formant_use_number + 1)) and not self.play_selection_on:
            #if self.current_redrawn_formant:
            #    if self.platform == 'Windows':
            #        self.exitDrawMode(event.keysym)
            #    else:
            #        self.afterId = self.master.after(300, self.exitDrawMode, event.keysym)

    def exitDrawMode (self):
        if self.current_redrawn_formant:
            self.deleteFormantLine()            
            self.redraw = 0
            self.current_redrawn_formant = 0
            #self.afterId = None
            
            
            
    def circlesToLines (self, formant_no):
        self.formant_line_coordinates = []
        for point in self.trajectories_list[formant_no - 1]:
            xy = self.spectrogram.coords(point)
            x = xy[0] + (self.trajectory_width / 2)
            y = xy[1] + (self.trajectory_width / 2)
            self.formant_line_coordinates.append((x,y))
            self.spectrogram.itemconfig(point, state=HIDDEN)
            self.spectrogram.itemconfig(self.trajectories_dic[point].tag_id, state=HIDDEN)
        self.formant_line = self.spectrogram.create_line(self.formant_line_coordinates, fill=self.formant_line_colour, width=self.formant_line_width)

    def deleteFormantLine (self):
        self.spectrogram.delete(self.formant_line)
        self.formant_line_coordinates = []
        #for point in self.trajectories_list[self.current_redrawn_formant - 1]:
        #    self.spectrogram.itemconfig(point, state=NORMAL)
        #    self.spectrogram.itemconfig(self.trajectories_dic[point].tag_id, state=NORMAL)
        #    self.spectrogram.lift(point)
        #    self.spectrogram.lift(self.trajectories_dic[point].tag_id)
        

    def updateFormantLine (self, point_no):
        xy = self.spectrogram.coords(self.trajectories_list[self.current_redrawn_formant - 1][point_no])
        x = xy[0] + (self.trajectory_width / 2)
        y = xy[1] + (self.trajectory_width / 2)
        self.formant_line_coordinates[point_no] = (x, y)
        c = []
        map(c.extend, self.formant_line_coordinates)
        self.spectrogram.coords(self.formant_line, *c)

    def spectrogramButtonMotion (self, event):
        if self.spectrogram_initialised:
            if self.select_anchor_x != -1:
                self.selectMotion(event)
                return
            if self.redraw and (self.redraw - 1) < len(self.trajectories_list):
                self.displayLocation(event)
                trajectory = self.trajectories_list[self.redraw - 1]
                for p in range(len(trajectory)):
                    point_id = trajectory[p]
                    point_x = self.spectrogram.coords(point_id)[0] + (self.trajectory_width / 2)
                    if (event.x > point_x and self.drag_data["x"] <= point_x) or (event.x < point_x and self.drag_data["x"] >= point_x):
                        interpolated_y = self.drag_data["y"] + ((point_x - self.drag_data["x"]) / (event.x - self.drag_data["x"])) * (event.y - self.drag_data["y"])
                        below_id = self.trajectories_dic[point_id].below
                        above_id = self.trajectories_dic[point_id].above
                        if above_id == None:
                            above = 0
                        else:
                            above = self.spectrogram.coords(above_id)[3]
                        if below_id == None:
                            below = self.spectrogram.winfo_height()
                        else:
                            below = self.spectrogram.coords(below_id)[1]
                        if (interpolated_y + (self.trajectory_width / 2) < below) and (interpolated_y - (self.trajectory_width / 2) > above):
                            self.spectrogram.coords(point_id, (point_x - (self.trajectory_width / 2), interpolated_y - (self.trajectory_width / 2), point_x + (self.trajectory_width / 2), interpolated_y + (self.trajectory_width / 2)))
                            self.updateFormantLine(p)
                            new_hz = self.yzoom_start + (1 - (interpolated_y / self.spectrogram.winfo_height())) * (self.yzoom_end - self.yzoom_start)
                            self.trajectories_dic[point_id].hz = new_hz
                self.drag_data["x"] = event.x
                self.drag_data["y"] = event.y
            elif self.play_selection_on:
                self.displayLocation(event)
                if event.x >= 0 and event.x <= self.spectrogram.winfo_width():
                    self.placePlayCursor(-1)
                    cursor_x = self.xzoom_start + ((event.x / self.spectrogram.winfo_width()) * (self.xzoom_end - self.xzoom_start))
                    if cursor_x >= self.play_cursor_x:
                        self.play_selection_start_x = self.play_cursor_x
                        self.play_selection_end_x = cursor_x
                    else:
                        self.play_selection_start_x = cursor_x
                        self.play_selection_end_x = self.play_cursor_x
                    self.placePlaySel()

    def spectrogramButtonUp (self, event):
        if self.spectrogram_initialised:
            if self.select_anchor_x != -1:
                self.selectOff(event)
                return
            self.drag_data["x"] = 0
            self.drag_data["y"] = 0
            #if self.redraw: 
            #    self.redraw = 0
            #    self.drawMeasurements()
            self.play_selection_on = False
            if self.play_selection_start_x == self.play_selection_end_x:
                self.play_selection_start_x = -1
                self.play_selection_end_x = -1
                self.placePlaySel()
                self.placePlayCursor(self.play_cursor_x)
            else:
                self.play_cursor_x = -1
            

    def tag (self, tag_number, event=None):
        if self.selected_points:
            print sum([self.trajectories_dic[i].tags for i in self.selected_points], [])
            tagged = sum([self.trajectories_dic[i].tags for i in self.selected_points], []).count(tag_number)
            for i in self.selected_points:
                if tagged < len(self.selected_points):
                    if tag_number not in self.trajectories_dic[i].tags:
                        self.trajectories_dic[i].tags.append(tag_number)
                else:
                    if tag_number in self.trajectories_dic[i].tags:
                        self.trajectories_dic[i].tags.remove(tag_number)
            self.drawMeasurements()
            
    def createTagText (self, point_id):
        return "".join([str(x) for x in sorted(self.trajectories_dic[point_id].tags)])

    def writeTags (self):
        output = {}
        for f in range(len(self.trajectories_list)):
            trajectory = self.trajectories_list[f]
            for p in range(len(trajectory)):
                point = trajectory[p]
                if self.trajectories_dic[point].tags:
                    output[(f,p)] = self.trajectories_dic[point].tags
        return output

    ##################
    #                #
    #  DRAG & DROP   #
    #                #
    ##################

    def boundaryDown (self, event):
        self.boundary_has_changed = False
        closest = self.spectrogram.find_overlapping(event.x - 3, event.y - 3, event.x + 3, event.y + 3)
        for item in list(closest):
            if item in self.boundaries:
                if not self.current_redrawn_formant:
                    self.drag_data["item_id"] = item
                    self.drag_data["x"] = event.x
                    self.drag_data["y"] = event.y
                    return

    def boundaryUp (self, event):
        self.drag_data["item_id"] = None
        self.drag_data["x"] = 0
        self.drag_data["y"] = 0
        if self.boundary_has_changed:
            self.refreshMeasurements()

    def boundaryMotion (self, event):
        delta_x = event.x - self.drag_data["x"]
        if not self.current_redrawn_formant and self.drag_data["item_id"]:
            self.boundary_has_changed = True
            xmin = self.spectrogram.coords(self.drag_data["item_id"])[0]
            xmax = self.spectrogram.coords(self.drag_data["item_id"])[0]
            if not (xmin + delta_x < self.boundary_width or xmax + delta_x > self.spectrogram.winfo_width() - self.boundary_width):
                move = True
                old_x = self.spectrogram.coords(self.drag_data["item_id"])[0]
                new_x = old_x + delta_x
                other_x = self.spectrogram.coords(self.boundaries[self.drag_data["item_id"]].other)[0]
                if (old_x - other_x) / abs(old_x - other_x) != (new_x - other_x) / abs(new_x - other_x):
                    move = False
                if move: 
                    self.spectrogram.move(self.drag_data["item_id"], delta_x, 0)
                    canvas_x = self.spectrogram.coords(self.drag_data["item_id"])[0]
                    new_ms = self.xzoom_start + (canvas_x / self.spectrogram.winfo_width()) * (self.xzoom_end - self.xzoom_start)
                    self.boundaries[self.drag_data["item_id"]].ms = new_ms
                display_x = self.spectrogram.coords(self.drag_data["item_id"])[0]
                display_y = event.y
                self.displayLocationDrag(display_x, display_y)
                self.placeBoundaryText()
        self.drag_data["x"] = event.x
        self.drag_data["y"] = event.y

    def formantDown (self, event):
        closest = self.spectrogram.find_overlapping(event.x - 3, event.y - 3, event.x + 3, event.y + 3)
        print closest
        for item in list(closest):
            if item in self.trajectories_dic:
                if not self.current_redrawn_formant:
                    if item not in self.selected_points:
                        self.clearSelection()
                        self.current_dragged_point = [item]
                    else:
                        self.current_dragged_point = []
                    self.drag_data["item_id"] = item
                    self.drag_data["x"] = event.x
                    self.drag_data["y"] = event.y
    
    def shiftFormantDown (self, event):
        closest = self.spectrogram.find_overlapping(event.x - 3, event.y - 3, event.x + 3, event.y + 3)
        for item in list(closest):
            if item in self.trajectories_dic:
                if not self.current_redrawn_formant:
                    if item not in self.selected_points:
                        self.selected_points.append(item)
                    else:
                        self.selected_points.remove(item)
                    self.drawMeasurements()
                    return


    def formantUp (self, event):
        self.drag_data["item_id"] = None
        self.drag_data["x"] = 0
        self.drag_data["y"] = 0
        self.current_dragged_point = -1
            
    def shiftFormantUp (self, event):
        pass

    def formantMotion (self, event):
        delta_x = event.x - self.drag_data["x"]
        delta_y = event.y - self.drag_data["y"]
        if not self.current_redrawn_formant:
            ymin = self.spectrogram.coords((self.selected_points + self.current_dragged_point)[0])[1]
            ymax = self.spectrogram.coords((self.selected_points + self.current_dragged_point)[0])[1]
            for point_id in list(set(self.selected_points + self.current_dragged_point)):
                y_bottom = self.spectrogram.coords(point_id)[1]
                y_top = self.spectrogram.coords(point_id)[3]
                if y_bottom < ymin:
                    ymin = y_bottom
                elif y_top > ymax:
                    ymax = y_top
            temp_selected_points = self.selected_points[:] + self.current_dragged_point[:]
            selected_columns = []
            while len(temp_selected_points) > 0:
                point_id = temp_selected_points[0]
                selected_columns.append([point_id])
                temp_selected_points.remove(point_id)
                while True:
                    above = self.trajectories_dic[selected_columns[-1][-1]].above
                    if above in temp_selected_points:
                        selected_columns[-1].append(above)
                        temp_selected_points.remove(above)
                    else:
                        break
                while True:
                    below = self.trajectories_dic[selected_columns[-1][-1]].below
                    if below in temp_selected_points:
                        selected_columns[-1].insert(0, below)
                        temp_selected_points.remove(below)
                    else:
                        break
            for column in selected_columns:
                if delta_y > 0:
                    counter = 0
                    step = 1
                else:
                    counter = len(column) - 1
                    step = -1
                while counter > -1 and counter < len(column):
                    point_id = column[counter]
                    counter += step
                    above_id = self.trajectories_dic[point_id].above
                    below_id = self.trajectories_dic[point_id].below
                    current = self.spectrogram.coords(point_id)[3] + delta_y - (self.trajectory_width / 2)
                    if above_id == None:
                        above = 0
                    else:
                        above = self.spectrogram.coords(above_id)[3]
                    if below_id == None:
                        below = self.spectrogram.winfo_height()
                    else:
                        below = self.spectrogram.coords(below_id)[1]
                    if (current + (self.trajectory_width / 2) < below) and (current - (self.trajectory_width / 2) > above):
                        self.spectrogram.move(point_id, 0, delta_y)
                        self.spectrogram.move(self.trajectories_dic[point_id].tag_id, 0, delta_y)
                        canvas_y = self.spectrogram.coords(point_id)[1] + (self.trajectory_width / 2)
                        new_hz = self.yzoom_start + (1 - (canvas_y / self.spectrogram.winfo_height())) * (self.yzoom_end - self.yzoom_start)
                        self.trajectories_dic[point_id].hz = new_hz
            display_x = self.spectrogram.coords(self.drag_data["item_id"])[0] + (self.trajectory_width / 2)
            display_y = self.spectrogram.coords(self.drag_data["item_id"])[1] + (self.trajectory_width / 2)
            self.displayLocationDrag(display_x, display_y)
        self.drag_data["x"] = event.x
        self.drag_data["y"] = event.y
    
    def Pass (self, event):
        pass

    ##################
    #                #
    #  ZOOM & MOVE   #
    #                #
    ##################


    def xZoomIn (self, event=None):
        if self.spectrogram_initialised:
            width = self.xzoom_end - self.xzoom_start
            self.xzoom_start += width * (self.zoom_amount / 2)
            self.xzoom_end -= width * (self.zoom_amount / 2)
            self.displayWaveform()
            self.displaySpectrogram()

    def xZoomOut (self, event=None):
        if self.spectrogram_initialised:
            width = self.xzoom_end - self.xzoom_start
            zoom_incr = width * ((self.zoom_amount / 2) / (1 - self.zoom_amount))
            if self.xzoom_start - zoom_incr < 0:
                self.xzoom_start = 0
            else:
                self.xzoom_start -= zoom_incr
            if self.xzoom_end + zoom_incr > self.current_dur:
                self.xzoom_end = self.current_dur
            else:
                self.xzoom_end += zoom_incr
            self.displayWaveform()
            self.displaySpectrogram()

    def xScrollLeft (self, event=None):
        if self.spectrogram_initialised:
            width = self.xzoom_end - self.xzoom_start
            scroll_amount = width * self.scroll_amount
            if self.xzoom_start - scroll_amount < 0:
                self.xzoom_end = self.xzoom_end - (self.xzoom_start - 0)
                self.xzoom_start = 0
            else: 
                self.xzoom_start = self.xzoom_start - scroll_amount
                self.xzoom_end = self.xzoom_end - scroll_amount
            self.displayWaveform()
            self.displaySpectrogram()

    def xScrollRight (self, event=None):
        if self.spectrogram_initialised:
            width = self.xzoom_end - self.xzoom_start
            scroll_amount = width * self.scroll_amount
            if self.xzoom_end + scroll_amount > self.current_dur:
                self.xzoom_start = self.xzoom_start + (self.current_dur - self.xzoom_end)
                self.xzoom_end = self.current_dur
            else: 
                self.xzoom_start = self.xzoom_start + scroll_amount
                self.xzoom_end = self.xzoom_end + scroll_amount
            self.displayWaveform()
            self.displaySpectrogram()

    def xZoomToSelection (self, event=None):
        if self.spectrogram_initialised:
            if self.play_selection_start_x != -1:
                self.xzoom_start = self.play_selection_start_x
                self.xzoom_end = self.play_selection_end_x
            else:
                self.xzoom_start = self.boundaries[self.left_boundary].ms
                self.xzoom_end = self.boundaries[self.right_boundary].ms
            self.displayWaveform()
            self.displaySpectrogram()

class Boundary:

    def __init__ (self,
                  ms,
                  idd,
                  other=None):
        self.ms = ms
        self.id = idd
        self.other = other
        
class Point:

    def __init__ (self,
                  hz,
                  idd,
                  formant,
                  previous=None,
                  following=None,
                  above=None,
                  below=None):
        self.hz = hz
        self.id = idd
        self.formant = formant
        self.previous = previous
        self.following = following
        self.above = above
        self.below = below
        self.tags = []

class Database:
    
    """
    Class for easily accessing and storing metadata, formant tracker & spectrogram settings
    and measurements for a list of sound files. There are three central lists:
    @metadata_table, @settings_table and @measurements; a sound file is fully described by
    a single item from each of these lists (with the same index). The indices are linked
    to the sound file names through @wav_dic. @wav_list is useful for enumerating all the
    wav files.
    """
    
    def __init__ (self, wav_column, master_folder, metadata, settings, files, measurements=None):
        self.wav_dic = {}
        self.wav_list = []
        self.current_wav = None
        self.metadata_header = metadata[0]
        if wav_column not in self.metadata_header:
            raise ValueError ("WAV column not found in CSV file.")
        self.metadata_table = metadata[1]
        self.settings_header = settings[0]
        self.settings_table = settings[1]
        for r in range(len(self.metadata_table)):
            row = self.metadata_table[r]
            wav = row[self.metadata_header[wav_column]].lower()
            if wav in files:
                self.wav_dic[wav] = r
                self.wav_list.append(wav)
            else:
                raise ValueError("Unable to locate " + wav + ".")
        self.measurements = measurements
    
    def checkSettingsHeader (self, settings_header, settings):
        if self.settings_header != settings_header:
            self.settings_header = settings_header
            self.settings_table = [list(settings) for x in range(len(self.wav_list))]
            return(" Settings changed due to compatibility issues. Measurements remain the same.")
        return("") 
    
    def addAttribute (self, attr_name, sel_attr_name, default_value=""):
        k = self.metadata_header[sel_attr_name]
        for attr in self.metadata_header:
            if self.metadata_header[attr] >= k:
                self.metadata_header[attr] += 1
        self.metadata_header[attr_name] = k
        for row in self.metadata_table:
            row.insert(k, default_value)
    
    def removeAttribute (self, attr_name):
        k = self.metadata_header[attr_name]
        for attr in self.metadata_header:
            if self.metadata_header[attr] > k:
                self.metadata_header[attr] -= 1
        for row in self.metadata_table:
            row.pop(k)
        del self.metadata_header[attr_name]
        
    def upAttribute (self, attr_name):
        k = self.metadata_header[attr_name]
        if k > 0:
            prev_attr_name = self.metadata_header.items()[map(itemgetter(1), self.metadata_header.items()).index(k-1)][0]
            self.metadata_header[attr_name] = k - 1
            self.metadata_header[prev_attr_name] = k
        for row in self.metadata_table:
            k_val = row[k]
            row[k] = row[k-1]
            row[k-1] = k_val
            
    def downAttribute (self, attr_name):
        k = self.metadata_header[attr_name]
        if k < max(self.metadata_header.values()):
            foll_attr_name = self.metadata_header.items()[map(itemgetter(1), self.metadata_header.items()).index(k+1)][0]
            self.metadata_header[attr_name] = k + 1
            self.metadata_header[foll_attr_name] = k
        for row in self.metadata_table:
            k_val = row[k]
            row[k] = row[k+1]
            row[k+1] = k_val

    def refreshWavDic (self):
        self.wav_dic = {}
        for w in range(len(self.wav_list)):
            wav = self.wav_list[w]
            self.wav_dic[wav] = w

    def fullPath (self, wav):
        return self.file[wav]

    def get (self, wav, attr):
        if attr in self.metadata_header:
            return self.metadata_table[self.wav_dic[wav]][self.metadata_header[attr]]
        elif attr in self.settings_header:
            return self.settings_table[self.wav_dic[wav]][self.settings_header[attr]]
        else:
            raise ValueError ("Attribute " + attr + " does not exist.")

    def put (self, wav, attr, value):
        if attr in self.metadata_header:
            self.metadata_table[self.wav_dic[wav]][self.metadata_header[attr]] = value
        elif attr in self.settings_header:
            self.settings_table[self.wav_dic[wav]][self.settings_header[attr]] = value
        else:
            raise ValueError ("Attribute " + attr + " does not exist.")
        
    def join (self, db):
        if type(self)!=type(db):
            raise ValueError("You can only join Database objects.")
        if sorted(self.metadata_header.keys()) != sorted(db.metadata_header.keys()):
            raise ValueError("Metadata headers are different.")
        if len(set(self.wav_list) & set(db.wav_list)) > 0:
            raise ValueError ("WAV file occurs twice in joined database.")
        if map(itemgetter(1), sorted(self.metadata_header.items(),key=itemgetter(0))) != map(itemgetter(1), sorted(db.metadata_header.items(),key=itemgetter(0))):
            for i in range(len(db.metadata_table)):
                row = copy(db.metadata_table[i])
                for colname in db.metadata_header.keys():
                    db.metadata_table[i][self.metadata_header[colname]] = row[db.metadata_header[colname]]
        self.wav_list += db.wav_list
        self.refreshWavDic()
        self.measurements += db.measurements
        self.settings_table += db.settings_table
        self.metadata_table += db.metadata_table
            
    


class selectColumn (tkSimpleDialog.Dialog):

    def body(self, master):

        Label(master, text="Name of WAV column:").grid(row=0)
        Label(master, text="Delimiter:").grid(row=1)
        Label(master, text="Number of measurement points:").grid(row=2)
        
        self.output = ("", "\\t", 11)
        self.e1 = Entry(master)
        self.e1.insert(0, self.output[0])
        self.e2 = Entry(master)
        self.e2.insert(0, self.output[1])
        self.e3 = Entry(master)
        self.e3.insert(0, str(self.output[2]))

        self.e1.grid(row=0, column=1)
        self.e2.grid(row=1, column=1)
        self.e3.grid(row=2, column=1)
        return self.e1 # initial focus

    def apply(self):
        self.output = (self.e1.get(), self.e2.get().decode('string_escape'), int(self.e3.get()))

class selectFormantNo (tkSimpleDialog.Dialog):

    def body(self, master):

        Label(master, text="Max. no. of formants:").grid(row=0)
        Label(master, text="(note that formants hidden by the user are not written out)").grid(row=1, columnspan=2)
        
        self.no = 3
        self.e1 = Entry(master)
        self.e1.insert(0, self.no)

        self.e1.grid(row=0, column=1)
        return self.e1 # initial focus

    def apply(self):
        self.no = int(self.e1.get())

class selectAttrName (tkSimpleDialog.Dialog):

    def body(self, master):

        Label(master, text="Attribute name:").grid(row=0)
        Label(master, text="Default value:").grid(row=1)
        
        self.e1 = Entry(master)
        self.e2 = Entry(master)
        

        self.e1.grid(row=0, column=1)
        self.e2.grid(row=1, column=1)
        return self.e1 # initial focus

    def apply(self):
        self.output = (self.e1.get(), self.e2.get())

class selectFilterExpression (tkSimpleDialog.Dialog):

    def body(self, master):
        
        #master.columnconfigure(0, weight=1)
        #master.rowconfigure(0, weight=1)
        
        Label(master, text="Filter expression:").pack(side=LEFT)
        
        self.e1 = Entry(master)
        self.e1.insert(0, self.parameters)
        
        self.e1.pack(fill=X, expand=1)
        return self.e1 # initial focus

    def apply(self):
        self.output = self.e1.get()


class ConfigWindow (tkSimpleDialog.Dialog):

    def body(self, master):

        Label(master, text="Program folder:", anchor=E).grid(row=0)
        Label(master, text="Temporary folder:", anchor=E).grid(row=1)
        Label(master, text="Praat path:", anchor=E).grid(row=2)

        self.output = self.parameters
        self.program_folder_entry = Entry(master)
        self.program_folder_entry.insert(0, self.parameters["program_folder"])
        self.temporary_folder_entry = Entry(master)
        self.temporary_folder_entry.insert(0, self.parameters["temporary_folder"])
        self.praat_path_entry = Entry(master)
        self.praat_path_entry.insert(0, self.parameters["praat_path"])
        self.program_folder_browse = Button(master, text="Browse", command=self.programFolderBrowse)
        self.temporary_folder_browse = Button(master, text="Browse", command=self.temporaryFolderBrowse)
        self.praat_path_browse = Button(master, text="Browse", command=self.praatPathBrowse)
        self.program_folder_entry.grid(row=0, column=1)
        self.temporary_folder_entry.grid(row=1, column=1)
        self.praat_path_entry.grid(row=2, column=1)
        Label(master, text="  ", anchor=E).grid(row=0,column=2)
        Label(master, text="  ", anchor=E).grid(row=1,column=2)
        Label(master, text="  ", anchor=E).grid(row=2,column=2)
        self.program_folder_browse.grid(row=0, column=3)
        self.temporary_folder_browse.grid(row=1, column=3)
        self.praat_path_browse.grid(row=2, column=3)

        return self.program_folder_entry

    def programFolderBrowse (self):
        out = tkFileDialog.askdirectory(title="Set program folder...")
        if out:
            self.program_folder_entry.delete(0, END)
            self.program_folder_entry.insert(0, out)

    def temporaryFolderBrowse (self):
        out = tkFileDialog.askdirectory(title="Set temporary folder...")
        if out:
            self.temporary_folder_entry.delete(0, END)
            self.temporary_folder_entry.insert(0, out)

    def praatPathBrowse (self):
        out = tkFileDialog.askopenfilename(title="Set Praat path...")
        if out:
            self.praat_path_entry.delete(0, END)
            self.praat_path_entry.insert(0, out)

    def apply(self):
        self.output["program_folder"] = self.program_folder_entry.get()
        self.output["temporary_folder"] = self.temporary_folder_entry.get()
        self.output["praat_path"] = self.praat_path_entry.get()

class RewriteName(ast.NodeTransformer):
    
    def visit_Name(self, node):
        result = ast.copy_location(
            ast.Subscript(value=
                ast.Subscript(value=
                    ast.Attribute(value=
                        ast.Attribute(value=ast.Name(id="self", ctx=ast.Load()), attr="db", ctx=ast.Load()),
                    attr="metadata_table", ctx=ast.Load()),
                slice=ast.Index(value=ast.Name(id="index", ctx=ast.Load())), ctx=ast.Load()),
            slice=ast.Index(value=
                ast.Subscript(value=
                    ast.Attribute(value=
                        ast.Attribute(value=ast.Name(id="self", ctx=ast.Load()), attr="db", ctx=ast.Load()),
                    attr="metadata_header", ctx=ast.Load()),
                slice=ast.Index(value=ast.Str(s=node.id)), ctx=ast.Load())),
            ctx=node.ctx), node)
        ast.fix_missing_locations(result)
        return result



root = Tk()
root.wm_title("Formant editor")
tkSnack.initializeSnack(root)
app = formantMonitor(root, script_dir)
root.mainloop()
back to top