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()
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()
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