Exemplo n.º 1
0
class Spectrogram(Module):
    def __init__(self,app):
        info( ' - initializing module: Spectrogram' )

        self.app = app

        self.frame = Frame(self.app.BOTTOM)
        self.frame.grid( row=0, column=1, pady=(self.app.pady*2,self.app.pady/2) )
        self.axis_frame = Frame(self.app.BOTTOM)
        self.axis_frame.grid( row=0, column=0, sticky='e', pady=(self.app.pady*2,self.app.pady/2) )
        self.canvas_width = self.app.TextGrid.canvas_width
        self.canvas_height = 106
        self.canvas = Canvas(self.frame, width=self.canvas_width, height=self.canvas_height, background='gray', highlightthickness=0)
        self.spectrogram = None
        self.spec_freq_max = DoubleVar()
        self.wl = DoubleVar()
        self.dyn_range = DoubleVar()
        self.clicktime = -1
        self.specClick = False
        self.oldSelected = None
        self.doDefaults()

        #make spinboxes & buttons for spectrogram specs
        self.spinwin = Frame(self.axis_frame)
        #spinboxes
        axis_ceil_box = Spinbox(self.spinwin, textvariable=self.spec_freq_max, command=self.drawSpectrogram, width=7, increment=100, from_=0, to_=100000)
        axis_ceil_box.bind('<Return>',self.drawSpectrogram)
        axis_ceil_box.bind('<Escape>',lambda ev: self.spinwin.focus())
        wl_box = Spinbox(self.spinwin, textvariable=self.wl, command=self.drawSpectrogram, width=7, increment=0.0005, from_=0, to_=1)
        wl_box.bind('<Return>',self.drawSpectrogram)
        wl_box.bind('<Escape>',lambda ev: self.spinwin.focus())
        dyn_range_box = Spinbox(self.spinwin, textvariable=self.dyn_range, command=self.drawSpectrogram, width=7, increment=10, from_=0, to_=10000)
        dyn_range_box.bind('<Return>',self.drawSpectrogram)
        dyn_range_box.bind('<Escape>',lambda ev: self.spinwin.focus())
        #buttons
        default_btn = Button(self.spinwin, text='Standards', command=self.restoreDefaults, takefocus=0)
        apply_btn = Button(self.spinwin, text='Apply', command=self.drawSpectrogram, takefocus=0, width=6)

        # self.axis_frame.create_window(wwidth,self.canvas_height, window=self.spinwin, anchor='ne')
        #grid spinboxes & buttons on subframe
        axis_ceil_box.grid(row=0, columnspan=2, sticky='ne')
        wl_box.grid(row=1, columnspan=2, sticky='ne')
        dyn_range_box.grid(row=2, columnspan=2, sticky='ne')
        default_btn.grid(row=3)
        apply_btn.grid(row=3, column=1)

        self.grid()

        self.canvas.bind('<Button-1>', self.jumpToFrame)
        # self.canvas.bind('<Shift-Button-1>', self.jumpToFrame)

    def doDefaults(self):
        self.spec_freq_max.set(5000.0)
        self.wl.set(0.005)
        self.dyn_range.set(90)

    def restoreDefaults(self):
        self.doDefaults()
        self.drawSpectrogram()

    def update(self):
        '''
        Removes and redraws lines on top of Spectrogram corresponding to selected interval(s)
        '''
        self.canvas.delete('line')
        self.drawInterval()

    def reset(self):
        self.drawSpectrogram()
        self.drawInterval()

    def drawSpectrogram(self, event=None):
        '''
        Extracts spectrogram data from sound, and draws it to canvas
        '''
        if not LIBS_INSTALLED:
            return

        if self.app.Audio.current:
            sound = parselmouth.Sound(self.app.Audio.current)
            self.canvas.delete('all')

            ts_fac = 10000.0
            wl = self.wl.get()
            screen_start = self.app.TextGrid.start
            screen_end = self.app.TextGrid.end
            screen_duration = screen_end - screen_start
            audio_start = 0
            audio_end = sound.get_total_duration()
            real_start = max(screen_start, audio_start)
            real_end = min(screen_end, audio_end)
            duration = real_end - real_start

            if duration <= 0:
                return

            self.ts = duration / ts_fac
            # the amount taken off in spectrogram creation seems to be
            # ( 2 * ts * floor( wl / ts ) ) + ( duration % ts )
            # but we've defined ts as duration / 10000, so duration % ts = 0
            # so the amount to increase the length by is ts * floor( wl / ts )
            # at either end - D.S.
            extra = self.ts * math.floor( wl / self.ts )
            start_time = max(0, real_start - extra)
            end_time = min(real_end + extra, sound.get_total_duration())
            sound_clip = sound.extract_part(from_time=start_time, to_time=end_time)

            spec = sound_clip.to_spectrogram(window_length=wl, time_step=self.ts, maximum_frequency=self.spec_freq_max.get())
            self.spectrogram = 10 * np.log10(np.flip(spec.values, 0))

            # self.spectrogram += self.spectrogram.min()
            # self.spectrogram *= (60.0 / self.spectrogram.max())

            mx = self.spectrogram.max()
            dyn = self.dyn_range.get()
            # debug(self.spectrogram.min(), self.spectrogram.max())
            self.spectrogram = self.spectrogram.clip(mx-dyn, mx) - mx
            # debug(self.spectrogram.min(), self.spectrogram.max())
            self.spectrogram *= (-255.0 / dyn)
            # self.spectrogram += 60
            # debug(self.spectrogram.min(), self.spectrogram.max())

            img = PIL.Image.fromarray(self.spectrogram)
            if img.mode != 'RGB':
                img = img.convert('RGB')
            # contrast = ImageEnhance.Contrast(img)
            # img = contrast.enhance(5)
            # self.canvas_height = img.height
            img = img.resize((int(self.canvas_width*(duration / screen_duration)), self.canvas_height))

            photo_img = ImageTk.PhotoImage(img)
            self.canvas.config(height=self.canvas_height)

            # self.canvas.create_image(0,0, anchor='nw', image=photo_img)
            # self.canvas.create_image(self.canvas_width/2,self.canvas_height/2, image=photo_img)
            if self.app.TextGrid.selectedItem:
                tags = self.app.TextGrid.selectedItem[0].gettags(self.app.TextGrid.selectedItem[1])
            coord = self.canvas_width
            coord *= 1 - ((screen_end - real_end) / screen_duration)
            img = self.canvas.create_image(coord, self.canvas_height, anchor='se', image=photo_img)
            self.img = photo_img
            #pass on selected-ness
            if self.app.TextGrid.selectedItem:
                if self.app.TextGrid.selectedItem[0] == self.canvas:
                    self.app.TextGrid.selectedItem = (self.canvas, img)
                    #pass on tags
                    for tag in tags:
                        self.canvas.addtag_all(tag)

    def drawInterval(self):
        '''
        Adapted with permission from
        https://courses.engr.illinois.edu/ece590sip/sp2018/spectrograms1_wideband_narrowband.html
        by Mark Hasegawa-Johnson
        '''
        if self.app.TextGrid.selectedItem:
            widg = self.app.TextGrid.selectedItem[0]
            itm = self.app.TextGrid.selectedItem[1]

            if widg in self.app.TextGrid.tier_pairs: #if widg is label
                itvl_canvas = self.app.TextGrid.tier_pairs[widg]
                for i in itvl_canvas.find_withtag('line'):
                    loc = itvl_canvas.coords(i)[0]
                    self.canvas.create_line(loc, 0, loc, self.canvas_height, tags='line', fill='blue')
            elif widg in self.app.TextGrid.tier_pairs.values(): #if widg is textgrid canvas
                if itm-1 in widg.find_all():
                    l_loc = widg.coords(itm-1)[0]
                    self.canvas.create_line(l_loc, 0, l_loc, self.canvas_height, tags='line', fill='blue')
                if itm+1 in widg.find_all():
                    r_loc = widg.coords(itm+1)[0]
                    self.canvas.create_line(r_loc, 0, r_loc, self.canvas_height, tags='line', fill='blue')
            elif widg == self.canvas:
                l_time, r_time = self.app.TextGrid.getMinMaxTime()
                l_loc = self.timeToX(float(l_time))
                r_loc = self.timeToX(float(r_time))
                self.canvas.create_line(l_loc, 0, l_loc, self.canvas_height, tags='line', fill='blue')
                self.canvas.create_line(r_loc, 0, r_loc, self.canvas_height, tags='line', fill='blue')

            #draw selected frame
            if self.app.TextGrid.firstFrame <= self.app.frame <= self.app.TextGrid.lastFrame :
                xcoord = self.app.TextGrid.frames_canvas.coords(self.app.TextGrid.highlighted_frame)[0]
                self.canvas.create_line(xcoord,0,xcoord,self.canvas_height, tags='line', fill='red')
            #draw line where user last clicked on spectrogram
            if self.clicktime != -1 and self.specClick == False:
                x = self.timeToX(self.clicktime)
                self.canvas.create_line(x,0,x,self.canvas_height, tags='line', fill='green')

    def jumpToFrame(self, event):
        '''  '''
        #restore textgrid selected interval between clicks
        if not self.app.TextGrid.selectedItem:
            key = next(iter(self.app.TextGrid.tier_pairs))
            wdg = self.app.TextGrid.tier_pairs[key]
            self.app.TextGrid.selectedItem = (wdg,wdg.find_all()[0])
            self.app.TextGrid.setSelectedIntvlFrames(self.app.TextGrid.selectedItem)
        if self.app.TextGrid.selectedItem[0] == self.canvas:
            self.app.TextGrid.selectedItem = self.oldSelected
            self.app.TextGrid.setSelectedIntvlFrames(self.app.TextGrid.selectedItem)
        #prevents wiping of canvases because of mouse click
        # self.app.resized = False
        # draw line at click location
        x = self.canvas.canvasx(event.x)
        self.clicktime = self.xToTime(x)
        #jump to new frame
        frame = self.app.TextGrid.my_find_closest(self.app.TextGrid.frames_canvas, self.canvas.canvasx(event.x))
        framenum = self.app.TextGrid.frames_canvas.gettags(frame)[0][5:]
        self.app.frame=int(framenum)
        self.app.framesUpdate()
        #remember which interval was selected before specgram click
        if event.state==1:
            self.oldSelected = self.app.TextGrid.selectedItem
        #for selecting & zooming interval (w/ shift)
            self.specClick = True

    def xToTime(self, x):
        ''' converts from a x coordinate (relative to the canvas) to the timestamp at that coordinate'''
        return (x*float(self.app.TextGrid.end - self.app.TextGrid.start)/self.canvas_width) + float(self.app.TextGrid.start)
    def timeToX(self,time):
        ''' converts from a time to the x coordinate on a canvas representing that time'''
        return self.canvas_width*(time - float(self.app.TextGrid.start))/float(self.app.TextGrid.end - self.app.TextGrid.start)

    def grid(self):
        '''
        Put tkinter items on app
        '''
        self.canvas.grid(row=0, column=0, sticky='news')
        self.spinwin.grid(row=0,column=0,sticky='ne')
        # self.axis_canvas.grid(row=0,column=0,sticky='se')

    def grid_remove(self):
        self.canvas.grid_remove()
        self.spinwin.grid_remove()
Exemplo n.º 2
0
class TaskFrame(BaseFrame):
    def __init__(self, master, **args) -> None:
        super().__init__(master=master)
        self.startnum = args['startnum']
        self.endnum = args['endnum']
        self.record = []  # [datetime, num, correction]
        self.currentnum = self.startnum
        self.create_widgets()
        self.draw2()
        self.starttime = datetime.now()

    def create_widgets(self):
        self.current_num_label = Label(self,
                                       text=self.currentnum,
                                       font=('', 30))
        self.current_num_label.pack()

        self.canvas_w = 800
        self.canvas_h = 600
        self.canvas_items = []  # idが入る [(oval_id, text_id), ...]
        self.canvas = Canvas(self,
                             bg='light gray',
                             width=self.canvas_w,
                             height=self.canvas_h)
        self.canvas.pack()

    def draw2(self):
        cn = self.currentnum
        nums = sample(list(range(cn, cn + 25)), 25)
        points = sample(list(range(0, 48)), 25)
        oval_size = 40
        for n, p in zip(nums, points):
            x0 = (p - (p // 8 * 8)) * 100
            y0 = (p // 8) * 100
            x0 = randint(x0, x0 + 100 - oval_size)
            y0 = randint(y0, y0 + 100 - oval_size)
            oval = self.canvas.create_oval(x0,
                                           y0,
                                           x0 + oval_size,
                                           y0 + oval_size,
                                           fill='gray',
                                           activefill='light gray',
                                           tags=n)
            oval_text = self.canvas.create_text(x0 + oval_size // 2,
                                                y0 + oval_size // 2,
                                                text=n,
                                                font=('', 14),
                                                fill='black',
                                                tags=n)
            self.canvas.tag_bind(oval_text, '<Enter>', self.__enter(oval))
            self.canvas.tag_bind(oval_text, '<Leave>', self.__leave(oval))
            self.canvas.tag_bind(oval_text, '<Button-1>', self.__clicked)
            self.canvas.tag_bind(oval, '<Button-1>', self.__clicked)

    def __enter(self, id):
        def inner(event):
            self.canvas.itemconfig(id, fill='light gray')

        return inner

    def __leave(self, id):
        def inner(event):
            self.canvas.itemconfig(id, fill='gray')

        return inner

    def __clicked(self, event):
        x, y = self.canvas.canvasx(event.x), self.canvas.canvasy(event.y)
        tags = [
            self.canvas.itemcget(obj, 'tags')
            for obj in self.canvas.find_overlapping(x, y, x, y)
        ]

        t = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
        correction = self.correct(tags)
        self.record.append([t, self.currentnum, correction])

        self.update()

    def correct(self, tags):
        if set([f'{self.currentnum}', f'{self.currentnum} current'
                ]) & set(tags):
            return True
        return False

    def clean_canvas(self):
        self.canvas.addtag_all('delete')
        self.canvas.delete('delete')

    def update(self):
        if self.currentnum < self.endnum:
            self.clean_canvas()
            self.currentnum += 1
            self.current_num_label.config(text=self.currentnum)
            self.draw2()
        else:
            print(*self.record, sep='\n')
            self.save()
            self.finish()

    def save(self):
        self.master.records.append(self.record)
        self.master.to_csv()

    def finish(self):
        self.master.finish()
Exemplo n.º 3
0
class GUI(Frame):
    def __init__(self, resources, master=None):
        Frame.__init__(self, master)
        self.resources = resources

        # Tkinter objects
        self.model_initial_names = None
        self.model_final_names = None
        self.widget_initial_names = None
        self.widget_final_names = None
        self.canvas_view = None

        self.model_height = None
        self.model_width = None
        self.vcmd = None

        self.selected_format = None
        self.validate = None

        # Initialize tkinter
        self.grid()
        self.create_objects()

        self.last_directory = ""

        # Lists for image name handling
        self.image_files = list()
        self.image_names = list()
        self.new_image_names = list()
        self.directory = ""

        # Thumbnails
        self.last_view = ""

        # Rules
        self.rule = None
        self.user_rule = ""

    def create_objects(self):
        top = self.winfo_toplevel()

        # Menu
        menu_top = Menu(top)
        top["menu"] = menu_top

        menu_top.add_command(label="About...")

        # Image line
        form_general = Frame(self, padx=10, pady=10)
        form_general.grid(row=0, column=0)

        # Original names
        form_initial_data = LabelFrame(form_general,
                                       text="Images",
                                       padx=10,
                                       pady=5)
        form_initial_data.grid(column=0, row=0, rowspan=2, sticky=N + S)

        initial_scroll_y = Scrollbar(form_initial_data, orient=VERTICAL)
        initial_scroll_y.grid(column=2, row=0, rowspan=5, sticky=N + S)

        self.model_initial_names = StringVar(self)
        self.widget_initial_names = Listbox(
            form_initial_data,
            height=27,
            width=35,
            activestyle="dotbox",
            listvariable=self.model_initial_names,
            selectmode=EXTENDED,
            yscrollcommand=initial_scroll_y.set)
        self.widget_initial_names.grid(column=0,
                                       row=0,
                                       rowspan=5,
                                       columnspan=2,
                                       sticky=N + S + W + E)

        initial_scroll_y["command"] = self.widget_initial_names.yview
        self.widget_initial_names.bind("<Double-Button-1>", self.select_name)
        self.widget_initial_names.bind("<<ListboxSelect>>", self.preview)

        button_up = Button(form_initial_data,
                           image=self.resources["up"],
                           command=self.name_up)
        button_up.grid(column=3, row=1, sticky=N + S)

        button_down = Button(form_initial_data,
                             image=self.resources["down"],
                             command=self.name_down)
        button_down.grid(column=3, row=3, sticky=N + S)

        button_add = Button(form_initial_data,
                            image=self.resources["add"],
                            command=self.add_file_names)
        button_add.grid(column=0, row=5, sticky=W + E)

        button_delete = Button(form_initial_data,
                               image=self.resources["delete"],
                               command=self.remove_file_names)
        button_delete.grid(column=1, row=5, sticky=W + E)

        # Preview
        form_preview = LabelFrame(form_general,
                                  text="Preview",
                                  padx=10,
                                  pady=5)
        form_preview.grid(column=1,
                          row=0,
                          columnspan=2,
                          sticky=N + S + W + E,
                          padx=10)

        form_preliminary = Frame(form_preview)
        form_preliminary.grid(column=1, row=0, sticky=N + S + W + E)

        self.canvas_view = Canvas(form_preliminary,
                                  width=256,
                                  height=256,
                                  bg="white",
                                  cursor="crosshair")
        self.canvas_view.grid(sticky=N + S + W + E)

        # Final names
        form_final_names = Frame(form_preview)
        form_final_names.grid(column=2, row=0, sticky=N + S + W + E)

        final_scroll_y = Scrollbar(form_final_names)
        final_scroll_y.grid(column=1, row=0, sticky=N + S)

        self.model_final_names = StringVar()
        self.widget_final_names = Listbox(form_final_names,
                                          height=18,
                                          width=35,
                                          activestyle="dotbox",
                                          listvariable=self.model_final_names,
                                          selectmode=SINGLE,
                                          yscrollcommand=final_scroll_y.set)
        self.widget_final_names.grid(column=0, row=0, sticky=N + W + E)
        final_scroll_y["command"] = self.widget_final_names.yview

        self.widget_final_names.bind("<Double-Button-1>", self.select_name)
        self.widget_final_names.bind("<<ListboxSelect>>", self.preview)

        # Options line
        form_options = Frame(form_general)
        form_options.grid(row=1, column=1, columnspan=2, sticky=E + W, padx=10)

        # ..dimensions
        self.model_width = StringVar()
        self.model_height = StringVar()
        form_dimensions = LabelFrame(form_options,
                                     text="Dimensions (0 = no change)",
                                     padx=10,
                                     pady=10)
        form_dimensions.grid(row=0, column=1, sticky=N + S + W + E, padx=5)
        self.vcmd = (form_dimensions.register(self.event_on_validate), "%S",
                     "%P")

        label_width = Label(form_dimensions, text="Width")
        label_width.grid(column=0, row=1, sticky=W)

        entry_width = Entry(form_dimensions,
                            validate="key",
                            validatecommand=self.vcmd,
                            textvariable=self.model_width,
                            justify=RIGHT)
        entry_width.grid(column=1, row=1, sticky=E + W)
        entry_width.insert(END, "0")

        label_height = Label(form_dimensions, text="Height")
        label_height.grid(column=0, row=2, sticky=W)

        entry_height = Entry(form_dimensions,
                             validate="key",
                             validatecommand=self.vcmd,
                             textvariable=self.model_height,
                             justify=RIGHT)
        entry_height.grid(column=1, row=2, sticky=E + W)
        entry_height.insert(END, "0")

        # .. formats
        form_formats = LabelFrame(form_options,
                                  text="Formats",
                                  padx=10,
                                  pady=10)
        form_formats.grid(row=0, column=0, rowspan=2, sticky=N + S + E + W)

        formats = ["No change", "JPG", "GIF", "BMP", "PNG", "TIFF"]
        self.selected_format = StringVar(value="No change")

        for n in range(len(formats)):
            radio_format = Radiobutton(form_formats,
                                       text=formats[n],
                                       value=formats[n],
                                       variable=self.selected_format)
            radio_format.grid(row=n + 1, column=0, sticky=W)

        # .. name change
        self.validate = (form_dimensions.register(self.event_validate_rule),
                         "%P")
        form_names = LabelFrame(form_options, text="Names", padx=10, pady=10)
        form_names.grid(row=1, column=1, sticky=N + S + E + W, padx=5)

        self.selected_name = IntVar(value=0)
        radio_nochange = Radiobutton(form_names,
                                     text="No change",
                                     value=0,
                                     variable=self.selected_name,
                                     command=self.rule_change)
        radio_nochange.grid(row=1, column=0, columnspan=2, sticky=W)

        radio_name_only = Radiobutton(form_names,
                                      text="Name + N",
                                      value=1,
                                      variable=self.selected_name,
                                      command=self.rule_change)
        radio_name_only.grid(row=2, column=0, sticky=W)

        self.entry_name_only = Entry(form_names,
                                     width=10,
                                     validate="key",
                                     validatecommand=self.validate)
        self.entry_name_only.grid(row=2, column=1, sticky=W + E)

        radio_rule = Radiobutton(form_names,
                                 text="Rule",
                                 value=2,
                                 variable=self.selected_name,
                                 command=self.rule_change)
        radio_rule.grid(row=3, column=0, sticky=W)
        self.button_change = Button(form_names,
                                    text="Change",
                                    command=self.custom_rule)
        self.button_change.grid(row=3, column=1, sticky=W + E)

        self.entry_name_only["state"] = DISABLED
        self.button_change["state"] = DISABLED

        # ..directory
        form_directory = LabelFrame(form_options,
                                    text="Destination",
                                    padx=10,
                                    pady=10)
        form_directory.grid(row=0, column=2, sticky=N + S + W)

        button_directory = Button(form_directory,
                                  image=self.resources["dir"],
                                  command=self.choose_directory)
        button_directory.grid(row=0, column=0, sticky=N + S + E + W)

        self.label_selected_directory = Label(form_directory,
                                              text="<Directory not chosen>",
                                              width=25,
                                              anchor=W)
        self.label_selected_directory.grid(row=0, column=1, stick=W + E)

        # .. convert
        self.button_convert = Button(form_options,
                                     image=self.resources["convert_d"],
                                     state=DISABLED,
                                     height=91,
                                     command=self.convert_images)
        self.button_convert.grid(row=1, column=2, sticky=E + W + S)

    #### EVENTS ############################################

    def add_file_names(self):
        new_names = get_image_file_names(self, self.last_directory)

        if len(new_names) > 0:
            names = get_names_in_path(new_names)
            self.image_files.extend(new_names)
            self.image_names.extend(names)
            self.change_names()

            self.refresh_names()

            self.last_directory = os.path.split(new_names[0])[0]

    def remove_file_names(self):
        indices = self.widget_initial_names.curselection()
        while len(indices) > 0:
            ind = indices[0]
            self.widget_initial_names.delete(ind)
            self.image_files.pop(int(ind))
            self.image_names.pop(int(ind))
            indices = self.widget_initial_names.curselection()

        if self.last_view not in self.image_files:
            self.canvas_view.delete("Image")

        self.change_names()
        self.refresh_names(True)

    def name_up(self):
        indices = list(self.widget_initial_names.curselection())
        for n in range(len(indices)):
            indices[n] = int(indices[n])
        indices.sort()

        for n in range(len(indices)):
            idx = indices[n]
            if idx != 0 and idx - 1 not in indices:
                x = self.image_files.pop(idx)
                self.image_files.insert(idx - 1, x)

                indices[n] -= 1

        self.image_names = get_names_in_path(self.image_files)

        self.change_names()
        self.refresh_names()

        for n in indices:
            self.widget_initial_names.selection_set(n)

    def name_down(self):
        indices = list(self.widget_initial_names.curselection())
        for n in range(len(indices)):
            indices[n] = int(indices[n])
        indices.sort()
        indices = indices[::-1]

        total = len(self.image_files)

        for n in range(len(indices)):
            indices[n] = int(indices[n])
            idx = indices[n]
            if idx != total - 1 and idx + 1 not in indices:
                x = self.image_files.pop(idx)
                self.image_files.insert(idx + 1, x)

                indices[n] += 1

        self.image_names = get_names_in_path(self.image_files)

        self.change_names()
        self.refresh_names()

        for n in indices:
            self.widget_initial_names.selection_set(n)

    def change_names(self):
        # Apply name rule
        self.new_image_names = self.image_names[:]
        for n in range(len(self.new_image_names)):
            self.new_image_names[n] = os.path.splitext(
                self.new_image_names[n])[0]

        if self.rule is not None:
            self.new_image_names = change_names_with_rule(
                self.new_image_names, self.rule)[0]
        return True

    def select_name(self, e):
        indices = e.widget.curselection()
        if len(indices) > 0:
            ind = int(indices[-1])
            if e.widget == self.widget_initial_names:
                self.widget_final_names.selection_clear(0, END)
                self.widget_final_names.selection_set(ind)
                self.widget_final_names.see(ind)
            else:
                self.widget_initial_names.selection_clear(0, END)
                self.widget_initial_names.selection_set(ind)
                self.widget_initial_names.see(ind)

    def refresh_names(self, only_last=False):
        if not only_last:
            self.model_initial_names.set('')
            for n in self.image_names:
                self.widget_initial_names.insert(END, n)

        self.model_final_names.set('')
        for n in self.new_image_names:
            self.widget_final_names.insert(END, n)

    def preview(self, e):
        indices = e.widget.curselection()
        if len(indices) > 0:
            idx = int(indices[-1])
            if self.last_view == self.image_files[idx]:
                return
            else:
                self.last_view = self.image_files[idx]

            try:
                current_image = Image.open(self.image_files[idx])
                current_image.thumbnail((256, 256))
                self.photo = ImageTk.PhotoImage(current_image)

                self.canvas_view.delete("Image")
                self.canvas_view.create_image(128, 128, image=self.photo)
                self.canvas_view.addtag_all("Image")
            except:
                showerror(
                    "Error in preview",
                    "It was not possible to preview image: " +
                    self.image_files[idx])

    def rule_change(self):
        n = self.selected_name.get()
        if n == 0:
            self.entry_name_only["state"] = DISABLED
            self.button_change["state"] = DISABLED
            self.rule = None
        elif n == 1:
            self.entry_name_only["state"] = NORMAL
            self.button_change["state"] = DISABLED
            self.rule = make_naming_rule(self.entry_name_only.get())
        elif n == 2:
            self.entry_name_only["state"] = DISABLED
            self.button_change["state"] = NORMAL

            self.custom_rule()

        self.change_names()
        self.refresh_names(True)

    def custom_rule(self):
        input_rule = AskRule(self, self.user_rule)
        if input_rule.result is not None:
            self.user_rule = input_rule.result
            rule = compile_rule(self.user_rule)
            if rule is None:
                showerror("Error - Rule", "Compilation error")
            else:
                self.rule = rule

        self.change_names()
        self.refresh_names(True)

    def event_validate_rule(self, text):
        self.rule = make_naming_rule(text)
        self.change_names()
        self.refresh_names(True)
        return True

    def choose_directory(self):
        directory = get_save_directory()
        if directory != "":
            self.directory = directory
            if len(directory) > 25:
                directory = directory[0:5] + "..." + directory[-16:]
            self.label_selected_directory["text"] = directory

        if self.directory != "":
            self.button_convert["state"] = NORMAL
            self.button_convert["image"] = self.resources["convert"]
        else:
            self.button_convert["state"] = DISABLED
            self.button_convert["image"] = self.resources["convert_d"]

    def convert_images(self):
        if len(self.image_files) == 0:
            showinfo("No images", "No images were selected.")
            return

        selected_format = self.selected_format.get()

        width = self.model_width.get()
        if width == "":
            width = 0
        else:
            width = int(width)

        height = self.model_height.get()
        if height == "":
            height = 0
        else:
            height = int(height)

        current_directory = self.directory

        for n in range(len(self.image_files)):
            path = self.image_files[n]
            name = self.new_image_names[n]
            name = os.path.splitext(name)[0]

            current_image = Images(path)
            if not current_image.is_valid():
                showerror("Error while converting",
                          "Unable to open image: " + path)
            else:
                if not (width == 0 and height == 0):
                    current_image.change_size(width, height)
                if selected_format != "No change":
                    current_image.change_format(selected_format)
                current_image.save(current_directory, name)

    def event_on_validate(self, modification, text):
        try:
            if len(text) > 5:
                return False

            n = int(modification)
            return True

        except ValueError:
            return False