class SettingsOBDView(Frame): settings_list = ["SAE J1850 PWM","SAE J1850 VPW","AUTO, ISO 9141-2","ISO 14230-4 (KWP 5BAUD)","ISO 14230-4 (KWP FAST)","ISO 15765-4 (CAN 11/500)","ISO 15765-4 (CAN 29/500)","ISO 15765-4 (CAN 11/250)","ISO 15765-4 (CAN 29/250)","SAE J1939 (CAN 29/250)"] def __init__(self, ui_cont, *args, **kwargs): Frame.__init__(self, *args, **kwargs) self.initializeGauges() self.placeGauges() self.ui_cont = ui_cont def initializeGauges(self): self.title_font = font.Font(family="FreeMono", size=15, weight="bold") self.list_font = font.Font(family="FreeMono", size=13) self.settings_text = Label(self, text="OBD Connection", background="white", font=self.title_font) self.listbox = Listbox(self, font=self.list_font, selectmode="single", borderwidth=0, relief="flat", highlightthickness=0) self.listbox.bind('<ButtonRelease-1>', self.item_deselected) for item in self.settings_list: self.listbox.insert(10, item) def placeGauges(self): self.settings_text.pack(fill="x") self.listbox.pack(fill="both", expand=1) def item_deselected(self, event): curr_selct = self.listbox.curselection() if len(curr_selct) > 0: item_id = self.listbox.curselection()[0] item_name = self.settings_list[item_id] self.listbox.selection_clear(item_id)
class SelectMultiple(TwoCells): def __init__(self, parent, text, values, **kwargs): super().__init__(parent, **kwargs) self.label = Label(self.frame, text=text) self.label.grid(column=0, row=0, padx=5, pady=5, sticky=W) self.selected_options = StringVar(self.frame) self.selected_options.set(values) self.listbox = Listbox(self.frame, listvariable=self.selected_options, selectmode=MULTIPLE) self.listbox.grid(column=1, row=0, padx=5, pady=5, sticky=E) def disable(self): self.listbox.configure(state=DISABLED) return self def enable(self): self.listbox.configure(state=NORMAL) return self def set(self, values): if values: self.enable() else: self.disable() self.listbox.selection_clear(0, self.listbox.size()) for value in values: for index in range(self.listbox.size()): if value == self.listbox.get(index): self.listbox.selection_set(index) self.listbox.event_generate("<<ListboxSelect>>") def get(self): return [self.listbox.get(i) for i in self.listbox.curselection()]
class AlexListBox(Frame): def __init__(self, parent, items=[], width=20, height=10, selectioncommand=None): super().__init__(parent) self.selectioncommand = selectioncommand self.listbox = Listbox(self, width=width, height=height) self.listbox.grid(row=0, column=0) scrollbar_y = AlexScrollbar(self, command=self.listbox.yview) scrollbar_y.grid(row=0, column=1, sticky=N + S) self.listbox.configure(yscrollcommand=scrollbar_y.set) scrollbar_x = AlexScrollbar(self, command=self.listbox.xview, orient=HORIZONTAL) scrollbar_x.grid(row=1, column=0, sticky=E + W) self.listbox.configure(xscrollcommand=scrollbar_x.set) if self.selectioncommand is not None: self.listbox.bind('<<ListboxSelect>>', self._select_callback) self.set_items(items) def _select_callback(self, event): selection = self.get() # ignore unselect if selection != None: self.selectioncommand(selection) def set_items(self, items): self.listbox.delete(0, END) self.items = [] for item in items: self.items.append(item) self.listbox.insert(END, "%s" % item) def get_items(self): return self.items def get(self): selections = self.listbox.curselection() if len(selections) > 0: return self.items[selections[0]] else: return None def set(self, selection): self.listbox.selection_clear(0, len(self.items)) for i in range(0, len(self.items)): if self.items[i] == selection: self.listbox.selection_set(i)
class SettingsView(Frame): settings_list = [ "General", "Wi-Fi", "Appearance", "GPS Chip", "OBD Connection" ] def __init__(self, ui_cont, *args, **kwargs): Frame.__init__(self, *args, **kwargs) self.initializeGauges() self.placeGauges() self.ui_cont = ui_cont self.gps_chip_view = SettingsGPSView(ui_cont, self.ui_cont.root) self.general_view = SettingsGeneralView(ui_cont, self.ui_cont.root) self.wifi_view = SettingsWifiView(ui_cont, self.ui_cont.root) self.appearance_view = SettingsAppearanceView(ui_cont, self.ui_cont.root) self.obd_view = SettingsOBDView(ui_cont, self.ui_cont.root) def initializeGauges(self): self.title_font = font.Font(family="FreeMono", size=15, weight="bold") self.list_font = font.Font(family="FreeMono", size=28) self.settings_text = Label(self, text="Settings", background="white", font=self.title_font) self.listbox = Listbox(self, font=self.list_font, selectmode="single", borderwidth=0, relief="flat", highlightthickness=0) self.listbox.bind('<ButtonRelease-1>', self.item_deselected) for item in self.settings_list: self.listbox.insert(10, item) def placeGauges(self): self.settings_text.pack(fill="x") self.listbox.pack(fill="both", expand=1) def item_deselected(self, event): curr_selct = self.listbox.curselection() if len(curr_selct) > 0: item_id = self.listbox.curselection()[0] item_name = self.settings_list[item_id] if item_name == "General": self.ui_cont.display_view(self.general_view) if item_name == "Wi-Fi": self.ui_cont.display_view(self.wifi_view) if item_name == "Appearance": self.ui_cont.display_view(self.appearance_view) if item_name == "GPS Chip": self.ui_cont.display_view(self.gps_chip_view) if item_name == "OBD Connection": self.ui_cont.display_view(self.obd_view) self.listbox.selection_clear(item_id)
class ListBoxChoice(object): def __init__(self, master=None, title="Title", message="Message", list=[]): self.selected_list = [] self.master = master self.list = list[:] self.master.geometry("300x250") # Width x Height self.master.grab_set() self.master.bind("<Return>", self._select) self.master.bind("<Escape>", self._cancel) self.master.title(title) Label(self.master, text=message).pack(padx=5, pady=5) self.listBox = Listbox(self.master, selectmode=MULTIPLE) self.listBox.pack(fill=BOTH) self.list.sort() for item in self.list: self.listBox.insert(END, item) buttonFrame = Frame(self.master) buttonFrame.pack(side=BOTTOM) chooseButton = Button(buttonFrame, text="Select", command=self._select) chooseButton.pack() cancelButton = Button(buttonFrame, text="Cancel", command=self._cancel) cancelButton.pack(side=RIGHT) def _select(self, event=None): try: self.selected_list = [self.listBox.get(i) for i in self.listBox.curselection()] except IndexError: self.selected_list = None self.master.destroy() def _cancel(self, event=None): self.listBox.selection_clear(0, END) def returnValue(self): self.master.wait_window() return self.selected_list
class CompetitorTable(Frame): def __init__(self, parent, db, *args, **kwargs): Frame.__init__(self, parent, *args, **kwargs) self.parent = parent self.db = db self.labelfont = ['Arial', -0.017, 'bold'] self.font = ['Arial', -0.017] self.labelfont[1] = round(self.font[1] * self.parent.parent.parent.SCREEN_HEIGHT) self.font[1] = round(self.font[1] * self.parent.parent.parent.SCREEN_HEIGHT) self.config(bg=LLBLUE) self.tablebg = 'white' self.tableborder = LLBLUE self.labelfg = 'white' self.labelbg = LBLUE self.scrollbar = Scrollbar(self) self.idLabel = Label(self, text='ID', fg=self.labelfg, bg=self.labelbg, font=self.labelfont, height=1, anchor='sw', highlightbackground=self.labelfg) self.fnameLabel = Label(self, text='First Name', fg=self.labelfg, bg=self.labelbg, font=self.labelfont, height=1, anchor='sw') self.lnameLabel = Label(self, text='Last Name', fg=self.labelfg, bg=self.labelbg, font=self.labelfont, height=1, anchor='sw') self.levelLabel = Label(self, text='Level', fg=self.labelfg, bg=self.labelbg, font=self.labelfont, height=1, anchor='sw') self.sexLabel = Label(self, text='Sex', fg=self.labelfg, bg=self.labelbg, font=self.labelfont, height=1, anchor='sw') self.ageLabel = Label(self, text='Age', fg=self.labelfg, bg=self.labelbg, font=self.labelfont, height=1, anchor='sw') self.idLB = Listbox(self, yscrollcommand=self.y_scroll, background=self.tablebg, highlightbackground=self.tableborder, borderwidth=0, width=5, font=self.font) self.fnameLB = Listbox(self, yscrollcommand=self.y_scroll, background=self.tablebg, highlightbackground=self.tableborder, borderwidth=0, width=25, font=self.font) self.lnameLB = Listbox(self, yscrollcommand=self.y_scroll, background=self.tablebg, highlightbackground=self.tableborder, borderwidth=0, width=25, font=self.font) self.levelLB = Listbox(self, yscrollcommand=self.y_scroll, background=self.tablebg, highlightbackground=self.tableborder, borderwidth=0, width=12, font=self.font) self.sexLB = Listbox(self, yscrollcommand=self.y_scroll, background=self.tablebg, highlightbackground=self.tableborder, borderwidth=0, width=2, font=self.font) self.ageLB = Listbox(self, yscrollcommand=self.y_scroll, background=self.tablebg, highlightbackground=self.tableborder, borderwidth=0, width=3, font=self.font) #self.registerButton = Button(self, bg=BLUE, fg='white', text="REGISTER\n\nNEW", font=self.font, width=5, wraplength=1, # borderwidth=1, command=self.register_new) #self.deleteButton = Button(self, bg=BLUE, fg='white', text="DELETE\n\nSELECTED", font=self.font, width=5, wraplength=1, # borderwidth=1, command=self.delete_competitor) #self.editButton = Button(self, bg=BLUE, fg='white', text="EDIT\n\nSELECTED", font=self.font, width=5, wraplength=1, # borderwidth=1, command=self.edit_competitor) self.listboxes = (self.idLB, self.fnameLB, self.lnameLB, self.levelLB, self.sexLB, self.ageLB) for lb in self.listboxes: lb.bind('<Delete>', self.delete_competitor) lb.bind('<Double-Button-1>', self.edit_competitor) self.idLB.bind('<FocusOut>', lambda e: self.idLB.selection_clear(0, 'end')) # these binds ensure that when self.fnameLB.bind('<FocusOut>', lambda e: self.fnameLB.selection_clear(0, 'end')) # leaving the table there self.lnameLB.bind('<FocusOut>', lambda e: self.lnameLB.selection_clear(0, 'end')) # doesn't remain a selection self.levelLB.bind('<FocusOut>', lambda e: self.levelLB.selection_clear(0, 'end')) # despite not having focus self.sexLB.bind('<FocusOut>', lambda e: self.sexLB.selection_clear(0, 'end')) self.ageLB.bind('<FocusOut>', lambda e: self.ageLB.selection_clear(0, 'end')) self.scrollbar.config(command=self.set_scrollables) self.idLabel.grid(row=0, column=0, sticky='nsew') self.fnameLabel.grid(row=0, column=1, sticky='nsew') self.lnameLabel.grid(row=0, column=2, sticky='nsew') self.levelLabel.grid(row=0, column=3, sticky='nsew') self.sexLabel.grid(row=0, column=4, sticky='nsew') self.ageLabel.grid(row=0, column=5, sticky='nsew') self.idLB.grid(row=1, column=0, sticky='nsew') self.fnameLB.grid(row=1, column=1, sticky='nsew') self.lnameLB.grid(row=1, column=2, sticky='nsew') self.levelLB.grid(row=1, column=3, sticky='nsew') self.sexLB.grid(row=1, column=4, sticky='nsew') self.ageLB.grid(row=1, column=5, sticky='nsew') #self.editButton.grid(row=0, column=6, sticky='nsew') #self.registerButton.grid(row=1, column=6, sticky='nsew') #self.deleteButton.grid(row=2, column=6, sticky='nsew') self.scrollbar.grid(row=0, column=7, sticky='nsew', rowspan=2) self.rowconfigure(0, weight=1) self.rowconfigure(1, weight=100) self.columnconfigure(0, weight=5) self.columnconfigure(1, weight=25) self.columnconfigure(2, weight=25) self.columnconfigure(3, weight=12) self.columnconfigure(4, weight=2) self.columnconfigure(5, weight=3) self.competitorRegistrationWindow = None def register_new(self, *args): if not self.competitorRegistrationWindow: self.competitorRegistrationWindow = CompetitorRegistrationWindow(self, self.db) self.competitorRegistrationWindow.protocol('WM_DELETE_WINDOW', self.close_competitor_registration_window) else: print('competitor registration window already open') def close_competitor_registration_window(self): self.competitorRegistrationWindow.destroy() self.competitorRegistrationWindow = None def get_selected_competitor(self): if len(self.idLB.curselection()) > 0: row = self.idLB.curselection() elif len(self.fnameLB.curselection()) > 0: row = self.fnameLB.curselection() elif len(self.lnameLB.curselection()) > 0: row = self.lnameLB.curselection() elif len(self.levelLB.curselection()) > 0: row = self.levelLB.curselection() elif len(self.sexLB.curselection()) > 0: row = self.sexLB.curselection() elif len(self.ageLB.curselection()) > 0: row = self.ageLB.curselection() # this block searches for any selection in all the list boxes return row def delete_competitor(self, *args): try: competitor_id = self.idLB.get(self.get_selected_competitor()) except UnboundLocalError: # occurs when there is no competitor selected print('cannot delete a competitor when there is none selected') return self.db.delete_row(competitor_id) def edit_competitor(self, *args): try: id = self.get_selected_competitor() except UnboundLocalError: # occurs when there is no competitor selected print('cannot edit a competitor when there is none selected') return if id: entrytab = self.parent.parent.parent.entryTab entrytab.routeAttemptsEntryFrame.reset() enable_frame(entrytab.competitorInfoFrame) enable_frame(entrytab.routeAttemptsEntryFrame) entrytab.competitorInfoFrame.fill_competitor(self.idLB.get(id)) # fills info to entry def set_scrollables(self, *args): self.idLB.yview(*args) self.fnameLB.yview(*args) self.lnameLB.yview(*args) self.levelLB.yview(*args) self.sexLB.yview(*args) self.ageLB.yview(*args) def y_scroll(self, *args): # keeps all listboxes at same scroll position always for lb in self.listboxes: lb.yview_moveto(args[0]) self.scrollbar.set(*args) def update_table(self, pattern=None): self.clear_table() if pattern and not pattern == 'Search competitors...': rows = self.db.get_specific(pattern) else: rows = self.db.get_all() for i, row in enumerate(rows): self.idLB.insert(i, row[0]) self.fnameLB.insert(i, row[1]) self.lnameLB.insert(i, row[2]) self.levelLB.insert(i, row[3]) self.sexLB.insert(i, row[4]) self.ageLB.insert(i, row[5]) def clear_table(self): self.idLB.delete(0, 'end') self.fnameLB.delete(0, 'end') self.lnameLB.delete(0, 'end') self.levelLB.delete(0, 'end') self.sexLB.delete(0, 'end') self.ageLB.delete(0, 'end')
class Widgets: def __init__(self, root): self.setup = Setup(self) json_data = Json() self.update = Update(self, self.setup) self.db = LocalSqlite3() self.tracks = Tracks() self.top_menu = TopMenu(self, root) self.preset_setups = json_data.preset_setups self.game_modes = json_data.game_modes self.weatherTypes = json_data.weather self.cars = ('All Cars', '') self.raceSettings = json_data.cars #self.game_versions = json_data.game_versions self.c = Frame(root, padding=(5, 5, 12, 0)) self.c.grid(column=0, row=0, sticky=(N, W, E, S)) root.grid_columnconfigure(0, weight=1) root.grid_rowconfigure(0, weight=1) self.bg = "#33393b" # backgroundcolor self.fg = "white" # forgroundcolor self.status_message = StringVar() self.tracks_sorted = StringVar(value=self.tracks.track_sorted_list) self.sliderFrame = LabelFrame( self.c, text="Setup", labelanchor='nw', padding=5) self.track_box = Listbox( self.c, listvariable=self.tracks_sorted, height=len(self.tracks.track_sorted_list), bg=self.bg, fg=self.fg, highlightcolor="black", selectbackground="darkred", selectforeground="white") self.track_box.selection_clear(1, last=None) # self.game_version_box = self.create_combobox(list(self.game_versions)) self.race_box = self.create_combobox(list(self.raceSettings)) self.cars_box = self.create_combobox(self.cars) self.weather_box = self.create_combobox(list(self.weatherTypes)) self.game_mode_box = self.create_combobox(list(self.game_modes)) self.preset_box = self.create_combobox(list(self.preset_setups.values())) """ self.sort_tracks_box = self.create_checkbox( 'Order Tracks', self.sort_tracks, lambda: self.toggle_track_list(self.sort_tracks.get())) self.auto_use_changes_box = self.create_checkbox( 'Auto Use Changes', self.auto_use_changes, lambda: self.update_auto_use(self.auto_use_changes.get())) self.auto_save_changes_box = self.create_checkbox( 'Auto Save Changes', self.auto_save_changes, lambda: self.update_auto_save(self.auto_save_changes.get())) self.auto_use_track_box = self.create_checkbox( 'Auto Use track', self.auto_use_track, lambda: self.update_auto_use_track(self.auto_use_track.get())) """ # self.upload_button = self.create_button("Upload", self.upload) # self.community_button = self.create_button("Community", community.Community) # self.import_setups = self.create_button("import previous setups", self.update.populate_db_from_setups_dir) self.useButton = self.create_button("Use", self.use_cmd) # self.useSaveButton = self.create_button("Save & Use", self.use_save_cmd) # self.saveButton = self.create_button("Save", self.save_cmd) # self.saveAsButton = self.create_button("Save As", self.save_as_cmd) # self.openButton = self.create_button("Open", self.open_cmd) # self.tipBtn = self.create_button("Tip Scruffe", # lambda: self.open_url("https://paypal.me/valar")) self.status_bar = Label( self.c, textvariable=self.status_message, anchor=W) scales = MakeScale(self.sliderFrame) self.front_wing_Scale = scales.make("Front wing") self.rear_wing_Scale = scales.make("Rear wing") self.on_throttle_Scale = scales.make("On throttle", from_=50, to=100) self.off_throttle_Scale = scales.make("Off throttle", from_=50, to=100) self.front_camber_Scale = scales.make("Front camber", step=0.1, offset=-3.5) self.rear_camber_Scale = scales.make("Rear camber", step=0.1, offset=-2) self.front_toe_Scale = scales.make("Front toe", step=0.01, offset=0.05, res=2) self.rear_toe_Scale = scales.make("Rear toe", step=0.03, offset=0.20, res=2) # there is an offset self.front_suspension_Scale = scales.make("Front suspension") self.rear_suspension_Scale = scales.make("Rear suspension") self.front_antiroll_bar_Scale = scales.make("Front antiroll bar") self.rear_antiroll_bar_Scale = scales.make("Rear antiroll bar") self.front_suspension_height_Scale = scales.make("Front suspension height") self.rear_suspension_height_Scale = scales.make("Rear suspension height") self.brake_pressure_Scale = scales.make("Brake pressure", from_=50, to=100) self.brake_bias_Scale = scales.make("Brake bias", from_=70, to=50) self.front_right_tyre_pressure_Scale = scales.make("Front right tyre pressure", step=.4, offset=21) # , 0.4) self.front_left_tyre_pressure_Scale = scales.make("Front left tyre pressure", step=.4, offset=21) self.rear_right_tyre_pressure_Scale = scales.make("Rear right tyre pressure", step=.4, offset=19.5) self.rear_left_tyre_pressure_Scale = scales.make("Rear left tyre pressure", step=.4, offset=19.5) self.ballast_Scale = scales.make("Ballast") self.ramp_differential_Scale = scales.make("Ramp differential", from_=50, to=70) self.fuel_load_Scale = scales.make("Fuel load", from_=5, to=110) self.grid() self.enable_free_widgets() @property def boxes(self): return [self.track_box, self.race_box, self.cars_box, self.weather_box, self.game_mode_box, self.preset_box] """@property def check_boxes(self): return [self.sort_tracks_box, self.auto_use_changes_box, self.auto_save_changes_box, self.auto_use_track_box, # self.import_setups ]""" @property def league_id(self): return self.race_box.get() @property def team_id(self): """returns the ingame f1game team_id""" team_ids = [ # https://forums.codemasters.com/topic/50942-f1-2020-udp-specification/ "Mercedes", "Ferrari", "Red Bull", "Williams", "Racing Point", "Renault", "AlphaTauri", "Haas", "McLaren", "Alfa Romeo", "McLaren 1988", "McLaren 1991", "Williams 1992", "Ferrari 1995", "Williams 1996", "McLaren 1998", "Ferrari 2002", "Ferrari 2004", "Renault 2006", "Ferrari 2007", "McLaren 2008", "Red Bull 2010", "Ferrari 1976", "ART Grand Prix", "Campos Vexatec Racing", "Carlin", "Charouz Racing System", "Dams", "Uni-Virtuosi Racing", # changed russiantime "MP Motorsport", "Pertamina", # "McLaren 1990", "Trident", "BWT HWA Racelab", "McLaren 1976", "Lotus 1972", "Ferrari 1979", "McLaren 1982", "Williams 2003", "Brawn 2009", "Lotus 1978", "F1 Generic car", "Art GP ’19", "Campos ’19", "Carlin ’19", "Sauber Junior Charouz ’19", "Dams '19", "Uni-Virtuosi ‘19", "MP Motorsport ‘19", "Prema ’19", "Trident ’19", "Arden ’19", "Benetton 1994", "Benetton 1995", "Ferrari 2000", "Jordan 1991", "All Cars", "My Team"] # 255 """ f2 does not comply with udp specification some examples: 69 =multiplayer 76= mpmotersport 77 perta 78 trident 79 bwt 80 hitech 81 jordan91 82 benet """ team = self.cars_box.get() if team == "All Cars": return 41 # multiplayer car return team_ids.index(team) @property def game_mode_id(self): return self.game_modes[self.game_mode_box.get()] @property def weather_id(self): if self.weather_box.get() == "Wet": return 0 return 1 @property def sliders(self): """list of sliders""" return [ self.front_wing_Scale, self.rear_wing_Scale, self.on_throttle_Scale, self.off_throttle_Scale, self.front_camber_Scale, self.rear_camber_Scale, self.front_toe_Scale, self.rear_toe_Scale, self.front_suspension_Scale, self.rear_suspension_Scale, self.front_suspension_height_Scale, self.rear_suspension_height_Scale, self.front_antiroll_bar_Scale, self.rear_antiroll_bar_Scale, self.brake_pressure_Scale, self.brake_bias_Scale, self.front_right_tyre_pressure_Scale, self.front_left_tyre_pressure_Scale, self.rear_right_tyre_pressure_Scale, self.rear_left_tyre_pressure_Scale, self.ballast_Scale, self.fuel_load_Scale, self.ramp_differential_Scale] @sliders.setter def sliders(self, unpacked_setup): print(' - - - - - - - - setting sliders - - - - - - - - ') print(unpacked_setup) database_setup = unpacked_setup[0] if database_setup is True: i = 8 for slider in self.sliders: if slider.offset != 1: value = unpacked_setup[i] p = round(value - slider.offset, slider.res) product = round(p / slider.step, slider.res) + 1 slider.set(product) else: slider.set(unpacked_setup[i]) i += 1 print('loaded : ', unpacked_setup[1:8]) print('sliders values : ', unpacked_setup[8:]) else: # .bin file create_slider = SliderCounter(unpacked_setup) for slider in self.sliders: create_slider.set_slider_value(slider) print(" - - - - - - - - - - - - - - - - ") def create_combobox(self, _values): return Combobox( self.c, justify="center", values=_values, state="readonly") def create_checkbox(self, text, variable, command=None): return Checkbutton( self.c, text=text, variable=variable, command=command) def create_checkbutton(self, txt, var, cmd): return Checkbutton( self.c, text=txt, variable=var, command=lambda: cmd) def create_button(self, text, command=None): return Button( self.c, text=text, command=command) def grid(self): self.sliderFrame.grid( row=0, rowspan=15, column=2, columnspan=5) box_grid = GridWidgets() for box in self.boxes: box_grid.grid_box(box) check_box_grid = GridWidgets(startrow=8, padx=10) """for check_box in self.check_boxes: check_box_grid.grid_box(check_box)""" buttons = GridWidgets(startrow=check_box_grid.row, increment_horizontal=True) buttons.grid_box(self.useButton, columnspan=2) # buttons.grid_box(self.saveButton) # buttons.grid_box(self.saveAsButton) # buttons.grid_box(self.openButton) # buttons.grid_box(self.tipBtn, columnspan=2) # buttons.grid_box(self.community_button) # buttons.grid_box(self.upload_button) status = GridWidgets(startrow=(buttons.row + 16)) status.grid_box(self.status_bar, columnspan=7) self.c.grid_columnconfigure(0, weight=1) self.c.grid_rowconfigure(11, weight=1) def use_cmd(self): print(" - - - - - - - - using - - - - - - - - ") self.setup.use_setup() self.status_message.set("Using current setup") print(" - - - - - - - - - - - - - - - - ") def open_cmd(self): print(" - - - - - - - - opening file - - - - - - - - ") try: path = self.setup.open_setup() self.status_message.set(" Opened (" + str(path) + ")") except FileNotFoundError: self.status_message.set(" Can't find file)") print(" - - - - - - - - - - - - - - - - ") def use_save_cmd(self): self.setup.use_setup() self.save_cmd() def save_cmd(self): print(" - - - - - - - - saving - - - - - - - - ") car_setup = self.create_car_setup_from_widgets() print("car setup info: ", car_setup.info) print("car setup values: ", car_setup.values) self.setup.save_setup(car_setup) self.status_message.set("Saved") print(" - - - - - - - - - - - - - - - - ") def save_as_cmd(self): print(" - - - - - - - - save as - - - - - - - - ") try: path = self.setup.save_as_setup() self.status_message.set("Saved: " + path) except FileNotFoundError: self.status_message.set("File not found") def upload(self): print(" - - - - - - - - uploading - - - - - - - - ") car_setup = self.create_car_setup_from_widgets() print(car_setup.info) # commands.Commands().upload(car_setup) def toggle_track_list_order(self, sort_bool): print(" - - - - - - - - sorting track list - - - - - - - - ") """sort track list based on calendar or Alphabet""" # save new value to config file Config().sort_tracks = sort_bool # update track list self.tracks.toggle_track_sort(sort_bool) self.track_box.delete(0, END) self.track_box.insert(END, *self.tracks.track_sorted_list) # redo background color self.tracks_background_color() # show the previous selected track Events(self).box_event() def tracks_background_color(self): for i in range(0, len(self.tracks.track_sorted_list), 2): self.track_box.itemconfigure(i, background='#576366', fg=self.fg) def set_starting_values(self): print(" - - - - - - - - set starting values - - - - - - - - ") race = Config().race self.track_box.selection_set(0) self.race_box.set(race) self.cars_box['values'] = self.raceSettings[race] self.cars_box.set(Config().cars) self.weather_box.set(Config().weather) self.game_mode_box.set(Config().game_mode) self.preset_box.set("Load Preset") self.status_message.set('') self.top_menu.set_starting_values() self.toggle_race_sliders(race) Events(self).show_track_selection() def toggle_race_sliders(self, race): print(" - - - - - - - - toggle race sliders - - - - - - - - ") on_throttle = 'enabled' off_throttle = 'enabled' brake_pressure = 'enabled' fuel_load = 'enabled' ballast = 'enabled' ramp_differential = 'enabled' if race == 'F1 2021' or race == 'F1 2020' or race == 'classic': ramp_differential = 'disabled' if race == 'F1 2021' or race == 'F1 2020': ballast = 'disabled' elif race == 'F2 2021' or race == 'F2 2020' or race == 'F2 2019': on_throttle = 'disabled' off_throttle = 'disabled' brake_pressure = 'disabled' fuel_load = 'disabled' self.on_throttle_Scale.config(state=on_throttle) self.off_throttle_Scale.config(state=off_throttle) self.brake_pressure_Scale.config(state=brake_pressure) self.fuel_load_Scale.config(state=fuel_load) self.ballast_Scale.config(state=ballast) self.ramp_differential_Scale.config(state=ramp_differential) def create_car_setup_from_widgets(self): print("... creating car setup from widgets") current_track = Config().current_track league_id = league_sql.LeagueSql().get_id_from_name(self.league_id) track_id = TrackSql().get_track_id_by_country(current_track) print("self.config.current_track", current_track) print("track id", track_id) team_id = self.db.teams.get_team_id(self.cars_box.get(), league_id) car_setup = CarSetup( league_id=league_id, save_name=" save name test", team_id=team_id, track_id=track_id, game_mode_id=self.game_mode_id, weather_id=self.weather_id, front_wing=self.front_wing_Scale.get(), rear_wing=self.rear_wing_Scale.get(), on_throttle=self.on_throttle_Scale.get(), off_throttle=self.off_throttle_Scale.get(), front_camber=self.front_camber_Scale.get(), rear_camber=self.rear_camber_Scale.get(), front_toe=self.front_toe_Scale.get(), rear_toe=self.rear_toe_Scale.get(), front_suspension=self.front_suspension_Scale.get(), rear_suspension=self.rear_suspension_Scale.get(), front_suspension_height=self.front_suspension_height_Scale.get(), rear_suspension_height=self.rear_suspension_height_Scale.get(), front_antiroll_bar=self.front_antiroll_bar_Scale.get(), rear_antiroll_bar=self.rear_antiroll_bar_Scale.get(), brake_pressure=self.brake_pressure_Scale.get(), brake_bias=self.brake_bias_Scale.get(), front_right_tyre_pressure=self.front_right_tyre_pressure_Scale.get(), front_left_tyre_pressure=self.front_left_tyre_pressure_Scale.get(), rear_right_tyre_pressure=self.rear_right_tyre_pressure_Scale.get(), rear_left_tyre_pressure=self.rear_left_tyre_pressure_Scale.get(), ballast=self.ballast_Scale.get(), fuel_load=self.fuel_load_Scale.get(), ramp_differential=self.ramp_differential_Scale.get() ) return car_setup @staticmethod def open_url(url): open_new(url) def enable_free_widgets(self): self.race_box.configure(state="readonly") self.preset_box.configure(state='readonly') def premium(self): """enables premium content"""
class AlbumsPanel(Frame): def __init__(self, parent, albumsController: IAlbumsController): super().__init__(parent) self.albumsController = albumsController self.albumList: List[AlbumDb] = [] self.selectedAlbumName: str = None self.musicFromDisc: List[Music] = [] self.displayedMusicOnComputer = None self.displayedMusicInAlbum = None self.__initView() def __initView(self): self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(3, weight=1) self.albumsListBox = Listbox(self, exportselection=0) self.albumsListBox.grid(row=0, column=0, sticky='WE', padx=(10, 0), pady=10) self.albumsListBox.bind('<Double-1>', self.__albumDoubleClicked) self.albumsListBox.bind("<<ListboxSelect>>", self.__albumSelected) albumsScrollbar = Scrollbar(self, command=self.albumsListBox.yview) albumsScrollbar.grid(row=0, column=1, sticky='NS', padx=(0, 10), pady=10) self.albumsListBox.configure(yscrollcommand=albumsScrollbar.set) ###################################################################################################### buttonsFrame = Frame(self) buttonsFrame.grid(row=1, column=0, sticky='W', padx=10) self.albumDeleteButton = Button(buttonsFrame, text=ALBUM_DELETE_TEXT, command=self.__deleteAlbum) self.albumDeleteButton.grid(row=0, column=0, padx=5) self.albumExportButton = Button(buttonsFrame, text=ALBUM_EXPORT_TEXT, command=self.__exportAlbumClicked) self.albumExportButton.grid(row=0, column=1, padx=5) self.albumImportButton = Button(buttonsFrame, text=ALBUM_IMPORT_TEXT, command=self.__importAlbumClicked) self.albumImportButton.grid(row=0, column=2, padx=5) ###################################################################################################### createFrame = Frame(self) createFrame.grid(row=2, column=0, sticky='W', padx=10) label = Label(createFrame, text=ALBUM_CREATE_LABEL_TEXT) label.grid(row=0, column=0) self.createAlbumNameEntry = Entry(createFrame) self.createAlbumNameEntry.grid(row=0, column=1) self.createAlbumButton = Button(createFrame, text=ALBUM_CREATE_BUTTON_TEXT, command=self.__createAlbum) self.createAlbumButton.grid(row=0, column=2) ###################################################################################################### # Music on computer musicFrame = Frame(self, padx=10, pady=10) musicFrame.grid(row=3, column=0, columnspan=2, sticky='WENS') musicFrame.grid_columnconfigure(0, weight=1) musicFrame.grid_columnconfigure(3, weight=1) musicFrame.grid_rowconfigure(1, weight=1) label = Label(musicFrame, text=MUSIC_ON_COMPUTER_TEXT) label.grid(row=0, column=0, sticky='W') self.musicOnComputerListBox = Listbox(musicFrame) self.musicOnComputerListBox.grid(row=1, column=0, sticky='WENS') musicOnComputerScrollbar = Scrollbar(musicFrame, command=self.musicOnComputerListBox.yview) musicOnComputerScrollbar.grid(row=1, column=1, sticky='NS') self.musicOnComputerListBox.configure(yscrollcommand=musicOnComputerScrollbar.set) ###################################################################################################### buttonFrame = Frame(musicFrame, padx=10) buttonFrame.grid(row=1, column=2) self.removeFromAlbumButton = Button(buttonFrame, text='<<', command=self.__removeMusicFromAlbumClicked) self.removeFromAlbumButton.grid(row=0, column=0) self.addToAlbumButton = Button(buttonFrame, text='>>', command=self.__addMusicToAlbumClicked) self.addToAlbumButton.grid(row=1, column=0) ###################################################################################################### #Music in album label = Label(musicFrame, text=MUSIC_IN_ALBUM_TEXT) label.grid(row=0, column=3, sticky='W') self.musicInAlbumListBox = Listbox(musicFrame) self.musicInAlbumListBox.grid(row=1, column=3, sticky='WENS') musicInAlbumScrollbar = Scrollbar(musicFrame, command=self.musicInAlbumListBox.yview) musicInAlbumScrollbar.grid(row=1, column=4, sticky='NS') self.musicInAlbumListBox.configure(yscrollcommand=musicInAlbumScrollbar.set) ###################################################################################################### def displayAlbums(self, albums: List[AlbumDb], musicFromDisc: List[Music]): self.albumList = albums self.musicFromDisc = musicFromDisc self.albumsListBox.delete(0, 'end') for i in range(len(self.albumList)): a = self.albumList[i] self.albumsListBox.insert(i, a.name) if self.selectedAlbumName is not None: selectedIndex = -1 for i in range(len(self.albumList)): if self.albumList[i].name == self.selectedAlbumName: selectedIndex = i break if selectedIndex >= 0: self.albumsListBox.select_set(selectedIndex) self.albumsListBox.event_generate("<<ListboxSelect>>") def __deleteAlbum(self): selection = self.albumsListBox.curselection() if len(selection) <= 0: messagebox.showwarning('Warning', DELETE_ALBUM_SELECT_TEXT) return self.albumsListBox.selection_clear(0, END) self.albumsListBox.event_generate("<<ListboxSelect>>") album = self.albumList[selection[0]] self.albumsController.deleteAlbum(album.name) def __createAlbum(self): name = self.createAlbumNameEntry.get() if name is None or name == '': messagebox.showwarning('Warning', CRATE_ALBUM_INSERT_NAME_TEXT) return self.albumsController.createAlbum(name) def __albumSelected(self, event): self.musicOnComputerListBox.delete(0, 'end') self.musicInAlbumListBox.delete(0, 'end') selection = self.albumsListBox.curselection() if len(selection) <= 0: return selectedAlbum = self.albumList[selection[0]] self.selectedAlbumName = selectedAlbum.name self.__lisMusicOnComputer(selectedAlbum) self.__lisMusicInAlbum(selectedAlbum) def __lisMusicOnComputer(self, album): musicInAlbum = set() for m in album.music: musicInAlbum.add(m.path) if self.musicFromDisc is None: return music = self.musicFromDisc.copy() music = filter(lambda m: m.path not in musicInAlbum, music) self.displayedMusicOnComputer = list(music) i = 0 for m in self.displayedMusicOnComputer: self.musicOnComputerListBox.insert(i, m.filename) i = i + 1 def __lisMusicInAlbum(self, album): musicOnDisc = set() if self.musicFromDisc is not None: for m in self.musicFromDisc: musicOnDisc.add(m.path) self.displayedMusicInAlbum = list(album.music) i = 0 for m in self.displayedMusicInAlbum: description = os.path.basename(m.path) if m.path not in musicOnDisc: description = '<missing> ' + m.path self.musicInAlbumListBox.insert(i, description) i = i + 1 def __addMusicToAlbumClicked(self): selection = self.albumsListBox.curselection() if len(selection) <= 0: return selectedAlbum = self.albumList[selection[0]] selection = self.musicOnComputerListBox.curselection() if len(selection) <= 0: messagebox.showwarning('Warning', ADD_TO_ALBUM_SELECT_TEXT) return selectedMusic = self.displayedMusicOnComputer[selection[0]] self.albumsController.addMusicToAlbum(selectedAlbum.name, selectedMusic.path) def __removeMusicFromAlbumClicked(self): selection = self.albumsListBox.curselection() if len(selection) <= 0: return selectedAlbum = self.albumList[selection[0]] selection = self.musicInAlbumListBox.curselection() if len(selection) <= 0: messagebox.showwarning('Warning', REMOVE_FROM_ALBUM_SELECT_TEXT) return selectedMusic = self.displayedMusicInAlbum[selection[0]] self.albumsController.removeMusicFromAlbum(selectedAlbum.name, selectedMusic.path) def __exportAlbumClicked(self): selection = self.albumsListBox.curselection() if len(selection) <= 0: messagebox.showwarning('Warning', EXPORT_ALBUM_SELECT_TEXT) return selectedAlbum = self.albumList[selection[0]] self.albumsController.exportAlbum(selectedAlbum.name) def __importAlbumClicked(self): self.albumsController.importAlbum() def __albumDoubleClicked(self, event): album = self.albumList[self.albumsListBox.curselection()[0]] self.albumsController.addAlbumToQueue(album)
class RecursiveDescentApp(object): """ A graphical tool for exploring the recursive descent parser. The tool displays the parser's tree and the remaining text, and allows the user to control the parser's operation. In particular, the user can expand subtrees on the frontier, match tokens on the frontier against the text, and backtrack. A "step" button simply steps through the parsing process, performing the operations that ``RecursiveDescentParser`` would use. """ def __init__(self, grammar, sent, trace=0): self._sent = sent self._parser = SteppingRecursiveDescentParser(grammar, trace) # Set up the main window. self._top = Tk() self._top.title('Recursive Descent Parser Application') # Set up key bindings. self._init_bindings() # Initialize the fonts. self._init_fonts(self._top) # Animations. animating_lock is a lock to prevent the demo # from performing new operations while it's animating. self._animation_frames = IntVar(self._top) self._animation_frames.set(5) self._animating_lock = 0 self._autostep = 0 # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_feedback(self._top) self._init_grammar(self._top) self._init_canvas(self._top) # Initialize the parser. self._parser.initialize(self._sent) # Resize callback self._canvas.bind('<Configure>', self._configure) ######################################### ## Initialization Helpers ######################################### def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = tkinter.font.Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget('size')) self._boldfont = tkinter.font.Font(family='helvetica', weight='bold', size=self._size.get()) self._font = tkinter.font.Font(family='helvetica', size=self._size.get()) if self._size.get() < 0: big = self._size.get() - 2 else: big = self._size.get() + 2 self._bigfont = tkinter.font.Font(family='helvetica', weight='bold', size=big) def _init_grammar(self, parent): # Grammar view. self._prodframe = listframe = Frame(parent) self._prodframe.pack(fill='both', side='left', padx=2) self._prodlist_label = Label(self._prodframe, font=self._boldfont, text='Available Expansions') self._prodlist_label.pack() self._prodlist = Listbox(self._prodframe, selectmode='single', relief='groove', background='white', foreground='#909090', font=self._font, selectforeground='#004040', selectbackground='#c0f0c0') self._prodlist.pack(side='right', fill='both', expand=1) self._productions = list(self._parser.grammar().productions()) for production in self._productions: self._prodlist.insert('end', (' %s' % production)) self._prodlist.config(height=min(len(self._productions), 25)) # Add a scrollbar if there are more than 25 productions. if len(self._productions) > 25: listscroll = Scrollbar(self._prodframe, orient='vertical') self._prodlist.config(yscrollcommand=listscroll.set) listscroll.config(command=self._prodlist.yview) listscroll.pack(side='left', fill='y') # If they select a production, apply it. self._prodlist.bind('<<ListboxSelect>>', self._prodlist_select) def _init_bindings(self): # Key bindings are a good thing. self._top.bind('<Control-q>', self.destroy) self._top.bind('<Control-x>', self.destroy) self._top.bind('<Escape>', self.destroy) self._top.bind('e', self.expand) #self._top.bind('<Alt-e>', self.expand) #self._top.bind('<Control-e>', self.expand) self._top.bind('m', self.match) self._top.bind('<Alt-m>', self.match) self._top.bind('<Control-m>', self.match) self._top.bind('b', self.backtrack) self._top.bind('<Alt-b>', self.backtrack) self._top.bind('<Control-b>', self.backtrack) self._top.bind('<Control-z>', self.backtrack) self._top.bind('<BackSpace>', self.backtrack) self._top.bind('a', self.autostep) #self._top.bind('<Control-a>', self.autostep) self._top.bind('<Control-space>', self.autostep) self._top.bind('<Control-c>', self.cancel_autostep) self._top.bind('<space>', self.step) self._top.bind('<Delete>', self.reset) self._top.bind('<Control-p>', self.postscript) #self._top.bind('<h>', self.help) #self._top.bind('<Alt-h>', self.help) self._top.bind('<Control-h>', self.help) self._top.bind('<F1>', self.help) #self._top.bind('<g>', self.toggle_grammar) #self._top.bind('<Alt-g>', self.toggle_grammar) #self._top.bind('<Control-g>', self.toggle_grammar) self._top.bind('<Control-g>', self.edit_grammar) self._top.bind('<Control-t>', self.edit_sentence) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill='none', side='bottom', padx=3, pady=2) Button( buttonframe, text='Step', background='#90c0d0', foreground='black', command=self.step, ).pack(side='left') Button( buttonframe, text='Autostep', background='#90c0d0', foreground='black', command=self.autostep, ).pack(side='left') Button(buttonframe, text='Expand', underline=0, background='#90f090', foreground='black', command=self.expand).pack(side='left') Button(buttonframe, text='Match', underline=0, background='#90f090', foreground='black', command=self.match).pack(side='left') Button(buttonframe, text='Backtrack', underline=0, background='#f0a0a0', foreground='black', command=self.backtrack).pack(side='left') # Replace autostep... # self._autostep_button = Button(buttonframe, text='Autostep', # underline=0, command=self.autostep) # self._autostep_button.pack(side='left') def _configure(self, event): self._autostep = 0 (x1, y1, x2, y2) = self._cframe.scrollregion() y2 = event.height - 6 self._canvas['scrollregion'] = '%d %d %d %d' % (x1, y1, x2, y2) self._redraw() def _init_feedback(self, parent): self._feedbackframe = feedbackframe = Frame(parent) feedbackframe.pack(fill='x', side='bottom', padx=3, pady=3) self._lastoper_label = Label(feedbackframe, text='Last Operation:', font=self._font) self._lastoper_label.pack(side='left') lastoperframe = Frame(feedbackframe, relief='sunken', border=1) lastoperframe.pack(fill='x', side='right', expand=1, padx=5) self._lastoper1 = Label(lastoperframe, foreground='#007070', background='#f0f0f0', font=self._font) self._lastoper2 = Label(lastoperframe, anchor='w', width=30, foreground='#004040', background='#f0f0f0', font=self._font) self._lastoper1.pack(side='left') self._lastoper2.pack(side='left', fill='x', expand=1) def _init_canvas(self, parent): self._cframe = CanvasFrame( parent, background='white', #width=525, height=250, closeenough=10, border=2, relief='sunken') self._cframe.pack(expand=1, fill='both', side='top', pady=2) canvas = self._canvas = self._cframe.canvas() # Initially, there's no tree or text self._tree = None self._textwidgets = [] self._textline = None def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label='Reset Parser', underline=0, command=self.reset, accelerator='Del') filemenu.add_command(label='Print to Postscript', underline=0, command=self.postscript, accelerator='Ctrl-p') filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-x') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) editmenu.add_command(label='Edit Grammar', underline=5, command=self.edit_grammar, accelerator='Ctrl-g') editmenu.add_command(label='Edit Text', underline=5, command=self.edit_sentence, accelerator='Ctrl-t') menubar.add_cascade(label='Edit', underline=0, menu=editmenu) rulemenu = Menu(menubar, tearoff=0) rulemenu.add_command(label='Step', underline=1, command=self.step, accelerator='Space') rulemenu.add_separator() rulemenu.add_command(label='Match', underline=0, command=self.match, accelerator='Ctrl-m') rulemenu.add_command(label='Expand', underline=0, command=self.expand, accelerator='Ctrl-e') rulemenu.add_separator() rulemenu.add_command(label='Backtrack', underline=0, command=self.backtrack, accelerator='Ctrl-b') menubar.add_cascade(label='Apply', underline=0, menu=rulemenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_checkbutton(label="Show Grammar", underline=0, variable=self._show_grammar, command=self._toggle_grammar) viewmenu.add_separator() viewmenu.add_radiobutton(label='Tiny', variable=self._size, underline=0, value=10, command=self.resize) viewmenu.add_radiobutton(label='Small', variable=self._size, underline=0, value=12, command=self.resize) viewmenu.add_radiobutton(label='Medium', variable=self._size, underline=0, value=14, command=self.resize) viewmenu.add_radiobutton(label='Large', variable=self._size, underline=0, value=18, command=self.resize) viewmenu.add_radiobutton(label='Huge', variable=self._size, underline=0, value=24, command=self.resize) menubar.add_cascade(label='View', underline=0, menu=viewmenu) animatemenu = Menu(menubar, tearoff=0) animatemenu.add_radiobutton(label="No Animation", underline=0, variable=self._animation_frames, value=0) animatemenu.add_radiobutton(label="Slow Animation", underline=0, variable=self._animation_frames, value=10, accelerator='-') animatemenu.add_radiobutton(label="Normal Animation", underline=0, variable=self._animation_frames, value=5, accelerator='=') animatemenu.add_radiobutton(label="Fast Animation", underline=0, variable=self._animation_frames, value=2, accelerator='+') menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label='About', underline=0, command=self.about) helpmenu.add_command(label='Instructions', underline=0, command=self.help, accelerator='F1') menubar.add_cascade(label='Help', underline=0, menu=helpmenu) parent.config(menu=menubar) ######################################### ## Helper ######################################### def _get(self, widget, treeloc): for i in treeloc: widget = widget.subtrees()[i] if isinstance(widget, TreeSegmentWidget): widget = widget.label() return widget ######################################### ## Main draw procedure ######################################### def _redraw(self): canvas = self._canvas # Delete the old tree, widgets, etc. if self._tree is not None: self._cframe.destroy_widget(self._tree) for twidget in self._textwidgets: self._cframe.destroy_widget(twidget) if self._textline is not None: self._canvas.delete(self._textline) # Draw the tree. helv = ('helvetica', -self._size.get()) bold = ('helvetica', -self._size.get(), 'bold') attribs = { 'tree_color': '#000000', 'tree_width': 2, 'node_font': bold, 'leaf_font': helv, } tree = self._parser.tree() self._tree = tree_to_treesegment(canvas, tree, **attribs) self._cframe.add_widget(self._tree, 30, 5) # Draw the text. helv = ('helvetica', -self._size.get()) bottom = y = self._cframe.scrollregion()[3] self._textwidgets = [ TextWidget(canvas, word, font=self._font) for word in self._sent ] for twidget in self._textwidgets: self._cframe.add_widget(twidget, 0, 0) twidget.move(0, bottom - twidget.bbox()[3] - 5) y = min(y, twidget.bbox()[1]) # Draw a line over the text, to separate it from the tree. self._textline = canvas.create_line(-5000, y - 5, 5000, y - 5, dash='.') # Highlight appropriate nodes. self._highlight_nodes() self._highlight_prodlist() # Make sure the text lines up. self._position_text() def _redraw_quick(self): # This should be more-or-less sufficient after an animation. self._highlight_nodes() self._highlight_prodlist() self._position_text() def _highlight_nodes(self): # Highlight the list of nodes to be checked. bold = ('helvetica', -self._size.get(), 'bold') for treeloc in self._parser.frontier()[:1]: self._get(self._tree, treeloc)['color'] = '#20a050' self._get(self._tree, treeloc)['font'] = bold for treeloc in self._parser.frontier()[1:]: self._get(self._tree, treeloc)['color'] = '#008080' def _highlight_prodlist(self): # Highlight the productions that can be expanded. # Boy, too bad tkinter doesn't implement Listbox.itemconfig; # that would be pretty useful here. self._prodlist.delete(0, 'end') expandable = self._parser.expandable_productions() untried = self._parser.untried_expandable_productions() productions = self._productions for index in range(len(productions)): if productions[index] in expandable: if productions[index] in untried: self._prodlist.insert(index, ' %s' % productions[index]) else: self._prodlist.insert(index, ' %s (TRIED)' % productions[index]) self._prodlist.selection_set(index) else: self._prodlist.insert(index, ' %s' % productions[index]) def _position_text(self): # Line up the text widgets that are matched against the tree numwords = len(self._sent) num_matched = numwords - len(self._parser.remaining_text()) leaves = self._tree_leaves()[:num_matched] xmax = self._tree.bbox()[0] for i in range(0, len(leaves)): widget = self._textwidgets[i] leaf = leaves[i] widget['color'] = '#006040' leaf['color'] = '#006040' widget.move(leaf.bbox()[0] - widget.bbox()[0], 0) xmax = widget.bbox()[2] + 10 # Line up the text widgets that are not matched against the tree. for i in range(len(leaves), numwords): widget = self._textwidgets[i] widget['color'] = '#a0a0a0' widget.move(xmax - widget.bbox()[0], 0) xmax = widget.bbox()[2] + 10 # If we have a complete parse, make everything green :) if self._parser.currently_complete(): for twidget in self._textwidgets: twidget['color'] = '#00a000' # Move the matched leaves down to the text. for i in range(0, len(leaves)): widget = self._textwidgets[i] leaf = leaves[i] dy = widget.bbox()[1] - leaf.bbox()[3] - 10.0 dy = max(dy, leaf.parent().label().bbox()[3] - leaf.bbox()[3] + 10) leaf.move(0, dy) def _tree_leaves(self, tree=None): if tree is None: tree = self._tree if isinstance(tree, TreeSegmentWidget): leaves = [] for child in tree.subtrees(): leaves += self._tree_leaves(child) return leaves else: return [tree] ######################################### ## Button Callbacks ######################################### def destroy(self, *e): self._autostep = 0 if self._top is None: return self._top.destroy() self._top = None def reset(self, *e): self._autostep = 0 self._parser.initialize(self._sent) self._lastoper1['text'] = 'Reset Application' self._lastoper2['text'] = '' self._redraw() def autostep(self, *e): if self._animation_frames.get() == 0: self._animation_frames.set(2) if self._autostep: self._autostep = 0 else: self._autostep = 1 self._step() def cancel_autostep(self, *e): #self._autostep_button['text'] = 'Autostep' self._autostep = 0 # Make sure to stop auto-stepping if we get any user input. def step(self, *e): self._autostep = 0 self._step() def match(self, *e): self._autostep = 0 self._match() def expand(self, *e): self._autostep = 0 self._expand() def backtrack(self, *e): self._autostep = 0 self._backtrack() def _step(self): if self._animating_lock: return # Try expanding, matching, and backtracking (in that order) if self._expand(): pass elif self._parser.untried_match() and self._match(): pass elif self._backtrack(): pass else: self._lastoper1['text'] = 'Finished' self._lastoper2['text'] = '' self._autostep = 0 # Check if we just completed a parse. if self._parser.currently_complete(): self._autostep = 0 self._lastoper2['text'] += ' [COMPLETE PARSE]' def _expand(self, *e): if self._animating_lock: return old_frontier = self._parser.frontier() rv = self._parser.expand() if rv is not None: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = rv self._prodlist.selection_clear(0, 'end') index = self._productions.index(rv) self._prodlist.selection_set(index) self._animate_expand(old_frontier[0]) return True else: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = '(all expansions tried)' return False def _match(self, *e): if self._animating_lock: return old_frontier = self._parser.frontier() rv = self._parser.match() if rv is not None: self._lastoper1['text'] = 'Match:' self._lastoper2['text'] = rv self._animate_match(old_frontier[0]) return True else: self._lastoper1['text'] = 'Match:' self._lastoper2['text'] = '(failed)' return False def _backtrack(self, *e): if self._animating_lock: return if self._parser.backtrack(): elt = self._parser.tree() for i in self._parser.frontier()[0]: elt = elt[i] self._lastoper1['text'] = 'Backtrack' self._lastoper2['text'] = '' if isinstance(elt, Tree): self._animate_backtrack(self._parser.frontier()[0]) else: self._animate_match_backtrack(self._parser.frontier()[0]) return True else: self._autostep = 0 self._lastoper1['text'] = 'Finished' self._lastoper2['text'] = '' return False def about(self, *e): ABOUT = ("NLTK Recursive Descent Parser Application\n" + "Written by Edward Loper") TITLE = 'About: Recursive Descent Parser Application' try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def help(self, *e): self._autostep = 0 # The default font's not very legible; try using 'fixed' instead. try: ShowText(self._top, 'Help: Recursive Descent Parser Application', (__doc__ or '').strip(), width=75, font='fixed') except: ShowText(self._top, 'Help: Recursive Descent Parser Application', (__doc__ or '').strip(), width=75) def postscript(self, *e): self._autostep = 0 self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) self._bigfont.configure(size=-(abs(size + 2))) self._redraw() ######################################### ## Expand Production Selection ######################################### def _toggle_grammar(self, *e): if self._show_grammar.get(): self._prodframe.pack(fill='both', side='left', padx=2, after=self._feedbackframe) self._lastoper1['text'] = 'Show Grammar' else: self._prodframe.pack_forget() self._lastoper1['text'] = 'Hide Grammar' self._lastoper2['text'] = '' # def toggle_grammar(self, *e): # self._show_grammar = not self._show_grammar # if self._show_grammar: # self._prodframe.pack(fill='both', expand='y', side='left', # after=self._feedbackframe) # self._lastoper1['text'] = 'Show Grammar' # else: # self._prodframe.pack_forget() # self._lastoper1['text'] = 'Hide Grammar' # self._lastoper2['text'] = '' def _prodlist_select(self, event): selection = self._prodlist.curselection() if len(selection) != 1: return index = int(selection[0]) old_frontier = self._parser.frontier() production = self._parser.expand(self._productions[index]) if production: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = production self._prodlist.selection_clear(0, 'end') self._prodlist.selection_set(index) self._animate_expand(old_frontier[0]) else: # Reset the production selections. self._prodlist.selection_clear(0, 'end') for prod in self._parser.expandable_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) ######################################### ## Animation ######################################### def _animate_expand(self, treeloc): oldwidget = self._get(self._tree, treeloc) oldtree = oldwidget.parent() top = not isinstance(oldtree.parent(), TreeSegmentWidget) tree = self._parser.tree() for i in treeloc: tree = tree[i] widget = tree_to_treesegment(self._canvas, tree, node_font=self._boldfont, leaf_color='white', tree_width=2, tree_color='white', node_color='white', leaf_font=self._font) widget.label()['color'] = '#20a050' (oldx, oldy) = oldtree.label().bbox()[:2] (newx, newy) = widget.label().bbox()[:2] widget.move(oldx - newx, oldy - newy) if top: self._cframe.add_widget(widget, 0, 5) widget.move(30 - widget.label().bbox()[0], 0) self._tree = widget else: oldtree.parent().replace_child(oldtree, widget) # Move the children over so they don't overlap. # Line the children up in a strange way. if widget.subtrees(): dx = (oldx + widget.label().width() / 2 - widget.subtrees()[0].bbox()[0] / 2 - widget.subtrees()[0].bbox()[2] / 2) for subtree in widget.subtrees(): subtree.move(dx, 0) self._makeroom(widget) if top: self._cframe.destroy_widget(oldtree) else: oldtree.destroy() colors = [ 'gray%d' % (10 * int(10 * x / self._animation_frames.get())) for x in range(self._animation_frames.get(), 0, -1) ] # Move the text string down, if necessary. dy = widget.bbox()[3] + 30 - self._canvas.coords(self._textline)[1] if dy > 0: for twidget in self._textwidgets: twidget.move(0, dy) self._canvas.move(self._textline, 0, dy) self._animate_expand_frame(widget, colors) def _makeroom(self, treeseg): """ Make sure that no sibling tree bbox's overlap. """ parent = treeseg.parent() if not isinstance(parent, TreeSegmentWidget): return index = parent.subtrees().index(treeseg) # Handle siblings to the right rsiblings = parent.subtrees()[index + 1:] if rsiblings: dx = treeseg.bbox()[2] - rsiblings[0].bbox()[0] + 10 for sibling in rsiblings: sibling.move(dx, 0) # Handle siblings to the left if index > 0: lsibling = parent.subtrees()[index - 1] dx = max(0, lsibling.bbox()[2] - treeseg.bbox()[0] + 10) treeseg.move(dx, 0) # Keep working up the tree. self._makeroom(parent) def _animate_expand_frame(self, widget, colors): if len(colors) > 0: self._animating_lock = 1 widget['color'] = colors[0] for subtree in widget.subtrees(): if isinstance(subtree, TreeSegmentWidget): subtree.label()['color'] = colors[0] else: subtree['color'] = colors[0] self._top.after(50, self._animate_expand_frame, widget, colors[1:]) else: widget['color'] = 'black' for subtree in widget.subtrees(): if isinstance(subtree, TreeSegmentWidget): subtree.label()['color'] = 'black' else: subtree['color'] = 'black' self._redraw_quick() widget.label()['color'] = 'black' self._animating_lock = 0 if self._autostep: self._step() def _animate_backtrack(self, treeloc): # Flash red first, if we're animating. if self._animation_frames.get() == 0: colors = [] else: colors = ['#a00000', '#000000', '#a00000'] colors += [ 'gray%d' % (10 * int(10 * x / (self._animation_frames.get()))) for x in range(1, self._animation_frames.get() + 1) ] widgets = [self._get(self._tree, treeloc).parent()] for subtree in widgets[0].subtrees(): if isinstance(subtree, TreeSegmentWidget): widgets.append(subtree.label()) else: widgets.append(subtree) self._animate_backtrack_frame(widgets, colors) def _animate_backtrack_frame(self, widgets, colors): if len(colors) > 0: self._animating_lock = 1 for widget in widgets: widget['color'] = colors[0] self._top.after(50, self._animate_backtrack_frame, widgets, colors[1:]) else: for widget in widgets[0].subtrees(): widgets[0].remove_child(widget) widget.destroy() self._redraw_quick() self._animating_lock = 0 if self._autostep: self._step() def _animate_match_backtrack(self, treeloc): widget = self._get(self._tree, treeloc) node = widget.parent().label() dy = (1.0 * (node.bbox()[3] - widget.bbox()[1] + 14) / max(1, self._animation_frames.get())) self._animate_match_backtrack_frame(self._animation_frames.get(), widget, dy) def _animate_match(self, treeloc): widget = self._get(self._tree, treeloc) dy = ((self._textwidgets[0].bbox()[1] - widget.bbox()[3] - 10.0) / max(1, self._animation_frames.get())) self._animate_match_frame(self._animation_frames.get(), widget, dy) def _animate_match_frame(self, frame, widget, dy): if frame > 0: self._animating_lock = 1 widget.move(0, dy) self._top.after(10, self._animate_match_frame, frame - 1, widget, dy) else: widget['color'] = '#006040' self._redraw_quick() self._animating_lock = 0 if self._autostep: self._step() def _animate_match_backtrack_frame(self, frame, widget, dy): if frame > 0: self._animating_lock = 1 widget.move(0, dy) self._top.after(10, self._animate_match_backtrack_frame, frame - 1, widget, dy) else: widget.parent().remove_child(widget) widget.destroy() self._animating_lock = 0 if self._autostep: self._step() def edit_grammar(self, *e): CFGEditor(self._top, self._parser.grammar(), self.set_grammar) def set_grammar(self, grammar): self._parser.set_grammar(grammar) self._productions = list(grammar.productions()) self._prodlist.delete(0, 'end') for production in self._productions: self._prodlist.insert('end', (' %s' % production)) def edit_sentence(self, *e): sentence = " ".join(self._sent) title = 'Edit Text' instr = 'Enter a new sentence to parse.' EntryDialog(self._top, sentence, instr, self.set_sentence, title) def set_sentence(self, sentence): self._sent = sentence.split() #[XX] use tagged? self.reset()
class TextSelect(Frame): def __init__(self, client, anchor, items, destroyAnchor=False): """ Args: client: [SelectionClient] The window that text is returned to. anchor: A window that the text selection popup is created relative to. items: [str], items to display in the listbox. destroyAnchor: [bool] if true, destroy the anchor after positioning the window. """ self.top = Toplevel() self.anchor = anchor self.top.overrideredirect(1) self.top.wm_geometry('+%s+%s' % (anchor.winfo_rootx() + anchor.winfo_x(), anchor.winfo_rooty() + anchor.winfo_y() ) ) super(TextSelect, self).__init__(self.top) self.entry = Entry(self) self.client = client self.items = items self.place(x = 0.5, y = 0.5, height = 100, width = 100) self.entry.bind('<Return>', self.close) self.entry.bind('<KeyPress>', self.filter) self.entry.bind('<Escape>', self.abort) self.entry.bind('<Up>', self.up) self.entry.bind('<Down>', self.down) self.entry.pack() # Create the list of items. self.list = Listbox(self) for item in self.items: self.list.insert('end', item) self.list.pack() self.grid() self.entry.focus() # Reposition the select button against the anchor. We defer this # until after idle so that the anchor has a chance to get rendered. def reposition(*args): self.top.wm_geometry('+%s+%s' % ( anchor.winfo_rootx(), anchor.winfo_rooty()) ) if destroyAnchor: anchor.destroy() self.after_idle(reposition) def close(self, event): sel = self.list.curselection() if sel: item = self.list.get(sel[0]) else: item = self.entry.get() # Note that the order of this appears to be significant: destroying # before selecting leaves the focus in a weird state. self.client.selected(item) self.top.destroy() return 'braek' def abort(self, event): self.top.destroy() self.client.aborted() return 'break' def up(self, event): sel = self.list.curselection() if not sel: self.list.selection_set(0) return 'break' sel = sel[0] print('sel is %s size is %s' % (sel, self.list.size())) if sel > 0: print('setting selection to %s' % sel) self.list.selection_clear(sel) self.list.selection_set(sel - 1) self.list.see(sel) return 'break' def down(self, event): sel = self.list.curselection() if not sel: self.list.selection_set(0) return 'break' sel = sel[0] print('sel is %s size is %s' % (sel, self.list.size())) if sel < self.list.size() - 1: print('setting selection to %s' % (sel + 1)) self.list.selection_clear(sel) self.list.selection_set(sel + 1) self.list.see(sel) return 'break' def filter(self, event): """Filter the listbox based on the contents of the entryfield.""" # first add the character to the entry. currentText = self.entry.get() print(event.keysym) if event.keysym == 'BackSpace': # Handle backspace specially. if currentText: currentText = currentText[:-1] self.entry.delete(0, 'end') self.entry.insert(0, currentText) else: return 'break' else: # Assume normal character. Insert it. self.entry.insert('insert', event.char) currentText += event.char self.list.delete(0, 'end') pattern = currentText.upper() for item in self.items: if pattern in item.upper(): self.list.insert('end', item) return 'break'
class ListboxVidget(Vidget, Eventor): """ ListboxVidget contains a Listbox widget. It adds the following abilities: - Store items of any type, unlike Listbox widget that only stores texts. - Remember selected item even if the listbox widget lost focus. - Notify pre-change and post-change events. """ # Error raised when trying to change the listbox while a change is going on class CircularCallError(ValueError): pass # Error raised when trying to change the listbox while it is disabled class DisabledError(ValueError): pass # Event notified when the listbox's items are to be changed ITEMS_CHANGE_SOON = 'ITEMS_CHANGE_SOON' # Event notified when the listbox's items are changed ITEMS_CHANGE_DONE = 'ITEMS_CHANGE_DONE' # Event notified when the listbox's active item is to be changed ITEMCUR_CHANGE_SOON = 'ITEMCUR_CHANGE_SOON' # Event notified when the listbox's active item is changed ITEMCUR_CHANGE_DONE = 'ITEMCUR_CHANGE_DONE' # Events list EVENTS = ( ITEMS_CHANGE_SOON, ITEMS_CHANGE_DONE, ITEMCUR_CHANGE_SOON, ITEMCUR_CHANGE_DONE, ) def __init__( self, items=None, item_to_text=None, normal_bg='', normal_fg='', active_bg='sky blue', active_fg='white', selected_bg='steel blue', selected_fg='white', master=None, ): """ Initialize object. @param items: Items list. @param item_to_text: Item-to-text function. Default is `str`. @param normal_bg: Unselected item background color. @param normal_fg: Unselected item foreground color. @param active_bg: Active item background color. `Active` means the item is selected (in general meaning) but the listbox has no focus. @param active_fg: Active item foreground color. `Active` means the item is selected (in general meaning) but the listbox has no focus. @param selected_bg: Selected item background color. `Selected` means the item is selected (in general meaning) and the listbox has focus. @param selected_fg: Selected item foreground color. `Selected` means the item is selected (in general meaning) and the listbox has focus. @param master: Master widget. @return: None. """ # Initialize Vidget. # Create main frame widget. Vidget.__init__( self, master=master, ) # Initialize Eventor Eventor.__init__(self) # If items list is given if items is not None: # If items list is not list if not isinstance(items, list): # Raise error raise TypeError(items) # If items list is list. # If items list is not given, or items list is given and is list # Items list self._items = items if items is not None else [] # Item-to-text function. Default is `str`. self._item_to_text = item_to_text if item_to_text is not None else str # Unselected item background color self._normal_fg = normal_fg # Unselected item foreground color self._normal_bg = normal_bg # Active item background color self._active_fg = active_fg # Active item foreground color self._active_bg = active_bg # Selected item background color self._selected_fg = selected_fg # Selected item foreground color self._selected_bg = selected_bg # Whether the listbox is changing self._is_changing = False # Active index. `-1` means void, i.e. no item is active. self._indexcur = -1 # Whether active index is being reset to same value self._is_resetting = False # Create listbox widget self._listbox = Listbox( master=self.widget(), relief='groove', activestyle='none', highlightthickness=0, # Active index cache only supports single-selection mode for now. # See 2N6OR. selectmode='single', ) # Set the listbox widget as config target self.config_target_set(self._listbox) # Create x-axis scrollbar self._scrollbar_xview = _HiddenScrollbar( self.widget(), orient=HORIZONTAL, ) # Create y-axis scrollbar self._scrollbar_yview = _HiddenScrollbar( self.widget(), orient=VERTICAL, ) # Mount scrollbars self._listbox.config(xscrollcommand=self._scrollbar_xview.set) self._listbox.config(yscrollcommand=self._scrollbar_yview.set) self._scrollbar_xview.config(command=self._listbox.xview) self._scrollbar_yview.config(command=self._listbox.yview) # Bind single-click event handler self._listbox.bind('<Button-1>', self._on_single_click) # Bind double-click event handler self._listbox.bind('<Double-Button-1>', self._on_double_click) # Update listbox widget self._listbox_widget_update(keep_active=False) # Update widget self._widget_update() def _widget_update(self): """ Update widget. @return: None. """ # Row 0 for listbox and y-axis scrollbar self.widget().rowconfigure(0, weight=1) # Row 1 for x-axis scrollbar self.widget().rowconfigure(1, weight=0) # Column 0 for listbox and x-axis scrollbar self.widget().columnconfigure(0, weight=1) # Column 1 for y-axis scrollbar self.widget().columnconfigure(1, weight=0) # Lay out listbox self._listbox.grid(row=0, column=0, sticky='NSEW') # Lay out x-axis scrollbar self._scrollbar_xview.grid(row=1, column=0, sticky='EW') # Lay out y-axis scrollbar self._scrollbar_yview.grid(row=0, column=1, sticky='NS') def is_enabled(self): """ Test whether the listbox is enabled. @return: Boolean. """ # Return whether the listbox is enabled return self._listbox.config('state')[4] != DISABLED def is_changing(self): """ Test whether the listbox is changing. @return: Boolean. """ # Return whether the listbox is changing return self._is_changing def is_resetting(self): """ Test whether the listbox is setting active index to the same value. @return: Boolean. """ # Return whether the listbox is setting active index to the same value return self._is_resetting def size(self): """ Get number of items. @return: Number of items. """ # Return number of items return len(self._items) def items(self): """ Get items list. Notice do not change the list outside. @return: Items list. """ # Return items list return self._items def items_set( self, items, notify=True, keep_active=False, ): """ Set items list. Notice do not change the list outside. @param items: Items list. @param notify: Whether notify pre-change and post-change events. @param keep_active: Whether keep or clear active index. @return: None. """ # If the items is not list if not isinstance(items, list): # Raise error raise TypeError(items) # If the items is list. # If the listbox is disabled if not self.is_enabled(): # Raise error raise ListboxVidget.DisabledError() # If the listbox is not disabled. # If the listbox is changing if self._is_changing: # Raise error raise ListboxVidget.CircularCallError() # If the listbox is not changing. # Set changing flag on self._is_changing = True # If notify events if notify: # Notify pre-change event self.handler_notify(self.ITEMS_CHANGE_SOON) # Store the new items self._items = items # Update listbox widget self._listbox_widget_update( keep_active=keep_active ) # If notify events if notify: # Notify post-change event self.handler_notify(self.ITEMS_CHANGE_DONE) # Set changing flag off self._is_changing = False def index_is_valid(self, index): """ Test whether given index is valid. Notice -1 is not valid. @param index: Index to test. @return: Boolean. """ # Test whether given index is valid return 0 <= index and index < self.size() def index_is_valid_or_void(self, index): """ Test whether given index is valid or is -1. @param index: Index to test. @return: Boolean. """ # Test whether given index is valid or is -1 return index == -1 or self.index_is_valid(index) def index_first(self): """ Get the first item's index. @return: First item's index, or -1 if the listbox is empty. """ # Return the first item's index return 0 if self.size() > 0 else -1 def index_last(self): """ Get the last item's index. @return: Last item's index, or -1 if the listbox is empty. """ # Return the last item's index return self.size() - 1 def indexcur(self, internal=False, raise_error=False): """ Get the active index. @param internal: See 2N6OR. @return: The active index. If no active active, either return -1, or raise IndexError if `raise_error` is True. """ # Get active indexes indexcurs = self._indexcurs(internal=internal) # If have active indexes if indexcurs: # Return the first active index return indexcurs[0] # If no active indexes else: # If raise error if raise_error: # Raise error raise IndexError(-1) # If not raise error else: # Return -1 return -1 def _indexcurs(self, internal=False): """ Get active indexes list. 2N6OR @param internal: Whether use listbox widget's selected indexes, instead of cached active index. Notice listbox widget has no selected indexes if it has no focus. Notice using cached active index only supports single-selection mode, which means the result list has at most one index. @return: Active indexes list. """ # If use listbox widget's selected indexes if internal: # Return listbox widget's selected indexes list return [int(x) for x in self._listbox.curselection()] # If not use listbox widget's selected indexes else: # If cached active index is valid if self.index_is_valid(self._indexcur): # Return a list with the cached active index return [self._indexcur] # If cached active index is not valid else: # Return empty list return [] def indexcur_set( self, index, focus=False, notify=True, notify_arg=None, ): """ Set active index. @param index: The index to set. @param focus: Whether set focus on the listbox widget. @param notify: Whether notify pre-change and post-change events. @param notify_arg: Event argument. @return: None. """ # If the index is not valid or -1 if not self.index_is_valid_or_void(index): # Raise error raise IndexError(index) # If the index is valid or is -1. # If the listbox is not enabled if not self.is_enabled(): # Raise error raise ListboxVidget.DisabledError() # If the listbox is enabled. # If the listbox is changing if self._is_changing: # Raise error raise ListboxVidget.CircularCallError() # If the listbox is not changing. # Set changing flag on self._is_changing = True # Get old active index old_indexcur = self._indexcur # Set resetting flag on if new and old indexes are equal self._is_resetting = (index == old_indexcur) # If notify events if notify: # Notify pre-change event self.handler_notify(self.ITEMCUR_CHANGE_SOON, notify_arg) # If old active index is valid if self.index_is_valid(old_indexcur): # Set old active item's background color to normal color self._listbox.itemconfig(old_indexcur, background=self._normal_bg) # Set old active item's foreground color to normal color self._listbox.itemconfig(old_indexcur, foreground=self._normal_fg) # Cache new active index self._indexcur = index # Clear listbox widget's selection self._listbox.selection_clear(0, END) # Set listbox widget's selection self._listbox.selection_set(index) # Set listbox widget's activated index self._listbox.activate(index) # If new active index is valid if index != -1: # Set new active item's background color to active color self._listbox.itemconfig(index, background=self._active_bg) # Set new active item's foreground color to active color self._listbox.itemconfig(index, foreground=self._active_fg) # If set focus if focus: # Set focus on the listbox widget self._listbox.focus_set() # If new active index is valid if index != -1: # Make the active item visible self._listbox.see(index) # If notify events if notify: # Notify post-change event self.handler_notify(self.ITEMCUR_CHANGE_DONE, notify_arg) # Set resetting flag off self._is_resetting = False # Set changing flag off self._is_changing = False def indexcur_set_by_event( self, event, focus=False, notify=True, notify_arg=None, ): """ Set active index using a Tkinter event object that contains coordinates of the active item. @param event: Tkinter event object. @param focus: Whether set focus on the listbox widget. @param notify: Whether notify pre-change and post-change events. @param notify_arg: Event argument. @return: None. """ # Get the event's y co-ordinate's nearest listbox item index index = self._listbox.nearest(event.y) # If the index is not valid if not self.index_is_valid_or_void(index): # Ignore the event return # If the index is valid else: # Set the index as active index self.indexcur_set( index=index, focus=focus, notify=notify, notify_arg=notify_arg, ) def item(self, index): """ Get item at given index. @return: Item at given index, or IndexError if the index is not valid. """ return self.items()[index] def itemcur(self, internal=False, raise_error=False): """ Get the active item. @param internal: See 2N6OR. @param raise_error: Whether raise error if no active item. @return: The active item. If no active item, if `raise_error` is True, raise IndexError, otherwise return None. """ # Get active index. # May raise IndexError if `raise_error` is True. indexcur = self.indexcur( internal=internal, raise_error=raise_error, ) # If no active index if indexcur == -1: # Return None return None # If have active index else: # Return the active item return self.items()[indexcur] def item_insert( self, item, index=None, notify=True, keep_active=True, ): """ Insert item at given index. @param item: Item to insert. @param index: Index to insert. `None` means active index, and if no active index, insert at the end. @param notify: Whether notify pre-change and post-change events. @param keep_active: Whether keep or clear active index. @return: None. """ # If notify events if notify: # Notify pre-change events self.handler_notify(self.ITEMCUR_CHANGE_SOON) self.handler_notify(self.ITEMS_CHANGE_SOON) # Get old active index active_index = self.indexcur() # If the index is None, # it means use active index. if index is None: # Use active index. # `-1` works and means appending. index = active_index # Insert the item to the items list self._items.insert(index, item) # If old active index is valid if active_index != -1: # If old active index is GE the inserted index if active_index >= index: # Shift active index by one active_index += 1 # If old active index is not GE the inserted index, use it as-is. # Set new active index self.indexcur_set(index=active_index, notify=False) # Update listbox widget self._listbox_widget_update( keep_active=keep_active ) # If notify events if notify: # Notify post-change events self.handler_notify(self.ITEMS_CHANGE_DONE) self.handler_notify(self.ITEMCUR_CHANGE_DONE) def item_remove( self, index, notify=True, keep_active=True, ): """ Remove item at given index. @param index: Index to remove. @param notify: Whether notify pre-change and post-change events. @param keep_active: Whether keep or clear active index. @return: None. """ # If the index is not valid if not self.index_is_valid(index): # Raise error raise ValueError(index) # If the index is valid. # If notify events if notify: # Notify pre-change events self.handler_notify(self.ITEMCUR_CHANGE_SOON) self.handler_notify(self.ITEMS_CHANGE_SOON) # Get old active index active_index = self.indexcur() # Remove item at the index del self._items[index] # If old active index is valid if active_index != -1: # Get the last index index_last = self.index_last() # If old active index is GT the last index if active_index > index_last: # Use the last index as new active index active_index = index_last # If old active index is not GT the last index, use it as-is. # Set new active index self.indexcur_set(index=active_index, notify=False) # Update listbox widget self._listbox_widget_update( keep_active=keep_active ) # If notify events if notify: # Notify post-change events self.handler_notify(self.ITEMS_CHANGE_DONE) self.handler_notify(self.ITEMCUR_CHANGE_DONE) def handler_add( self, event, handler, need_arg=False, ): """ Add event handler for an event. If the event is ListboxVidget event, add the event handler to Eventor. If the event is not ListboxVidget event, add the event handler to listbox widget. Notice this method overrides `Eventor.handler_add` in order to add non-ListboxVidget event handler to listbox widget. @param event: Event name. @param handler: Event handler. @param need_arg: Whether the event handler needs event argument. @return: None. """ # If the event is ListboxVidget event if event in self.EVENTS: # Add the event handler to Eventor return Eventor.handler_add( self, event=event, handler=handler, need_arg=need_arg, ) # If the event is not ListboxVidget event, # it is assumed to be Tkinter widget event. else: # Add the event handler to listbox widget return self.bind( event=event, handler=handler, ) def bind( self, event, handler, ): """ Add event handler to listbox widget. ListboxVidget internally uses `<Button-1>` and `<Double-Button-1>` to capture active index changes. So if the given event is `<Button-1>` or `<Double-Button-1>`, the given handler will be wrapped. @param event: Event name. @param handler: Event handler. @return: None. """ # If the event is not `<Button-1>` or `<Double-Button-1>` if event not in ['<Button-1>', '<Double-Button-1>']: # Add the event handler to listbox widget self._listbox.bind(event, handler) # If the event is `<Button-1>` or `<Double-Button-1>` else: # Create event handler wrapper def handler_wrapper(e): """ Event handler wrapper that sets new active index and then calls the wrapped event handler. Setting new active index is needed because when this handler is called by Tkinter, the active index of the listbox is still old. @param e: Tkinter event object. @return: None. """ # Set new active index self.indexcur_set_by_event(e, notify=True) # Call the wrapped event handler handler(e) # Add the event handler wrapper to the listbox widget self._listbox.bind(event, handler_wrapper) def _on_single_click(self, event): """ `<Button-1>` event handler that updates active index. @param event: Tkinter event object. @return: None. """ # Updates active index self.indexcur_set_by_event(event, notify=True) def _on_double_click(self, event): """ `<Double-Button-1>` event handler that updates active index. @param event: Tkinter event object. @return: None. """ # Updates active index self.indexcur_set_by_event(event, notify=True) def _listbox_widget_update( self, keep_active, ): """ Update listbox widget's items and selection. @param keep_active: Whether keep or clear active index. @return: None. """ # Remove old items from listbox widget self._listbox.delete(0, END) # Insert new items into listbox widget. # For each ListboxVidget items. for index, item in enumerate(self.items()): # Get item text item_text = self._item_to_text(item) # Insert the item text into listbox widget self._listbox.insert(index, item_text) # Set the item's normal background color self._listbox.itemconfig(index, background=self._normal_bg) # Set the item's normal foreground color self._listbox.itemconfig(index, foreground=self._normal_fg) # Set the item's selected background color self._listbox.itemconfig(index, selectbackground=self._selected_bg) # Set the item's selected foreground color self._listbox.itemconfig(index, selectforeground=self._selected_fg) # If keep active index if keep_active: # Use old active index indexcur = self._indexcur # If not keep active index else: # Set active index to -1 indexcur = self._indexcur = -1 # Clear old selection self._listbox.selection_clear(0, END) # Set new selection. # `-1` works. self._listbox.selection_set(indexcur) # Set new active index. # `-1` works. self._listbox.activate(indexcur) # If new active index is valid if indexcur != -1: # Set active background color self._listbox.itemconfig(indexcur, background=self._active_bg) # Set active foreground color self._listbox.itemconfig(indexcur, foreground=self._active_fg) # Make the active item visible self._listbox.see(indexcur)
class AutocompleteEntry(Entry): def __init__(self, *args, **kwargs): Entry.__init__(self, width=100, *args, **kwargs) self.focus_set() self.pack() self.var = self["textvariable"] if self.var == '': self.var = self["textvariable"] = StringVar() self.var.trace('w', self.changed) self.bind("<Right>", self.selection) self.bind("<Up>", self.up) self.bind("<Down>", self.down) self.bind("<Return>", self.enter) self.lb_up = False self.lb = None def enter(self, event): print(event) def changed(self, name, index, mode): if self.var.get() == '': if self.lb: self.lb.destroy() self.lb_up = False else: words = self.comparison() if words: if not self.lb_up: self.lb = Listbox(master=root, width=100) self.lb.bind("<Double-Button-1>", self.selection) self.lb.bind("<Right>", self.selection) self.lb.place(x=self.winfo_x(), y=self.winfo_y()+self.winfo_height()) self.lb_up = True self.lb.delete(0, END) for w in words: self.lb.insert(END,w) else: if self.lb_up: self.lb.destroy() self.lb_up = False def selection(self, _): if self.lb_up: self.var.set(self.lb.get(ACTIVE)) self.lb.destroy() self.lb_up = False self.icursor(END) def up(self, _): if self.lb_up: if self.lb.curselection() == (): index = '0' else: index = self.lb.curselection()[0] if index != '0': self.lb.selection_clear(first=index) index = str(int(index)-1) self.lb.selection_set(first=index) self.lb.activate(index) def down(self, _): if self.lb_up: if self.lb.curselection() == (): index = '0' else: index = self.lb.curselection()[0] if index != END: self.lb.selection_clear(first=index) index = str(int(index)+1) self.lb.selection_set(first=index) self.lb.activate(index) def comparison(self): q = self.var.get() q = str(q.decode('utf8')) for hit in searcher.search(qp.parse(q), limit=50): if hit['author']: yield '%s. "%s"' % (hit['author'], hit['title']) else: yield hit['title']
class Editor: # Info Defaults LOG_FILE = 'editor.log' devices = [] vidPK = [] vidPATH = [] vidNAME = [] uuid = [] uuidFK = [] def __init__(self, master, soundGenerator, rfidScanner): self.environment = Environment() self.soundProvider = SoundProvider(soundGenerator) self.configureScannerProvider(rfidScanner) self.load() frame = Frame(master) frame.pack() self.activeCardNumber = StringVar() self.usbSpin = StringVar() self.usbSpin.set(self.environment.Usb) Label(frame, text='RFID Card').grid(row=0, column=0) Label(frame, text='Video').grid(row=0, column=2) self.ee = Entry(frame, textvariable=self.activeCardNumber, state=DISABLED, disabledforeground='black') self.ee.grid(row=1, column=0) self.r = Button(frame, text='Read Card', command=self.startCardProcess) self.r.grid(row=1, column=1) self.box = Listbox(frame) for entry in self.vidNAME: self.box.insert(END, entry) self.box.bind("<<ListboxSelect>>", self.newselection) self.box.grid(row=1, rowspan=5, column=2, columnspan=2) self.scroll = Scrollbar(self.box, orient=VERTICAL) self.box.config(yscrollcommand=self.scroll.set) self.scroll.config(command=self.box.yview) Button(frame, text='Assign Kill Code', command=self.createKiller).grid(row=2, column=0) Label(frame, text='Source USB').grid(row=4, column=0) self.spin = Spinbox(frame, values=self.devices) self.spin.delete(0, END) self.spin.insert(0, self.environment.Usb) self.spin.grid(row=5, column=0) self.status = Button(frame, text='Update Device Repository', command=self.updateDevice, disabledforeground='blue') self.status.grid(row=6, column=0) Button(frame, text='Save', command=self.save).grid(row=6, column=2) Button(frame, text='Quit', command=self.closeWithSavePrompt).grid( row=6, column=3) def configureScannerProvider(self, rfidScanner): provider = RFIDScannerProvider(rfidScanner) self.RFIDScannerProvider = provider.PN532( int(self.environment.CHIP_SELECT_PIN), int(self.environment.MASTER_OUTPUT_SLAVE_INPUT_PIN), int(self.environment.MASTER_INPUT_SLAVE_OUTPUT_PIN), int(self.environment.SERIAL_CLOCK_PIN)) def closeWithSavePrompt(self): ans = messagebox.askquestion( 'Save And Quit', 'Would you like to save your changes?') if ans == 'yes': self.save() sys.exit(0) def startCardProcess(self): # Disable button to prevent event stacking self.r.config(state=DISABLED) self.processCard() self.r.config(state=NORMAL) def processCard(self): # Scans RFID cards and sets them to text box try: self.processCardUnchecked() except Exception as e: self.displayScanError(e) def processCardUnchecked(self): cardScan = CardScanWrapper(self.soundS, self.RFIDScannerProvider) cardScan.runScan() self.processResult(cardScan.getFormattedResult()) def processResult(self, scanResult): if scanResult == None: return self.activeCardNumber.set(scanResult) # Populate text box self.deselectActiveListboxItems() self.linkCardWithListbox(scanResult) def deselectActiveListboxItems(self): # De-select any active items in listbox self.box.selection_clear(0, END) def linkCardWithListbox(self, scanResult): index = self.verifyCard(scanResult) if str(self.uuidFK[index]) == self.environment.KillCommand: messagebox.showinfo( 'Kill Card', 'This card is currently assigned to kill the application.') return self.highlightItemInListbox(index) def highlightItemInListbox(self, index): try: i = self.vidPK.index(self.uuidFK[index]) except: messagebox.showinfo('Card Unassigned', 'Card is not currently assigned to a video') else: self.box.see(i) self.box.selection_clear(0, END) self.box.selection_set(i) self.box.activate(i) def verifyCard(self, uidt): try: uuidIndex = self.uuid.index(uidt) except: uuidIndex = self.addNewCard(uidt) return uuidIndex def addNewCard(self, uidt): self.uuid.append(uidt) self.uuidFK.append(-1) newIndex = len(self.uuid) - 1 return newIndex def displayScanError(self, e): messagebox.showerror('Error Occurred', 'Error: ' + str(e)) logging.error('Scan Failed: ' + str(e)) def save(self): toSaveList = self.makePairedList( self.vidPK, self.vidNAME, self.vidPATH) self.safeSaveToFile(self.environment.VideoList, toSaveList) toSaveList = self.makePairedList(self.uuid, self.uuidFK) self.safeSaveToFile(self.environment.UuidTable, toSaveList) def makePairedList(self, *itemLists): stop = len(itemLists) subList = [] listAll = [] for listIndex in range(len(itemLists[0])): del subList[:] for indice in range(stop): subList.append(itemLists[indice][listIndex]) listAll.append(list(subList)) return listAll def safeSaveToFile(self, fileName, pairList): try: self.writePairedListToTempFile(fileName, pairList) except Exception as e: logging.error('Failed to create video list: ' + str(e)) else: self.replaceOriginalFileWithItsTemp(fileName) def replaceOriginalFileWithItsTemp(self, fileName): try: if os.path.isfile(fileName): os.remove(fileName) os.rename(fileName + '.temp', fileName) except Exception as e: logging.error('Failed to replace old video list: ' + str(e)) def writePairedListToTempFile(self, fileName, pairedList): f = open(fileName + '.temp', 'w') self.writePairedListGivenFile(f, pairedList) f.close() def writePairedListGivenFile(self, f, pairedList): i = 0 while(i < len(pairedList) - 1): self.writeSingleLineOfPairedListToOpenFile(f, pairedList, i) f.write('\n') i = i+1 self.writeSingleLineOfPairedListToOpenFile(f, pairedList, i) def writeSingleLineOfPairedListToOpenFile(self, f, pairedList, itemIndex): fLine = "" for item in range(len(pairedList[itemIndex])): fLine = fLine + str(pairedList[itemIndex][item]) + ',' f.write(fLine[:-1]) def updateDevice(self): scan = self.safeScan() if scan != None: self.safeProcessScan(scan) self.status.config(text='Update Device Repository', state=NORMAL) self.status.update_idletasks() def safeScan(self): scan = None try: scan = self.runScannerWithNotification() except Exception as e: self.showScanErrorMessage(e) return scan def runScannerWithNotification(self): self.status.config(text='Scanning...', state=DISABLED) self.status.update_idletasks() scan = ScriptedFileSearch(subprocess) scan.scan("scanner.sh") return scan def showScanErrorMessage(self, e): messagebox.showerror('Scan Failed', 'Scan error: ' + str(e)) logging.error(str(e)) def safeProcessScan(self, scan): try: self.processScan(scan) except Exception as e: self.showErrorMessage(e) def showErrorMessage(self, e): messagebox.showerror('Error', 'Error: ' + str(e)) logging.error(str(e)) def refreshListBox(self): self.box.delete(0, END) for entry in self.vidNAME: self.box.insert(END, entry) def processScan(self, scan): # Check if a scan turned up any results if self.scanIsEmpty(scan): self.showAbortScanMessage() return self.verifyUSB() self.processScanFiles(scan) self.refreshListBox() def showAbortScanMessage(self): messagebox.showwarning( 'No Files Found', 'A scan failed to find any files.') logging.warning('Empty Scan occurred when attempting a merge') def scanIsEmpty(self, scan): return len(scan.NAME) == 0 def verifyUSB(self): if self.environment.Usb != self.spin.get(): self.Usb = self.spin.get() self.environment.update() def processScanFiles(self, scan): i = 0 j = 0 newPK = [] newName = [] newPath = [] self.status.config(text='Reading Files...') self.status.update_idletasks() # Iterate through list while i < len(scan.NAME): # Verifiy File try: if scan.PATH[i].find(self.environment.Usb) >= 0: # File resides on repository - update FK try: # Locate matching entry fkk = self.vidNAME.index(scan.NAME[i]) except Exception as e: # No matching entry logging.info('New file found in repository: ' + str(e)) pass else: # Update FK on uuid table for uu in self.uuidFK: if uu == self.vidPK[fkk]: uu = scan.PK[i] # Store entry in new Tables newPK.append(scan.PK[i]) newName.append(scan.NAME[i]) newPath.append(scan.PATH[i]) else: # Video resides on updating device - check if file already copied found = False while j < len(scan.NAME): if str(scan.NAME[i]) == str(scan.NAME[j]) and scan.PATH[j].find(self.environment.Usb) >= 0: found = True break j = j + 1 if not found: # Copy file and append try: # Get device name device = scan.PATH[i].replace('/media/pi/', '') device = device[0:device.find('/')] # Copy self.status.config( text='Copying ' + scan.NAME[i] + '...') self.status.update_idletasks() shutil.copyfile( scan.PATH[i], scan.PATH[i].replace(device, self.environment.Usb)) except Exception as e: logging.error('Failed to copy' + scan.NAME[i] + ': ' + str(e)) else: # Add to new array newPK.append(scan.PK[i]) newName.append(scan.NAME[i]) newPath.append( scan.PATH[i].replace(device, self.environment.Usb)) except Exception as e: logging.error(str(e)) i = i+1 del self.vidNAME[:] del self.vidPATH[:] del self.vidPK[:] self.vidNAME = newName self.vidPATH = newPath self.vidPK = newPK def newselection(self, event): # Fires when a new item is selected in the listbox selection = event.widget.curselection() try: txt = self.ee.get() if txt == '': return i = self.uuid.index(txt) self.uuidFK[i] = self.vidPK[selection[0]] except Exception as e: messagebox.showerror('Error During Set', 'Error: ' + str(e)) logging.error(str(e)) def createKiller(self): try: self.assignCurrentCardAsKiller() self.box.selection_clear(0, END) except Exception as e: self.handleCardNotScannedError(e) def assignCurrentCardAsKiller(self): i = self.uuid.index(self.ee.get()) self.uuidFK[i] = int(self.environment.KillCommand) def handleCardNotScannedError(self, e): messagebox.showinfo( 'Card Not Scanned', 'Please scan a card to assign it a [Kill Application] code.' + str(e)) logging.error(str(e)) def load(self): # Generate Log logging.basicConfig(filename=self.LOG_FILE, level=logging.INFO) # Load Sound file self.soundProvider.init() self.soundProvider.mixer.pre_init(44100, -16, 12, 512) # pygame.init() IS this only for linux distribution? self.soundS = self.soundProvider.mixer.Sound(self.environment.SCAN_SOUND) self.soundS.set_volume(1) # Create an instance of the PN532 class. self.RFIDScannerProvider.begin()) # Configure PN532 to communicate with MiFare cards. self.RFIDScannerProvider.SAM_configuration() self.loadFiles() self.locateDevices() self.loadDevice() def loadFiles(self): self.readCSV(self.environment.VideoList, (int, self.vidPK), (str, self.vidNAME), (str, self.vidPATH)) self.readCSV(self.environment.UuidTable, (str, self.uuid), (int, self.uuidFK))
class Combobox_Autocomplete(Entry, object): def __init__(self, master, list_of_items=None, autocomplete_function=None, listbox_width=None, listbox_height=7, ignorecase_match=False, startswith_match=True, vscrollbar=True, hscrollbar=True, **kwargs): if hasattr(self, "autocomplete_function"): if autocomplete_function is not None: raise ValueError( "Combobox_Autocomplete subclass has 'autocomplete_function' implemented") else: if autocomplete_function is not None: self.autocomplete_function = autocomplete_function else: if list_of_items is None: raise ValueError( "If not given complete function, list_of_items can't be 'None'") elif ignorecase_match: if startswith_match: def matches_function(entry_data, item): return item.startswith(entry_data) else: def matches_function(entry_data, item): return item in entry_data self.autocomplete_function = lambda entry_data: [ item for item in self.list_of_items if matches_function(entry_data, item)] else: if startswith_match: def matches_function(escaped_entry_data, item): if re.match(escaped_entry_data, item, re.IGNORECASE): return True else: return False else: def matches_function(escaped_entry_data, item): if re.search(escaped_entry_data, item, re.IGNORECASE): return True else: return False def autocomplete_function2(entry_data): escaped_entry_data = re.escape(entry_data) return [item for item in self.list_of_items if matches_function(escaped_entry_data, item)] self.autocomplete_function = autocomplete_function2 self._listbox_height = int(listbox_height) self._listbox_width = listbox_width self.list_of_items = list_of_items self._use_vscrollbar = vscrollbar self._use_hscrollbar = hscrollbar kwargs.setdefault("background", "white") if "textvariable" in kwargs: self._entry_var = kwargs["textvariable"] else: self._entry_var = kwargs["textvariable"] = StringVar() Entry.__init__(self, master, **kwargs) self._trace_id = self._entry_var.trace('w', self._on_change_entry_var) self._listbox = None self.bind("<Tab>", self._on_tab) self.bind("<Up>", self._previous) self.bind("<Down>", self._next) self.bind('<Control-n>', self._next) self.bind('<Control-p>', self._previous) self.bind("<Return>", self._update_entry_from_listbox) self.bind("<Escape>", lambda event: self.unpost_listbox()) def _on_tab(self, event): self.post_listbox() return "break" def _on_change_entry_var(self, name, index, mode): entry_data = self._entry_var.get() if entry_data == '': self.unpost_listbox() self.focus() else: values = self.autocomplete_function(entry_data) if values: if self._listbox is None: self._build_listbox(values) else: self._listbox.delete(0, END) height = min(self._listbox_height, len(values)) self._listbox.configure(height=height) for item in values: self._listbox.insert(END, item) else: self.unpost_listbox() self.focus() def _build_listbox(self, values): listbox_frame = Frame() self._listbox = Listbox(listbox_frame, background="white", selectmode=SINGLE, activestyle="none", exportselection=False) self._listbox.grid(row=0, column=0, sticky=N+E+W+S) self._listbox.bind("<ButtonRelease-1>", self._update_entry_from_listbox) self._listbox.bind("<Return>", self._update_entry_from_listbox) self._listbox.bind("<Escape>", lambda event: self.unpost_listbox()) self._listbox.bind('<Control-n>', self._next) self._listbox.bind('<Control-p>', self._previous) if self._use_vscrollbar: vbar = Scrollbar(listbox_frame, orient=VERTICAL, command=self._listbox.yview) vbar.grid(row=0, column=1, sticky=N+S) self._listbox.configure( yscrollcommand=lambda f, l: autoscroll(vbar, f, l)) elif self._use_hscrollbar: hbar = Scrollbar(listbox_frame, orient=HORIZONTAL, command=self._listbox.xview) hbar.grid(row=1, column=0, sticky=E+W) self._listbox.configure( xscrollcommand=lambda f, l: autoscroll(hbar, f, l)) listbox_frame.grid_columnconfigure(0, weight=1) listbox_frame.grid_rowconfigure(0, weight=1) x = -self.cget("borderwidth") - self.cget("highlightthickness") y = self.winfo_height()-self.cget("borderwidth") - \ self.cget("highlightthickness") elif self._listbox_width: width = self._listbox_width else: width = self.winfo_width() listbox_frame.place(in_=self, x=x, y=y, width=width) height = min(self._listbox_height, len(values)) self._listbox.configure(height=height) for item in values: self._listbox.insert(END, item) def post_listbox(self): if self._listbox is not None: return entry_data = self._entry_var.get() if entry_data == '': return values = self.autocomplete_function(entry_data) if values: self._build_listbox(values) def unpost_listbox(self): if self._listbox is not None: self._listbox.master.destroy() self._listbox = None def get_value(self): return self._entry_var.get() def set_value(self, text, close_dialog=False): self._set_var(text) if close_dialog: self.unpost_listbox() self.icursor(END) self.xview_moveto(1.0) def _set_var(self, text): self._entry_var.trace_vdelete("w", self._trace_id) self._entry_var.set(text) self._trace_id = self._entry_var.trace('w', self._on_change_entry_var) def _update_entry_from_listbox(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if current_selection: text = self._listbox.get(current_selection) self._set_var(text) self._listbox.master.destroy() self._listbox = None self.focus() self.icursor(END) self.xview_moveto(1.0) return "break" def _previous(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if len(current_selection) == 0: self._listbox.selection_set(0) self._listbox.activate(0) else: index = int(current_selection[0]) self._listbox.selection_clear(index) if index == 0: index = END else: index -= 1 self._listbox.see(index) self._listbox.selection_set(first=index) self._listbox.activate(index) return "break" def _next(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if len(current_selection) == 0: self._listbox.selection_set(0) self._listbox.activate(0) else: index = int(current_selection[0]) self._listbox.selection_clear(index) if index == self._listbox.size() - 1: index = 0 else: index += 1 self._listbox.see(index) self._listbox.selection_set(index) self._listbox.activate(index) return "break"
class Window: FONT = ('Monospace', 11) ITEM_HEIGHT = 22 MAX_FOUND = 10 BG_COLOR = '#202b3a' FG_COLOR = '#ced0db' def resize(self, items): if self.resized: return self.root.geometry('{0}x{1}'.format( self.width, self.height + items * Window.ITEM_HEIGHT)) self.resized = True def __init__(self, root, width, height): self.root = root self.width = width self.height = height self.all_windows = [] self.resized = False # master.geometry(500) root.title("window switcher") root.resizable(width=False, height=False) root.configure(background=Window.BG_COLOR) # ugly tkinter code below sv = StringVar() sv.trace("w", lambda name, index, mode, sv=sv: self.on_entry(sv)) self.main_entry = Entry(root, font=Window.FONT, width=1000, textvariable=sv, bg=Window.BG_COLOR, fg=Window.FG_COLOR, insertbackground=Window.FG_COLOR, bd=0) self.main_entry.grid(row=0, column=0, padx=10) self.main_entry.focus_set() self.listbox = Listbox(root, height=Window.ITEM_HEIGHT, font=Window.FONT, highlightthickness=0, borderwidth=0, bg=Window.BG_COLOR, fg=Window.FG_COLOR, selectbackground='#2c3c51', selectforeground='#cedaed') self.listbox.grid(row=1, column=0, sticky='we', padx=10, pady=10) # key bindings self.main_entry.bind('<Control-a>', self.select_all) self.main_entry.bind('<Up>', self.select_prev) self.main_entry.bind('<Down>', self.select_next) self.main_entry.bind('<Return>', self.select_window) self.root.bind('<Escape>', lambda e: sys.exit()) # self.resize(Window.MAX_FOUND) self.initial_get(None) def initial_get(self, event): self.all_windows = get_windows() self.find_windows('') def select_all(self, event): # select text self.main_entry.select_clear() self.main_entry.select_range(0, 'end') # move cursor to the end self.main_entry.icursor('end') return 'break' def find_windows(self, text): text = text.lower() found = [ window for window in self.all_windows if window['name'].find(text) != -1 ] # print(found) self.found = found self.listbox.delete(0, 'end') for i, item in enumerate(found): if i >= Window.MAX_FOUND: break self.listbox.insert('end', item['name']) self.resize(min(len(found), Window.MAX_FOUND)) # select first element self.listbox.selection_set(0) def select_next(self, event): if len(self.found) == 0: return idx = self.listbox.curselection()[0] max = self.listbox.size() idx += 1 if idx >= max: idx = 0 self.listbox.selection_clear(0, 'end') self.listbox.selection_set(idx) def select_prev(self, event): if len(self.found) == 0: return idx = self.listbox.curselection()[0] max = self.listbox.size() idx -= 1 if idx < 0: idx = max - 1 self.listbox.selection_clear(0, 'end') self.listbox.selection_set(idx) def select_window(self, event): idx = self.listbox.curselection()[0] id = self.found[idx]['id'] # switch to window and exit # wmctrl -ia <id`> subprocess.call(['wmctrl', '-ia', id]) # print(subprocess.check_output(['wmctrl', '-ia', id]).decode('utf-8')) sys.exit(0) def on_entry(self, newtext): search_test = newtext.get() self.find_windows(search_test) return True
class AutocompleteEntry(Entry): def __init__(self, autocompleteList, *args, **kwargs): # Listbox length if 'listboxLength' in kwargs: self.listboxLength = kwargs['listboxLength'] del kwargs['listboxLength'] else: self.listboxLength = 8 # Custom matches function if 'matchesFunction' in kwargs: self.matchesFunction = kwargs['matchesFunction'] del kwargs['matchesFunction'] else: def matches(fieldValue, acListEntry): pattern = re.compile('.*' + re.escape(fieldValue) + '.*', re.IGNORECASE) return re.match(pattern, acListEntry) self.matchesFunction = matches Entry.__init__(self, *args, **kwargs) self.focus() self.autocompleteList = autocompleteList self.var = self["textvariable"] if self.var == '': self.var = self["textvariable"] = StringVar() self.var.trace('w', self.changed) self.bind("<Right>", self.selection) self.bind("<Up>", self.moveUp) self.bind("<Down>", self.moveDown) self.listboxUp = False def changed(self, name, index, mode): if self.var.get() == '': if self.listboxUp: self.listbox.destroy() self.listboxUp = False else: words = self.comparison() if words: if not self.listboxUp: self.listbox = Listbox(width=self["width"], height=self.listboxLength) self.listbox.bind("<Button-1>", self.selection) self.listbox.bind("<Right>", self.selection) self.listbox.place(x=self.winfo_x(), y=self.winfo_y() + self.winfo_height()) self.listboxUp = True self.listbox.delete(0, END) for w in words: self.listbox.insert(END, w) else: if self.listboxUp: self.listbox.destroy() self.listboxUp = False def selection(self, event): if self.listboxUp: self.var.set(self.listbox.get(ACTIVE)) self.listbox.destroy() self.listboxUp = False self.icursor(END) def moveUp(self, event): if self.listboxUp: if self.listbox.curselection() == (): index = '0' else: index = self.listbox.curselection()[0] if index != '0': self.listbox.selection_clear(first=index) index = str(int(index) - 1) self.listbox.see(index) # Scroll! self.listbox.selection_set(first=index) self.listbox.activate(index) def moveDown(self, event): if self.listboxUp: if self.listbox.curselection() == (): index = '0' else: index = self.listbox.curselection()[0] if index != END: self.listbox.selection_clear(first=index) index = str(int(index) + 1) self.listbox.see(index) # Scroll! self.listbox.selection_set(first=index) self.listbox.activate(index) def comparison(self): return [ w for w in self.autocompleteList if self.matchesFunction(self.var.get(), w) ]
class Application(Frame): """Container class, encapsulates app""" # this class inherits from Tkinter.parent def __init__(self, path, master=None): """Constructor""" # call parent class constructor Frame.__init__(self, master) self.path = path self.image_filenames = find_image_files(path) # necessary to make the application actually appear on the screen self.grid(sticky=N + S + E + W) # PIL image, for loading from file and for resizing self.image_pil = None # Tk photoimage, for display self.image_tk = None # list of coords (2-element lists) of current selection's pts self.points_orig = [] # points_canvas is 'points_orig', in canvas coordinates self.points_canvas = [] # x- and y-coords of displayed image in canvas coordinate frame self.x_offset = -1 self.y_offset = -1 # font in listbox text self.font = Font(family='Helvetica', size=10, weight='normal') # crosshair line size , as fraction of # min(displayed-imagewidth, displayed-image-height) self.crosshair_fraction = 0.05 # color for drawing first crosshair - the origin self.crosshair1_color = 'red' # color for drawing second crosshair - (together with the first # crosshair, these two points define a coordinate frame) - self.crosshair2_color = 'green' # color for drawing third and on crosshairs - all points other # than the first and second, have this color self.crosshair3_color = 'cyan' # the width, in pixels, of crosshairs self.crosshair_thickness = 2 # length of crosshair (updated upon display) self.crosshair_radius = -1 # the scale of currently displayed image (updated upon display) self.image_scaling = 1.0 # create all widges and set their initial conditions self.create_widgets() def create_widgets(self): """Set up all application graphics""" # get the top level winddow top = self.winfo_toplevel() # set the title of the top level window top.title('Image Point Tagging Tool') # make row 0 of the top level window's grid stretchable top.rowconfigure(0, weight=1) # make column 0 of the top level window's grid stretchable top.columnconfigure(0, weight=1) # bind keys for entire app top.bind_all('<Up>', self.select_prev) top.bind_all('<Down>', self.select_next) # make row 0 of Application's widget's grid stretchable self.rowconfigure(0, weight=1) # make column 0 of Application's widget's grid stretchable self.columnconfigure(0, weight=1) self.canvas = Canvas(self, bg='gray') self.canvas.grid(row=0, column=0, rowspan=2, sticky=N + S + E + W) self.canvas.rowconfigure(0, weight=1) self.canvas.columnconfigure(0, weight=1) # bind resize events (need -4 here bec. event gives 4+(real_size)) self.canvas.bind( '<Configure>', lambda e, s=self: s.on_resize_canvas(e.width - 2, e.height - 2)) # bind canvas mouse clicks self.canvas.bind('<Button-1>', self.on_click_button1) self.canvas.bind('<Button-3>', self.on_click_button3) # create scrollbars self.scrollbar_x = Scrollbar(self, orient=HORIZONTAL, width=10) self.scrollbar_y = Scrollbar(self, orient=VERTICAL, width=10) self.scrollbar_x.grid(row=1, column=1, columnspan=2, sticky=E + W) self.scrollbar_y.grid(row=0, column=3, sticky=N + S) # create lb for showing labeled/not-labeled images self.listbox_marks = Listbox(self, width=1, takefocus=0, exportselection=0, font=self.font) self.listbox_marks.grid(row=0, column=1, sticky=N + S + E + W) # create lb for showing image filenames self.lisbox_filenames = Listbox(self, width=30, selectmode=SINGLE, xscrollcommand=self.scrollbar_x.set, yscrollcommand=self.scrollbar_y.set, exportselection=0, font=self.font) self.lisbox_filenames.grid(row=0, column=2, sticky=N + S + E + W) # bind scrollbar movement self.scrollbar_x['command'] = self.lisbox_filenames.xview self.scrollbar_y['command'] = self.on_scrollbar_y # bind left mouse click selection self.lisbox_filenames.bind( '<Button-1>', lambda e, s=self: s.select(self.lisbox_filenames.nearest(e.y))) self.listbox_marks.bind( '<Button-1>', lambda e, s=self: s.select(self.lisbox_filenames.nearest(e.y))) # bind wheel scroll self.lisbox_filenames.bind( '<Button-4>', lambda e, s=self: on_mousewheel(self.listbox_marks, 4)) self.lisbox_filenames.bind( '<Button-5>', lambda e, s=self: on_mousewheel(self.listbox_marks, 5)) self.listbox_marks.bind( '<Button-4>', lambda e, s=self: on_mousewheel(self.lisbox_filenames, 4)) self.listbox_marks.bind( '<Button-5>', lambda e, s=self: on_mousewheel(self.lisbox_filenames, 5)) # skip is # of chars to skip in path string so that only the # part of the path that was not supplied is displayed skip = len(self.path) if self.path[skip - 1] != '/': skip += 1 # insert image filenames plus marks into lists and # select first image that does not have pts file i = 0 index_of_image_with_no_pts_file = -1 for image_filename in self.image_filenames: self.lisbox_filenames.insert(END, image_filename[skip:]) if self.has_pts_file(i): self.listbox_marks.insert(END, '+') else: self.listbox_marks.insert(END, '') if index_of_image_with_no_pts_file < 0: index_of_image_with_no_pts_file = i i += 1 if index_of_image_with_no_pts_file < 0: self.select(0) else: self.select(index_of_image_with_no_pts_file) def on_scrollbar_y(self, *args): """Vertical scrollbar motion callback""" apply(self.lisbox_filenames.yview, args) apply(self.listbox_marks.yview, args) def on_click_button1(self, event): """Button 1 click callback: adds a crosshair at click location""" if self.coord_in_img(event.x, event.y): point = [(event.x - self.x_offset) / self.image_scaling, (event.y - self.y_offset) / self.image_scaling] point_scaled = [float(event.x), float(event.y)] self.points_orig.append(point) self.points_canvas.append(point_scaled) if len(self.points_orig) == 1: self.mark_labeled() self.on_resize_canvas(int(self.canvas['width']), int(self.canvas['height'])) self.save_points() def on_click_button3(self, event): """Button 3 click callback: deletes landmark near click location""" if not self.coord_in_img(event.x, event.y): return i = self.find_point_near_crosshair(event.x, event.y) if i >= 0: del self.points_orig[i] del self.points_canvas[i] if len(self.points_orig) == 0: self.mark_unlabeled() self.on_resize_canvas(int(self.canvas['width']), int(self.canvas['height'])) self.save_points() def select(self, i): """Select the i'th image to work with - make current selection = i""" # uncomment the following line if you are only dealing with # faces that have three points labeled on them and you want to # automatically reorder a previously tagged database so that # the person's right eye is the first point, left eye is # second point and mouth is third point self.sort_points() self.lisbox_filenames.selection_clear(0, END) self.listbox_marks.selection_clear(0, END) self.lisbox_filenames.selection_set(i) self.listbox_marks.selection_set(i) self.lisbox_filenames.see(i) self.listbox_marks.see(i) self.image_pil = PIL.Image.open(self.get_image_filename()) self.points_orig = self.read_pts_file() self.on_resize_canvas(int(self.canvas['width']), int(self.canvas['height'])) def select_prev(self, *args): #pylint: disable=unused-argument """Select entry that comes before current selection""" i = self.get_selected_index() if i > 0: self.select(i - 1) def select_next(self, *args): #pylint: disable=unused-argument """Select entry that comes after current selection""" i = self.get_selected_index() if i < len(self.image_filenames) - 1: self.select(i + 1) def on_resize_canvas(self, width, height): """Called when canvas is resized""" if width <= 0 or height <= 0: return # maximize image width or height depending on aspect ratios image_width = self.image_pil.size[0] image_height = self.image_pil.size[1] image_aspect_ratio = float(image_width) / float(image_height) self.canvas['width'] = width self.canvas['height'] = height canvas_width = int(self.canvas['width']) canvas_height = int(self.canvas['height']) canvas_aspect_ratio = float(canvas_width) / float(canvas_height) if image_aspect_ratio < canvas_aspect_ratio: new_image_width = int(image_aspect_ratio * float(canvas_height)) new_image_height = canvas_height else: new_image_width = canvas_width new_image_height = int(float(canvas_width) / image_aspect_ratio) self.image_tk = PhotoImage( self.image_pil.resize((new_image_width, new_image_height), PIL.Image.BILINEAR)) self.x_offset = 0.5 * (float(canvas_width) - float(new_image_width)) self.y_offset = 0.5 * (float(canvas_height) - float(new_image_height)) self.crosshair_radius = 0.5 * self.crosshair_fraction * float( min(new_image_width, new_image_height)) self.canvas.delete('image') self.canvas.create_image(self.x_offset, self.y_offset, anchor=NW, image=self.image_tk, tags='image') width_scale = float(new_image_width) / float(image_width) height_scale = float(new_image_height) / float(image_height) self.image_scaling = 0.5 * (width_scale + height_scale) self.points_canvas = [[ x[0] * self.image_scaling + self.x_offset, x[1] * self.image_scaling + self.y_offset ] for x in self.points_orig] self.redraw_points() def redraw_points(self): """redraw points in current entry's .pts file""" self.canvas.delete('line') # draw first crosshair in color1 if len(self.points_canvas) > 0: point1 = self.points_canvas[0] self.draw_crosshair(point1[0], point1[1], self.crosshair1_color) # draw second crosshair in color2 if len(self.points_canvas) > 1: point2 = self.points_canvas[1] self.draw_crosshair(point2[0], point2[1], self.crosshair2_color) # draw third or higher crosshair in color3 if len(self.points_canvas) > 2: for point in self.points_canvas[2:]: self.draw_crosshair(point[0], point[1], self.crosshair3_color) def draw_crosshair(self, x_coord, y_coord, fill_color): """Draw a cross at (x_coord, y_coord) in the currently selected image""" start_x = x_coord - self.crosshair_radius start_y = y_coord - self.crosshair_radius end_x = x_coord + self.crosshair_radius end_y = y_coord + self.crosshair_radius min_x = self.x_offset min_y = self.y_offset max_x = self.x_offset + self.image_tk.width() - 1 max_y = self.y_offset + self.image_tk.height() - 1 if start_x < min_x: start_x = min_x if start_y < min_y: start_y = min_y if end_x > max_x: end_x = max_x if end_y > max_y: end_y = max_y self.canvas.create_line(x_coord, start_y, x_coord, end_y, width=self.crosshair_thickness, tags='line', fill=fill_color) self.canvas.create_line(start_x, y_coord, end_x, y_coord, width=self.crosshair_thickness, tags='line', fill=fill_color) def get_selected_index(self): """Returns index of current selection""" return int(self.lisbox_filenames.curselection()[0]) def coord_in_img(self, x_coord, y_coord): """Returns whether (x_coord, y_coord) is inside the shown image""" return (x_coord >= self.x_offset and y_coord >= self.y_offset and x_coord < self.x_offset + self.image_tk.width() and y_coord < self.y_offset + self.image_tk.height()) def find_point_near_crosshair(self, x_coord, y_coord): """Returns index of landmark point near (x_coord, y_coord), or -1""" i = 0 i_min = -1 min_dist = self.image_tk.width() + self.image_tk.height() for pair in self.points_canvas: x_dist = x_coord - pair[0] y_dist = y_coord - pair[1] dist = sqrt(x_dist * x_dist + y_dist * y_dist) if dist <= self.crosshair_radius and dist < min_dist: i_min = i i += 1 return i_min def save_points(self): """Save current points to pts file""" # remove whatever was there before if self.has_pts_file(): os.remove(self.get_pts_filename()) # save current result if len(self.points_orig) > 0: filehandle = open(self.get_pts_filename(), 'w') for pair in self.points_orig: message = str(pair[0]) + ', ' + str(pair[1]) + '\n' filehandle.write(message) filehandle.close() def sort_points(self): """ Reorder points, assuming face labeling, so that the first point is always the person's right eye, the second point is the person's left eye and the third point is the mouth. NB: this function only (destructively) works on self.points_orig """ if len(self.points_orig) != 3: return # step 1 sort the points according to y-value self.points_orig.sort(key=lambda pt: pt[1]) # step 2: from the top-most two points, call the leftmost one # the person's right eye and call the other the person's left eye if self.points_orig[0][0] > self.points_orig[1][0]: # swap first and second points' x-coordinate tmp = self.points_orig[0][0] self.points_orig[0][0] = self.points_orig[1][0] self.points_orig[1][0] = tmp # swap first and second points' y-coordinate tmp = self.points_orig[0][1] self.points_orig[0][1] = self.points_orig[1][1] self.points_orig[1][1] = tmp # order changed, so re-save self.save_points() def has_pts_file(self, i=None): """Returns whether (i'th) selection has a pts file with landmarks""" if i is None: i = self.get_selected_index() return os.path.exists(self.get_pts_filename(i)) def get_pts_filename(self, i=None): """Returns filename of selected (or i'th) .pts file""" if i is None: i = self.get_selected_index() image_filename = self.image_filenames[i] return os.path.splitext(image_filename)[0] + '.pts' def get_image_filename(self, i=None): """Returns filename of (i'th) selection's image""" if i is None: i = self.get_selected_index() return self.image_filenames[i] def read_pts_file(self, i=None): """Returns list of points (lists) in (i'th) selection's .pts file""" if i is None: i = self.get_selected_index() if self.has_pts_file(i): filehandle = open(self.get_pts_filename(i), 'r') lines = filehandle.readlines() filehandle.close() return [[float(pair[0]), float(pair[1])] for pair in [line.split(',') for line in lines]] else: return [] def mark_labeled(self, i=None): """Mark (i'th) selection as having a .pts file""" if i is None: i = self.get_selected_index() self.listbox_marks.insert(i, '+') self.listbox_marks.delete(i + 1) self.listbox_marks.selection_set(i) def mark_unlabeled(self, i=None): """Unmark (i'th) selection as having a .pts file""" if i is None: i = self.get_selected_index() self.listbox_marks.insert(i, '') self.listbox_marks.delete(i + 1) self.listbox_marks.selection_set(i)
class TagWindow: def __init__(self, parent): self.window = Toplevel(parent.window) self.window.withdraw() self.parent = parent self.tags = deepcopy(parent.tags) self.activeFrame = Frame(self.window) self.upDownButtonFrame = Frame(self.window) self.inactiveFrame = Frame(self.window) self.okCancelFrame = Frame(self.window) self.selectedActive = None self.selectedInactive = None self.activeListbox = None self.inactiveListbox = None WindowHelper.initializeWindow(self.window, self.parent, 460, 514, 30, 50, LabelConstants.TAG_WINDOW) self.gridFrames() self.addActiveListbox() self.addMoveButtons() self.addInactiveListbox() self.addOkCancelButtons() self.populateListboxes() WindowHelper.finalizeWindow(self.window, self.parent) def gridFrames(self): self.activeFrame.grid(row=0, sticky=NSEW, padx=4, pady=4) self.upDownButtonFrame.grid(row=1, sticky=NSEW, padx=4, pady=4) self.inactiveFrame.grid(row=2, sticky=NSEW, padx=4, pady=4) self.okCancelFrame.grid(row=3, sticky=E, padx=4, pady=4) def addActiveListbox(self): labelExplain = Label(self.activeFrame, text=LabelConstants.EXTENDED_MODE) labelExplain.grid(row=0, column=0, sticky=W, padx=4, pady=(0,4)) labelActiveListbox = Label(self.activeFrame, text=LabelConstants.ACTIVE_TAGS) labelActiveListbox.grid(row=1, column=0, sticky=W, padx=4, pady=4) scrollbarActive = Scrollbar(self.activeFrame) scrollbarActive.grid(row=2, column=1, sticky="NWS") self.activeListbox = Listbox(self.activeFrame, selectmode=EXTENDED, yscrollcommand=scrollbarActive.set, activestyle=NONE, width=70) scrollbarActive.config(command=self.activeListbox.yview) self.activeListbox.bind('<<ListboxSelect>>', self.onSelectActiveListbox) self.activeListbox.grid(row=2, column=0, sticky=NSEW, padx=(4, 0)) def addMoveButtons(self): buttonUp = Button(self.upDownButtonFrame, text=LabelConstants.UP, width=13, command=lambda: self.moveUp()) buttonUp.grid(row=0, column=0, sticky=NSEW, padx=(116, 4), pady=4) buttonDown = Button(self.upDownButtonFrame, text=LabelConstants.DOWN, width=13, command=lambda: self.moveDown()) buttonDown.grid(row=0, column=1, sticky=NSEW, padx=(20, 4), pady=4) def addInactiveListbox(self): labelInactiveListbox = Label(self.inactiveFrame, text=LabelConstants.INACTIVE_TAGS) labelInactiveListbox.grid(row=0, column=0, sticky=W, padx=4, pady=4) scrollbarInactive = Scrollbar(self.inactiveFrame) scrollbarInactive.grid(row=1, column=1, sticky="NWS") self.inactiveListbox = Listbox(self.inactiveFrame, selectmode=EXTENDED, yscrollcommand=scrollbarInactive.set, activestyle=NONE, width=70) scrollbarInactive.config(command=self.inactiveListbox.yview) self.inactiveListbox.bind('<<ListboxSelect>>', self.onSelectInactiveListbox) self.inactiveListbox.grid(row=1, column=0, sticky=NSEW, padx=(4, 0)) def addOkCancelButtons(self): buttonUpdateTags = Button(self.okCancelFrame, text=LabelConstants.UPDATE_TAGS, width=26, command=lambda: self.updateTagLibrary()) buttonUpdateTags.grid(row=0, column=1, sticky=E, padx=4, pady=4) buttonOk = Button(self.okCancelFrame, text=LabelConstants.OK, width=13, command=lambda: self.ok()) buttonOk.grid(row=0, column=2, sticky=E, padx=4, pady=4) buttonCancel = Button(self.okCancelFrame, text=LabelConstants.CANCEL, width=13, command=lambda: self.close()) buttonCancel.grid(row=0, column=3, sticky=E, padx=(4, 0), pady=4) def populateListboxes(self): self.activeListbox.delete(0, END) self.inactiveListbox.delete(0, END) descriptions = [] for tag in filter(lambda t: t.isActive, self.tags): descriptions.append(tag.localizationNames["en-us"]) for desc in sorted(descriptions, key=str.casefold): self.activeListbox.insert(END, desc) descriptions = [] for tag in filter(lambda t: not t.isActive, self.tags): descriptions.append(tag.localizationNames["en-us"]) for desc in sorted(descriptions, key=str.casefold): self.inactiveListbox.insert(END, desc) def updateTagLibrary(self): UpdatingTwitchTagsWindow(self) self.populateListboxes() def ok(self): for tag in self.tags: if tag.localizationNames["en-us"] in self.inactiveListbox.get(0, END): tag.isActive = False else: tag.isActive = True self.close() def close(self): self.parent.setTags(self.tags) self.window.destroy() def moveUp(self): if self.selectedInactive: for tag in self.selectedInactive: alphabeticallyInsert(self.activeListbox, self.inactiveListbox.get(tag)) for tag in reversed(self.selectedInactive): self.inactiveListbox.delete(tag) self.inactiveListbox.selection_clear(0, END) self.selectedInactive = None def moveDown(self): if self.selectedActive: for tag in self.selectedActive: alphabeticallyInsert(self.inactiveListbox, self.activeListbox.get(tag)) for tag in reversed(self.selectedActive): self.activeListbox.delete(tag) self.activeListbox.selection_clear(0, END) self.selectedActive = None def onSelectActiveListbox(self, event): w = event.widget self.selectedActive = w.curselection() def onSelectInactiveListbox(self, event): w = event.widget self.selectedInactive = w.curselection()
class AutomateCenter(Toplevel): def __init__(self, parent, dir=""): super().__init__(parent, height=100) self.parent = parent self.attributes('-topmost', 'true') self.resizable(width=False, height=False) self.title('Automate Center') self.dir = dir self.files = [] self.filesToInsert = [] print('Current dir received by Automate Center: ' + self.dir) for file in os.listdir(self.dir): self.files.append( os.path.join(self.dir, file).replace('\\', '/').replace('//', '/')[2:]) self.lbl = Label(self, text = ' Select the files for which you \nwish' \ + ' to automate PPT Insert' ) self.lbl.pack() selectFrame = Frame(self) selectFrame.pack(pady=5) self.selectAllButton = Button(selectFrame, text="ALL") self.selectNoneButton = Button(selectFrame, text="NONE") self.selectAllButton.grid(row=0, column=1, padx=2) self.selectNoneButton.grid(row=0, column=0, padx=2) self.selectAllButton.bind('<ButtonRelease-1>', self.onAllClick) self.selectNoneButton.bind('<ButtonRelease-1>', self.onNoneClick) self.lb = Listbox(self, selectmode=MULTIPLE) for i in self.files: self.lb.insert(END, i) self.lb.bind("<<ListboxSelect>>", self.onSelect) self.lb.pack(pady=5) self.cancelBtn = Button(self, text='Cancel', width=12, height=1) self.cancelBtn.pack() self.okBtn = Button(self, text='OK', width=12, height=1) self.okBtn.pack(pady=2) self.okBtn.bind('<ButtonRelease-1>', self.onOkClick) self.cancelBtn.bind('<ButtonRelease-1>', self.onCancelClick) self.NABtn = Button(self, text='No Automation', width=12, height=1) self.NABtn.pack(pady=2) self.NABtn.bind('<ButtonRelease-1>', self.onNAClick) def onCancelClick(self, event): self.destroy() def onNAClick(self, event): log.writeEvent('Cancel Automate') self.parent.automate = False self.parent.slides.append(self.parent.blank) self.destroy() def onOkClick(self, event): if self.filesToInsert == []: return for idx in range(len(self.files)): if idx in self.filesToInsert: self.parent.slides.append(PPTImage('OS/' + self.files[idx])) self.parent.show.configure(image=self.parent.slides[-1].original) self.parent.show.image = self.parent.slides[-1].original self.parent.populate() self.destroy() def onSelect(self, event): self.filesToInsert = list(self.lb.curselection()) def onAllClick(self, event): self.filesToInsert = [] self.lb.selection_set(0, END) for idx in range(len(self.files)): self.filesToInsert.append(idx) def onNoneClick(self, event): self.lb.selection_clear(0, END) self.filesToInsert = []
class Window2(Frame): def __init__(self, parent, context): super(Window2, self).__init__(parent.Frame) context.add_observer(self) self.parent = parent self.context = context self.controller = parent.controller self._initialize() self.build() def _initialize(self): self.id = 'task' #画面遷移の順番を示す指示物 self.unit_name_list = [] def update(self, msg): self._gid = msg['gid'] self.unit_name_list = _global_group[msg['gid'] - 1] self._update_unit_name_list() self.task_lbx.delete(0, END) def convert(self): units = self._task_box.get(0, END) if not units: self.context.notify('root', 'info', "처리할 작업이 없습니다.") return working_dir = self.context.get('directory') if not working_dir: self.context.directory() working_dir = self.context.get('directory') if not working_dir: messagebox.showwarning("경고", '작업디렉토리가 설정되어 있지 않습니다.') return # html변환여부 체크옵션 _html = self.context.get('html') _gid = self._gid try: self.controller.convert(working_dir, _html, units, _gid) self.context.notify('root', 'info', "작업완료") messagebox.showinfo("알림", message="작업이 완료되었습니다.") except Exception as e: print(e) messagebox.showerror('포맷오류', '잘못된 포맷을 적용하였습니다.') self.context.notify('root', 'info', "작업오류") def build(self): componets = self.component_list() tasks = self.task_list() componets.pack(side="left", fill='y') componets.config(bg='steel blue3') tasks.pack(side='right', fill='both', expand=True) tasks.config(bg='light grey', padx=3) def _update_unit_name_list(self): self._list.delete(0, END) for i, name in enumerate(self.unit_name_list): self._list.insert(i, name) def component_list(self): #sandy brown fr1 = Frame(self) scrollbar = Scrollbar(fr1) scrollbar.pack(side="right", fill="y") self._list = Listbox(fr1, bg="dim grey", fg="white", width=20, yscrollcommand=scrollbar.set) mb1 = Menubutton(fr1, text='선택메뉴', relief='flat', bg='steel blue3') mb1.menu = Menu(mb1, tearoff=0) mb1['menu'] = mb1.menu mb1.menu.add_command(label='등록', command=self.choose_all) mb1.pack(side='bottom') mb1.menu.add_command(label='선택', command=lambda: self._list.select_set(0, END)) mb1.menu.add_command( label='해제', command=lambda: self._list.selection_clear(0, 'end')) self._list.pack(anchor="center", fill="both", expand=True, padx=3, pady=3) scrollbar.config(command=self._list.yview) self._list.config(highlightcolor='green', font=('나눔고딕', 10), activestyle='none', selectmode='extended') self._list.bind('<<ListboxSelect>>', self.select_item) self._list.bind('<Button-3>', self.sub_menu1) self._list.exportselection = 0 return fr1 def select_item(self, event): self.clear_submenu() widget = event.widget #print("select item",widget.curselection()) # if isinstance(widget,Listbox): v = widget.curselection() t = [widget.get(i) for i in v] self.context.notify('root', 'info', t) def clear_submenu(self): if hasattr(self, 'sub_fr'): self.sub_fr.destroy() def _setActivate(self, obj, index): obj.selection_set(index) # obj.see(index) # obj.activate(index) # obj.selection_anchor(index) def sub_menu1(self, event): self.clear_submenu() x, y = event.x, event.y self._setActivate(self._list, self._list.nearest(y)) self.sub_fr = Frame(self, height=10) b1 = Button(self.sub_fr, text='reg', command=self.choose_task, relief='flat') b1.pack(side='top', fill='both') self.sub_fr.place(x=x + 15, y=y) def choose_all(self): self.clear_submenu() for el in self._list.get(0, END): if not self._isduplicate(el): self._task_box.insert(END, el) def choose_task(self): self.clear_submenu() ixs = self._list.curselection() for ix in ixs: el = self._list.get(ix) if not self._isduplicate(el): self._task_box.insert(END, el) def _isduplicate(self, txt): for v in list(self._task_box.get(0, 'end')): if v == txt: return True return False def task_list(self): fr = Frame(self) fr_top = Frame(fr) task_lbx = Listbox(fr_top) task_lbx.pack(anchor="center", fill="both", expand=True, padx=3, pady=3) self._task_box = task_lbx fr_bot = Frame(fr, height='2') b1 = Menubutton(fr_bot, text='실행메뉴') b1.menu = Menu(b1, tearoff=0) b1['menu'] = b1.menu b1.menu.add_command(label='실행', command=self.convert) b1.menu.add_command(label='비우기', command=self.delete_task_item) b1.pack() fr_top.pack(side='top', fill='both', expand=True) fr_bot.pack(side='bottom', fill='x') task_lbx.config(highlightcolor='green', font=('굴림체', 10), activestyle='none', selectmode='extended') self.task_lbx = task_lbx return fr def delete_task_item(self): v = self.task_lbx.curselection() #print(v) if not v: self.task_lbx.delete(0, END) elif len(v) == 1: self.task_lbx.delete(v) else: self.task_lbx.delete(v[0], v[-1])
class BanqueGraphique(Tk): """Classe principale de l'interface de la banque. Nous héritons de "Tk", la classe représentant la fenêtre principale d'une application tkinter. Attributes: banque (Banque): La banque liée à l'interface compte_selectionne (Compte): Référence vers le compte actuellement sélectionnée dans l'interface. """ def __init__(self): # On appelle le constructeur de la classe Tk. super().__init__() # On crée une instance de la banque. On pourra donc utiliser ses attributs et méthodes avec self.banque.attribut, self.banque.methode, etc. self.banque = Banque() # On crée également un attribut qui servira à se "rappeler" du compte sélectionné. self.compte_selectionne = None # Manière intéressante de changer la police de caractères pour tous les widgets de l'interface. self.option_add("*Font", "DejaVu") # On change le titre de la fenêtre. La méthode "title" est une méthode de la classe mère (Tk). self.title("Banque") # Liste des clients. Mettre à jour en appelant la méthode mise_a_jour_interface(). Label(self, text="Clients").grid(row=0, column=0, padx=10, pady=10) self.widget_clients = Listbox(self, exportselection=False) self.widget_clients.grid(row=1, column=0, padx=10, pady=10) # On lie l'événement de sélection à la méthode qui affiche les comptes du client. self.widget_clients.bind('<<ListboxSelect>>', self.mettre_a_jour_comptes) # Liste des comptes pour un client donné. Label(self, text="Comptes").grid(row=0, column=1, padx=10, pady=10) self.widget_comptes = Listbox(self, exportselection=False) self.widget_comptes.grid(row=1, column=1, padx=10, pady=10) # On lie l'événement de sélection à la méthode qui affiche le contenu du compte. self.widget_comptes.bind('<<ListboxSelect>>', self.mettre_a_jour_info_compte) # Contenu d'un compte. Label(self, text="Gestion du compte").grid(row=0, column=3, padx=10, pady=10) cadre_compte = Frame(self) cadre_compte.grid(row=1, column=3, padx=10, pady=10) Label(cadre_compte, text="Numéro: ").grid(row=0, column=0, padx=10, sticky=E) Label(cadre_compte, text="Type: ").grid(row=1, column=0, padx=10, sticky=E) Label(cadre_compte, text="Solde: ").grid(row=2, column=0, padx=10, sticky=E) self.entree_numero = Entry(cadre_compte, state="readonly") self.entree_numero.grid(row=0, column=1) self.entree_type = Entry(cadre_compte, state="readonly") self.entree_type.grid(row=1, column=1) self.entree_solde = Entry(cadre_compte, state="readonly") self.entree_solde.grid(row=2, column=1) # Boutons de dépôt et retrait. Non disponibles tant que nous n'avons pas sélectionné un compte. cadre_boutons = Frame(cadre_compte) cadre_boutons.grid(row=3, column=1, pady=10) self.bouton_depot = Button(cadre_boutons, text="Dépôt", state=DISABLED, command=self.deposer) self.bouton_depot.grid(row=3, column=0) self.bouton_retrait = Button(cadre_boutons, text="Retrait", state=DISABLED, command=self.retirer) self.bouton_retrait.grid(row=3, column=1) # On ajoute les clients de la banque (pour tests seulement). # Dans la "vraie vie", on pourrait par exemple avoir un fichier (ou une base de données) contenant # ces informations et les charger à l'ouverture de la fenêtre. self.initialiser_clients_bidons() # On remplit l'interface avec des clients bidon. self.mettre_a_jour_interface() def initialiser_clients_bidons(self): """Ajoute des clients bidon à la banque. Dans la "vraie vie", on ajouterait la possiblité de sauvegarder et charger les informations d'une banque à partir d'un fichier, par exemple. """ c_1 = Client("Jane Doe") c_1.ajouter_compte(1) c_1.ajouter_compte(2) c_1.deposer_dans_compte(1, 1000) c_1.retirer_dans_compte(2, 500) c_1.deposer_dans_compte(2, 250.25) c_2 = Client("John Doe") c_2.ajouter_compte(1) c_2.deposer_dans_compte(1, 10000) self.banque.ajouter_client(c_1) self.banque.ajouter_client(c_2) self.banque.recuperer_client("Jane Doe").ajouter_compte_sans_dette(3) self.banque.recuperer_client("Jane Doe").deposer_dans_compte(3, 1000) def mettre_a_jour_interface(self): """Méthode appelée pour mettre à jour les différents éléments de l'interface en fonction du contenu de la banque, puis de ce qui est sélectionné dans l'interface. """ # Mise à jour de la liste de clients. self.widget_clients.delete(0, END) for client in self.banque.clients: self.widget_clients.insert(END, client.nom) # On déselectionne. self.widget_clients.selection_clear(0, END) # On vide la liste des comptes. self.widget_comptes.delete(0, END) # On vide le contenu de la gestion du compte self.desactiver_gestion_compte() def mettre_a_jour_comptes(self, evenement=None): """Met à jour la liste des comptes affichés. Réinitialise le compte affiché. Args: evenement (Event): Objet reçu de la part de tkinter. Nous n'en avons pas besoin, alors on met sa valeur par défaut à None. """ # On récupère quel client est sélectionné. index_clique = int(self.widget_clients.curselection()[0]) valeur = self.widget_clients.get(index_clique) # Mise à jour de la liste de comptes (en fonction du client sélectionné) self.widget_comptes.delete(0, END) client = self.banque.recuperer_client(valeur) for numero, compte in client.comptes.items(): self.widget_comptes.insert(END, str(numero)) # On désactive la gestion du compte. self.desactiver_gestion_compte() def desactiver_gestion_compte(self): """Désactive les éléments d'interface liés à la gestion du compte, et remet à None le compte sélectionné. """ self.compte_selectionne = None self.entree_numero['state'] = NORMAL self.entree_type['state'] = NORMAL self.entree_solde['state'] = NORMAL self.entree_numero.delete(0, END) self.entree_type.delete(0, END) self.entree_solde.delete(0, END) self.entree_numero['state'] = 'readonly' self.entree_type['state'] = 'readonly' self.entree_solde['state'] = 'readonly' self.bouton_depot['state'] = DISABLED self.bouton_retrait['state'] = DISABLED def mettre_a_jour_info_compte(self, evenement=None): """En fonction des autres éléments d'interface, met à jour le contenu lié au compte sélectionné. Met à jour l'attribut self.compte_selectionne pour usage futur. Args: evenement (Event): Objet reçu de la part de tkinter. Nous n'en avons pas besoin, alors on met sa valeur par défaut à None. """ # On récupère quel client et quel compte est sélectionné. index_client = self.widget_clients.curselection()[0] nom_client = self.widget_clients.get(index_client) index_compte = int(self.widget_comptes.curselection()[0]) numero_compte = int(self.widget_comptes.get(index_compte)) # On récupère l'objet de type Compte, et on place sa référence dans L'attribut # self.compte_selectionne. Note: modifier le compte via cet attribut modifiera # également le compte dans la hiérarchie d'objets de la banque! objet_compte = self.banque.recuperer_client( nom_client).comptes[numero_compte] self.compte_selectionne = objet_compte # Gestion des éléments d'interface. self.entree_numero['state'] = NORMAL self.entree_type['state'] = NORMAL self.entree_solde['state'] = NORMAL self.entree_numero.delete(0, END) self.entree_type.delete(0, END) self.entree_solde.delete(0, END) self.entree_numero.insert(END, numero_compte) self.entree_type.insert(END, objet_compte.__class__.__name__) self.entree_solde.insert(END, objet_compte.solde) self.entree_numero['state'] = 'readonly' self.entree_type['state'] = 'readonly' self.entree_solde['state'] = 'readonly' # On active les boutons de dépôt et de retrait self.bouton_depot['state'] = NORMAL self.bouton_retrait['state'] = NORMAL def deposer(self): """Effectue un dépôt dans le compte sélectionné. """ # On demande le montant à l'utilisateur. fenetre_depot = FenetreDepotRetrait(self, sorte="dépôt") self.wait_window(fenetre_depot) # On fait le dépôt dans le compte sélectionné. if self.compte_selectionne is not None: # TODO: Lorsque nous connaîtrons la gestion des erreurs, # TODO: nous pourrons faire des validations supplémentaires ici. self.compte_selectionne.deposer(fenetre_depot.valeur) # On met à jour l'interface. self.mettre_a_jour_info_compte(evenement=None) def retirer(self): """Effectue un retrait dans le compte sélectionné. """ # On demande le montant à l'utilisateur. fenetre_retrait = FenetreDepotRetrait(self, sorte="retrait") self.wait_window(fenetre_retrait) # On fait le dépôt dans le compte sélectionné. if self.compte_selectionne is not None: # TODO: Lorsque nous connaîtrons la gestion des erreurs, # TODO: nous pourrons faire des validations supplémentaires ici. self.compte_selectionne.retirer(fenetre_retrait.valeur) # On met à jour l'interface. self.mettre_a_jour_info_compte(evenement=None)
class DMselector(ttk.Frame): """Listbox for DM Creation, Selection, and Sorting.""" def __init__(self, master, conflict): """Initialize DM selection/creation Widget.""" ttk.Frame.__init__(self, master) self.conflict = conflict self.dms = conflict.decisionMakers # variables self.listVariable = StringVar( value=tuple(['Double Click to Add Item'])) self.selIdx = None self.selectedDM = None # widgets self.label = ttk.Label(self, text="Decision Makers") self.dmListDisp = Listbox(self, listvariable=self.listVariable) self.scrollY = ttk.Scrollbar(self, orient=VERTICAL, command=self.dmListDisp.yview) self.upBtn = ttk.Button(self, width=10, text='Up', command=self.upCmd) self.downBtn = ttk.Button(self, width=10, text='Down', command=self.downCmd) self.delBtn = ttk.Button(self, width=10, text='Delete', command=self.delCmd) # configuration self.dmListDisp.configure(yscrollcommand=self.scrollY.set) self.columnconfigure(0, weight=1) self.rowconfigure(1, weight=1) self.label.grid(column=0, row=0, columnspan=5, sticky=NSEW) self.dmListDisp.grid(column=0, row=1, columnspan=5, sticky=NSEW) self.scrollY.grid(column=5, row=1, sticky=NSEW) self.upBtn.grid(column=2, row=2, sticky=NSEW) self.downBtn.grid(column=3, row=2, sticky=NSEW) self.delBtn.grid(column=4, row=2, sticky=NSEW) self.dmListDisp.bind('<<ListboxSelect>>', self.selChgCmd) self.dmListDisp.bind('<Double-1>', self.editCmd) self.dmListDisp.bind('<Delete>', self.delCmd) self.dmListDisp.bind('<Return>', self.editCmd) def refresh(self, event=None): """Refresh widget contents.""" self.updateList() for idx in range(len(self.dms)): self.dmListDisp.itemconfigure(idx, foreground='black') self.dmListDisp.itemconfigure(len(self.dms), foreground='#A0A0A0') def updateList(self, event=None): """Update the value in the displayed listVariable.""" self.listVariable.set( tuple(self.dms.names() + ['Double Click to Add Item'])) def moveEntry(self, idx, idx2): """Move an item from idx to idx2.""" self.dms.insert(idx2, self.dms.pop(idx)) self.updateList() self.dmListDisp.selection_clear(idx) self.dmListDisp.selection_set(idx2) self.selChgCmd() self.event_generate("<<dmOptChg>>") def upCmd(self, event=None): """Move the selected element up one space in the list.""" idx = self.selIdx # check that there is an entry selected, and it isn't the first entry. if idx not in [-1, 0, len(self.dms)]: self.moveEntry(idx, idx - 1) def downCmd(self, event=None): """Move the selected element down one space in the list.""" idx = self.selIdx # check that there is an entry selected, and it isn't the last entry if idx not in [-1, len(self.dms) - 1, len(self.dms)]: self.moveEntry(idx, idx + 1) def delCmd(self, *args): """Delete the selected element from the list.""" idx = self.selIdx if idx != len(self.dms): # check that a valid entry is selected del self.dms[idx] self.refresh() self.event_generate("<<dmOptChg>>") def selChgCmd(self, *args): """Called when the selection changes.""" self.selIdx = int(self.dmListDisp.curselection()[0]) if self.selIdx != len(self.conflict.decisionMakers): self.selectedDM = self.conflict.decisionMakers[self.selIdx] else: self.selectedDM = None self.event_generate('<<DMselected>>') def editCmd(self, *args): """Called when a list entry is selected for editing.""" self.event_generate('<<EditDM>>') def reselect(self, event=None): """Return selection focus to a dm after an action is completed.""" if self.selIdx is not None: self.dmListDisp.selection_set(self.selIdx) self.dmListDisp.event_generate('<<ListboxSelect>>')
class RecursiveDescentApp(object): """ A graphical tool for exploring the recursive descent parser. The tool displays the parser's tree and the remaining text, and allows the user to control the parser's operation. In particular, the user can expand subtrees on the frontier, match tokens on the frontier against the text, and backtrack. A "step" button simply steps through the parsing process, performing the operations that ``RecursiveDescentParser`` would use. """ def __init__(self, grammar, sent, trace=0): self._sent = sent self._parser = SteppingRecursiveDescentParser(grammar, trace) # Set up the main window. self._top = Tk() self._top.title('Recursive Descent Parser Application') # Set up key bindings. self._init_bindings() # Initialize the fonts. self._init_fonts(self._top) # Animations. animating_lock is a lock to prevent the demo # from performing new operations while it's animating. self._animation_frames = IntVar(self._top) self._animation_frames.set(5) self._animating_lock = 0 self._autostep = 0 # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_feedback(self._top) self._init_grammar(self._top) self._init_canvas(self._top) # Initialize the parser. self._parser.initialize(self._sent) # Resize callback self._canvas.bind('<Configure>', self._configure) ######################################### ## Initialization Helpers ######################################### def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = tkinter.font.Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget('size')) self._boldfont = tkinter.font.Font(family='helvetica', weight='bold', size=self._size.get()) self._font = tkinter.font.Font(family='helvetica', size=self._size.get()) if self._size.get() < 0: big = self._size.get()-2 else: big = self._size.get()+2 self._bigfont = tkinter.font.Font(family='helvetica', weight='bold', size=big) def _init_grammar(self, parent): # Grammar view. self._prodframe = listframe = Frame(parent) self._prodframe.pack(fill='both', side='left', padx=2) self._prodlist_label = Label(self._prodframe, font=self._boldfont, text='Available Expansions') self._prodlist_label.pack() self._prodlist = Listbox(self._prodframe, selectmode='single', relief='groove', background='white', foreground='#909090', font=self._font, selectforeground='#004040', selectbackground='#c0f0c0') self._prodlist.pack(side='right', fill='both', expand=1) self._productions = list(self._parser.grammar().productions()) for production in self._productions: self._prodlist.insert('end', (' %s' % production)) self._prodlist.config(height=min(len(self._productions), 25)) # Add a scrollbar if there are more than 25 productions. if len(self._productions) > 25: listscroll = Scrollbar(self._prodframe, orient='vertical') self._prodlist.config(yscrollcommand = listscroll.set) listscroll.config(command=self._prodlist.yview) listscroll.pack(side='left', fill='y') # If they select a production, apply it. self._prodlist.bind('<<ListboxSelect>>', self._prodlist_select) def _init_bindings(self): # Key bindings are a good thing. self._top.bind('<Control-q>', self.destroy) self._top.bind('<Control-x>', self.destroy) self._top.bind('<Escape>', self.destroy) self._top.bind('e', self.expand) #self._top.bind('<Alt-e>', self.expand) #self._top.bind('<Control-e>', self.expand) self._top.bind('m', self.match) self._top.bind('<Alt-m>', self.match) self._top.bind('<Control-m>', self.match) self._top.bind('b', self.backtrack) self._top.bind('<Alt-b>', self.backtrack) self._top.bind('<Control-b>', self.backtrack) self._top.bind('<Control-z>', self.backtrack) self._top.bind('<BackSpace>', self.backtrack) self._top.bind('a', self.autostep) #self._top.bind('<Control-a>', self.autostep) self._top.bind('<Control-space>', self.autostep) self._top.bind('<Control-c>', self.cancel_autostep) self._top.bind('<space>', self.step) self._top.bind('<Delete>', self.reset) self._top.bind('<Control-p>', self.postscript) #self._top.bind('<h>', self.help) #self._top.bind('<Alt-h>', self.help) self._top.bind('<Control-h>', self.help) self._top.bind('<F1>', self.help) #self._top.bind('<g>', self.toggle_grammar) #self._top.bind('<Alt-g>', self.toggle_grammar) #self._top.bind('<Control-g>', self.toggle_grammar) self._top.bind('<Control-g>', self.edit_grammar) self._top.bind('<Control-t>', self.edit_sentence) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill='none', side='bottom', padx=3, pady=2) Button(buttonframe, text='Step', background='#90c0d0', foreground='black', command=self.step,).pack(side='left') Button(buttonframe, text='Autostep', background='#90c0d0', foreground='black', command=self.autostep,).pack(side='left') Button(buttonframe, text='Expand', underline=0, background='#90f090', foreground='black', command=self.expand).pack(side='left') Button(buttonframe, text='Match', underline=0, background='#90f090', foreground='black', command=self.match).pack(side='left') Button(buttonframe, text='Backtrack', underline=0, background='#f0a0a0', foreground='black', command=self.backtrack).pack(side='left') # Replace autostep... # self._autostep_button = Button(buttonframe, text='Autostep', # underline=0, command=self.autostep) # self._autostep_button.pack(side='left') def _configure(self, event): self._autostep = 0 (x1, y1, x2, y2) = self._cframe.scrollregion() y2 = event.height - 6 self._canvas['scrollregion'] = '%d %d %d %d' % (x1,y1,x2,y2) self._redraw() def _init_feedback(self, parent): self._feedbackframe = feedbackframe = Frame(parent) feedbackframe.pack(fill='x', side='bottom', padx=3, pady=3) self._lastoper_label = Label(feedbackframe, text='Last Operation:', font=self._font) self._lastoper_label.pack(side='left') lastoperframe = Frame(feedbackframe, relief='sunken', border=1) lastoperframe.pack(fill='x', side='right', expand=1, padx=5) self._lastoper1 = Label(lastoperframe, foreground='#007070', background='#f0f0f0', font=self._font) self._lastoper2 = Label(lastoperframe, anchor='w', width=30, foreground='#004040', background='#f0f0f0', font=self._font) self._lastoper1.pack(side='left') self._lastoper2.pack(side='left', fill='x', expand=1) def _init_canvas(self, parent): self._cframe = CanvasFrame(parent, background='white', #width=525, height=250, closeenough=10, border=2, relief='sunken') self._cframe.pack(expand=1, fill='both', side='top', pady=2) canvas = self._canvas = self._cframe.canvas() # Initially, there's no tree or text self._tree = None self._textwidgets = [] self._textline = None def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label='Reset Parser', underline=0, command=self.reset, accelerator='Del') filemenu.add_command(label='Print to Postscript', underline=0, command=self.postscript, accelerator='Ctrl-p') filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-x') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) editmenu.add_command(label='Edit Grammar', underline=5, command=self.edit_grammar, accelerator='Ctrl-g') editmenu.add_command(label='Edit Text', underline=5, command=self.edit_sentence, accelerator='Ctrl-t') menubar.add_cascade(label='Edit', underline=0, menu=editmenu) rulemenu = Menu(menubar, tearoff=0) rulemenu.add_command(label='Step', underline=1, command=self.step, accelerator='Space') rulemenu.add_separator() rulemenu.add_command(label='Match', underline=0, command=self.match, accelerator='Ctrl-m') rulemenu.add_command(label='Expand', underline=0, command=self.expand, accelerator='Ctrl-e') rulemenu.add_separator() rulemenu.add_command(label='Backtrack', underline=0, command=self.backtrack, accelerator='Ctrl-b') menubar.add_cascade(label='Apply', underline=0, menu=rulemenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_checkbutton(label="Show Grammar", underline=0, variable=self._show_grammar, command=self._toggle_grammar) viewmenu.add_separator() viewmenu.add_radiobutton(label='Tiny', variable=self._size, underline=0, value=10, command=self.resize) viewmenu.add_radiobutton(label='Small', variable=self._size, underline=0, value=12, command=self.resize) viewmenu.add_radiobutton(label='Medium', variable=self._size, underline=0, value=14, command=self.resize) viewmenu.add_radiobutton(label='Large', variable=self._size, underline=0, value=18, command=self.resize) viewmenu.add_radiobutton(label='Huge', variable=self._size, underline=0, value=24, command=self.resize) menubar.add_cascade(label='View', underline=0, menu=viewmenu) animatemenu = Menu(menubar, tearoff=0) animatemenu.add_radiobutton(label="No Animation", underline=0, variable=self._animation_frames, value=0) animatemenu.add_radiobutton(label="Slow Animation", underline=0, variable=self._animation_frames, value=10, accelerator='-') animatemenu.add_radiobutton(label="Normal Animation", underline=0, variable=self._animation_frames, value=5, accelerator='=') animatemenu.add_radiobutton(label="Fast Animation", underline=0, variable=self._animation_frames, value=2, accelerator='+') menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label='About', underline=0, command=self.about) helpmenu.add_command(label='Instructions', underline=0, command=self.help, accelerator='F1') menubar.add_cascade(label='Help', underline=0, menu=helpmenu) parent.config(menu=menubar) ######################################### ## Helper ######################################### def _get(self, widget, treeloc): for i in treeloc: widget = widget.subtrees()[i] if isinstance(widget, TreeSegmentWidget): widget = widget.label() return widget ######################################### ## Main draw procedure ######################################### def _redraw(self): canvas = self._canvas # Delete the old tree, widgets, etc. if self._tree is not None: self._cframe.destroy_widget(self._tree) for twidget in self._textwidgets: self._cframe.destroy_widget(twidget) if self._textline is not None: self._canvas.delete(self._textline) # Draw the tree. helv = ('helvetica', -self._size.get()) bold = ('helvetica', -self._size.get(), 'bold') attribs = {'tree_color': '#000000', 'tree_width': 2, 'node_font': bold, 'leaf_font': helv,} tree = self._parser.tree() self._tree = tree_to_treesegment(canvas, tree, **attribs) self._cframe.add_widget(self._tree, 30, 5) # Draw the text. helv = ('helvetica', -self._size.get()) bottom = y = self._cframe.scrollregion()[3] self._textwidgets = [TextWidget(canvas, word, font=self._font) for word in self._sent] for twidget in self._textwidgets: self._cframe.add_widget(twidget, 0, 0) twidget.move(0, bottom-twidget.bbox()[3]-5) y = min(y, twidget.bbox()[1]) # Draw a line over the text, to separate it from the tree. self._textline = canvas.create_line(-5000, y-5, 5000, y-5, dash='.') # Highlight appropriate nodes. self._highlight_nodes() self._highlight_prodlist() # Make sure the text lines up. self._position_text() def _redraw_quick(self): # This should be more-or-less sufficient after an animation. self._highlight_nodes() self._highlight_prodlist() self._position_text() def _highlight_nodes(self): # Highlight the list of nodes to be checked. bold = ('helvetica', -self._size.get(), 'bold') for treeloc in self._parser.frontier()[:1]: self._get(self._tree, treeloc)['color'] = '#20a050' self._get(self._tree, treeloc)['font'] = bold for treeloc in self._parser.frontier()[1:]: self._get(self._tree, treeloc)['color'] = '#008080' def _highlight_prodlist(self): # Highlight the productions that can be expanded. # Boy, too bad tkinter doesn't implement Listbox.itemconfig; # that would be pretty useful here. self._prodlist.delete(0, 'end') expandable = self._parser.expandable_productions() untried = self._parser.untried_expandable_productions() productions = self._productions for index in range(len(productions)): if productions[index] in expandable: if productions[index] in untried: self._prodlist.insert(index, ' %s' % productions[index]) else: self._prodlist.insert(index, ' %s (TRIED)' % productions[index]) self._prodlist.selection_set(index) else: self._prodlist.insert(index, ' %s' % productions[index]) def _position_text(self): # Line up the text widgets that are matched against the tree numwords = len(self._sent) num_matched = numwords - len(self._parser.remaining_text()) leaves = self._tree_leaves()[:num_matched] xmax = self._tree.bbox()[0] for i in range(0, len(leaves)): widget = self._textwidgets[i] leaf = leaves[i] widget['color'] = '#006040' leaf['color'] = '#006040' widget.move(leaf.bbox()[0] - widget.bbox()[0], 0) xmax = widget.bbox()[2] + 10 # Line up the text widgets that are not matched against the tree. for i in range(len(leaves), numwords): widget = self._textwidgets[i] widget['color'] = '#a0a0a0' widget.move(xmax - widget.bbox()[0], 0) xmax = widget.bbox()[2] + 10 # If we have a complete parse, make everything green :) if self._parser.currently_complete(): for twidget in self._textwidgets: twidget['color'] = '#00a000' # Move the matched leaves down to the text. for i in range(0, len(leaves)): widget = self._textwidgets[i] leaf = leaves[i] dy = widget.bbox()[1] - leaf.bbox()[3] - 10.0 dy = max(dy, leaf.parent().label().bbox()[3] - leaf.bbox()[3] + 10) leaf.move(0, dy) def _tree_leaves(self, tree=None): if tree is None: tree = self._tree if isinstance(tree, TreeSegmentWidget): leaves = [] for child in tree.subtrees(): leaves += self._tree_leaves(child) return leaves else: return [tree] ######################################### ## Button Callbacks ######################################### def destroy(self, *e): self._autostep = 0 if self._top is None: return self._top.destroy() self._top = None def reset(self, *e): self._autostep = 0 self._parser.initialize(self._sent) self._lastoper1['text'] = 'Reset Application' self._lastoper2['text'] = '' self._redraw() def autostep(self, *e): if self._animation_frames.get() == 0: self._animation_frames.set(2) if self._autostep: self._autostep = 0 else: self._autostep = 1 self._step() def cancel_autostep(self, *e): #self._autostep_button['text'] = 'Autostep' self._autostep = 0 # Make sure to stop auto-stepping if we get any user input. def step(self, *e): self._autostep = 0; self._step() def match(self, *e): self._autostep = 0; self._match() def expand(self, *e): self._autostep = 0; self._expand() def backtrack(self, *e): self._autostep = 0; self._backtrack() def _step(self): if self._animating_lock: return # Try expanding, matching, and backtracking (in that order) if self._expand(): pass elif self._parser.untried_match() and self._match(): pass elif self._backtrack(): pass else: self._lastoper1['text'] = 'Finished' self._lastoper2['text'] = '' self._autostep = 0 # Check if we just completed a parse. if self._parser.currently_complete(): self._autostep = 0 self._lastoper2['text'] += ' [COMPLETE PARSE]' def _expand(self, *e): if self._animating_lock: return old_frontier = self._parser.frontier() rv = self._parser.expand() if rv is not None: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = rv self._prodlist.selection_clear(0, 'end') index = self._productions.index(rv) self._prodlist.selection_set(index) self._animate_expand(old_frontier[0]) return 1 else: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = '(all expansions tried)' return 0 def _match(self, *e): if self._animating_lock: return old_frontier = self._parser.frontier() rv = self._parser.match() if rv is not None: self._lastoper1['text'] = 'Match:' self._lastoper2['text'] = rv self._animate_match(old_frontier[0]) return 1 else: self._lastoper1['text'] = 'Match:' self._lastoper2['text'] = '(failed)' return 0 def _backtrack(self, *e): if self._animating_lock: return if self._parser.backtrack(): elt = self._parser.tree() for i in self._parser.frontier()[0]: elt = elt[i] self._lastoper1['text'] = 'Backtrack' self._lastoper2['text'] = '' if isinstance(elt, Tree): self._animate_backtrack(self._parser.frontier()[0]) else: self._animate_match_backtrack(self._parser.frontier()[0]) return 1 else: self._autostep = 0 self._lastoper1['text'] = 'Finished' self._lastoper2['text'] = '' return 0 def about(self, *e): ABOUT = ("NLTK Recursive Descent Parser Application\n"+ "Written by Edward Loper") TITLE = 'About: Recursive Descent Parser Application' try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def help(self, *e): self._autostep = 0 # The default font's not very legible; try using 'fixed' instead. try: ShowText(self._top, 'Help: Recursive Descent Parser Application', (__doc__ or '').strip(), width=75, font='fixed') except: ShowText(self._top, 'Help: Recursive Descent Parser Application', (__doc__ or '').strip(), width=75) def postscript(self, *e): self._autostep = 0 self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) self._bigfont.configure(size=-(abs(size+2))) self._redraw() ######################################### ## Expand Production Selection ######################################### def _toggle_grammar(self, *e): if self._show_grammar.get(): self._prodframe.pack(fill='both', side='left', padx=2, after=self._feedbackframe) self._lastoper1['text'] = 'Show Grammar' else: self._prodframe.pack_forget() self._lastoper1['text'] = 'Hide Grammar' self._lastoper2['text'] = '' # def toggle_grammar(self, *e): # self._show_grammar = not self._show_grammar # if self._show_grammar: # self._prodframe.pack(fill='both', expand='y', side='left', # after=self._feedbackframe) # self._lastoper1['text'] = 'Show Grammar' # else: # self._prodframe.pack_forget() # self._lastoper1['text'] = 'Hide Grammar' # self._lastoper2['text'] = '' def _prodlist_select(self, event): selection = self._prodlist.curselection() if len(selection) != 1: return index = int(selection[0]) old_frontier = self._parser.frontier() production = self._parser.expand(self._productions[index]) if production: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = production self._prodlist.selection_clear(0, 'end') self._prodlist.selection_set(index) self._animate_expand(old_frontier[0]) else: # Reset the production selections. self._prodlist.selection_clear(0, 'end') for prod in self._parser.expandable_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) ######################################### ## Animation ######################################### def _animate_expand(self, treeloc): oldwidget = self._get(self._tree, treeloc) oldtree = oldwidget.parent() top = not isinstance(oldtree.parent(), TreeSegmentWidget) tree = self._parser.tree() for i in treeloc: tree = tree[i] widget = tree_to_treesegment(self._canvas, tree, node_font=self._boldfont, leaf_color='white', tree_width=2, tree_color='white', node_color='white', leaf_font=self._font) widget.label()['color'] = '#20a050' (oldx, oldy) = oldtree.label().bbox()[:2] (newx, newy) = widget.label().bbox()[:2] widget.move(oldx-newx, oldy-newy) if top: self._cframe.add_widget(widget, 0, 5) widget.move(30-widget.label().bbox()[0], 0) self._tree = widget else: oldtree.parent().replace_child(oldtree, widget) # Move the children over so they don't overlap. # Line the children up in a strange way. if widget.subtrees(): dx = (oldx + widget.label().width()/2 - widget.subtrees()[0].bbox()[0]/2 - widget.subtrees()[0].bbox()[2]/2) for subtree in widget.subtrees(): subtree.move(dx, 0) self._makeroom(widget) if top: self._cframe.destroy_widget(oldtree) else: oldtree.destroy() colors = ['gray%d' % (10*int(10*x/self._animation_frames.get())) for x in range(self._animation_frames.get(),0,-1)] # Move the text string down, if necessary. dy = widget.bbox()[3] + 30 - self._canvas.coords(self._textline)[1] if dy > 0: for twidget in self._textwidgets: twidget.move(0, dy) self._canvas.move(self._textline, 0, dy) self._animate_expand_frame(widget, colors) def _makeroom(self, treeseg): """ Make sure that no sibling tree bbox's overlap. """ parent = treeseg.parent() if not isinstance(parent, TreeSegmentWidget): return index = parent.subtrees().index(treeseg) # Handle siblings to the right rsiblings = parent.subtrees()[index+1:] if rsiblings: dx = treeseg.bbox()[2] - rsiblings[0].bbox()[0] + 10 for sibling in rsiblings: sibling.move(dx, 0) # Handle siblings to the left if index > 0: lsibling = parent.subtrees()[index-1] dx = max(0, lsibling.bbox()[2] - treeseg.bbox()[0] + 10) treeseg.move(dx, 0) # Keep working up the tree. self._makeroom(parent) def _animate_expand_frame(self, widget, colors): if len(colors) > 0: self._animating_lock = 1 widget['color'] = colors[0] for subtree in widget.subtrees(): if isinstance(subtree, TreeSegmentWidget): subtree.label()['color'] = colors[0] else: subtree['color'] = colors[0] self._top.after(50, self._animate_expand_frame, widget, colors[1:]) else: widget['color'] = 'black' for subtree in widget.subtrees(): if isinstance(subtree, TreeSegmentWidget): subtree.label()['color'] = 'black' else: subtree['color'] = 'black' self._redraw_quick() widget.label()['color'] = 'black' self._animating_lock = 0 if self._autostep: self._step() def _animate_backtrack(self, treeloc): # Flash red first, if we're animating. if self._animation_frames.get() == 0: colors = [] else: colors = ['#a00000', '#000000', '#a00000'] colors += ['gray%d' % (10*int(10*x/(self._animation_frames.get()))) for x in range(1, self._animation_frames.get()+1)] widgets = [self._get(self._tree, treeloc).parent()] for subtree in widgets[0].subtrees(): if isinstance(subtree, TreeSegmentWidget): widgets.append(subtree.label()) else: widgets.append(subtree) self._animate_backtrack_frame(widgets, colors) def _animate_backtrack_frame(self, widgets, colors): if len(colors) > 0: self._animating_lock = 1 for widget in widgets: widget['color'] = colors[0] self._top.after(50, self._animate_backtrack_frame, widgets, colors[1:]) else: for widget in widgets[0].subtrees(): widgets[0].remove_child(widget) widget.destroy() self._redraw_quick() self._animating_lock = 0 if self._autostep: self._step() def _animate_match_backtrack(self, treeloc): widget = self._get(self._tree, treeloc) node = widget.parent().label() dy = (1.0 * (node.bbox()[3] - widget.bbox()[1] + 14) / max(1, self._animation_frames.get())) self._animate_match_backtrack_frame(self._animation_frames.get(), widget, dy) def _animate_match(self, treeloc): widget = self._get(self._tree, treeloc) dy = ((self._textwidgets[0].bbox()[1] - widget.bbox()[3] - 10.0) / max(1, self._animation_frames.get())) self._animate_match_frame(self._animation_frames.get(), widget, dy) def _animate_match_frame(self, frame, widget, dy): if frame > 0: self._animating_lock = 1 widget.move(0, dy) self._top.after(10, self._animate_match_frame, frame-1, widget, dy) else: widget['color'] = '#006040' self._redraw_quick() self._animating_lock = 0 if self._autostep: self._step() def _animate_match_backtrack_frame(self, frame, widget, dy): if frame > 0: self._animating_lock = 1 widget.move(0, dy) self._top.after(10, self._animate_match_backtrack_frame, frame-1, widget, dy) else: widget.parent().remove_child(widget) widget.destroy() self._animating_lock = 0 if self._autostep: self._step() def edit_grammar(self, *e): CFGEditor(self._top, self._parser.grammar(), self.set_grammar) def set_grammar(self, grammar): self._parser.set_grammar(grammar) self._productions = list(grammar.productions()) self._prodlist.delete(0, 'end') for production in self._productions: self._prodlist.insert('end', (' %s' % production)) def edit_sentence(self, *e): sentence = " ".join(self._sent) title = 'Edit Text' instr = 'Enter a new sentence to parse.' EntryDialog(self._top, sentence, instr, self.set_sentence, title) def set_sentence(self, sentence): self._sent = sentence.split() #[XX] use tagged? self.reset()
class ChooseFiles(tk.Frame): '''The main frame for adding/removing files, accessing setttings and parsing.''' from seebeck.parse import GUIParse opts = SimpleNamespace(in_files=[], out_dir=os.path.join(os.getcwd(), 'parsed'), out_file='', plot=True, write=True, truetemp=False, col_to_parse=1, dTn=[], cuttoff_to_toss=100) # TODO: Make this a user option raw_data = {} gothreads = [] plots = [] outdir = '' boolmap = {1: True, 0: False} colmap = {1:'Raw Voltage (uV)', 2:'Corrected Voltage (uV)', 3:'Top T', 4:'Bottom T', 5:'Delta-T (°C)', 6:'Seebeck (uV/K)'} FileListBoxFrameLabelVar = None FileListBoxFrameLabel = None filelist = None FileListBox = None checks = [] OutputFileName = None DeltaTn = None OptionsColString = None OptionCol = None def __init__(self, master=None): if master is None: master = Tk() super().__init__(master) bgimg = PhotoImage(file=os.path.join(absdir, 'gui', 'RCCLabFluidic.png')) limg = Label(self.master, i=bgimg, background=GREY) limg.pack(side=TOP) try: self.last_input_path = os.getcwd() except KeyError: self.last_input_path = os.path.expanduser('~') master.tk_setPalette(background=GREY, activeBackground=GREY) master.title("RCCLab Rick-9000 Parser") master.geometry('800x850+250+250') self.pack(fill=BOTH) if len(sys.argv) > 1: self.opts.in_files = sys.argv[1:] self.__createWidgets() self.ToFront() def __createWidgets(self): self.ButtonFrame = tk.Frame(self) self.LoggingFrame = tk.Frame(self) self.RightOptionsFrame = tk.Frame(self) self.FileListBoxFrame = tk.Frame(self) # ScrolledText widget to display logging output stlogger = ScrolledText.ScrolledText(self.LoggingFrame, state='disabled') stlogger.configure(font='TkFixedFont') stlogger.pack(side=LEFT, fill=BOTH, expand=True) # Create textLogger text_handler = TextHandler(stlogger) # Logging configuration logging.basicConfig(filename='Rick-9000.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') # Add the handler to logger logger = logging.getLogger(__package__) logger.addHandler(text_handler) self.__createButtons() self.__createFileListBox() self.__createOptions() self.ButtonFrame.pack(side=BOTTOM, fill=None) self.FileListBoxFrame.pack(side=TOP, fill=BOTH, expand=True) self.RightOptionsFrame.pack(side=RIGHT, fill=Y) self.LoggingFrame.pack(side=BOTTOM, fill=BOTH) # self.logger.setLevel(getattr(logging, self.opts.loglevel.upper())) self.updateFileListBox() self.checkOptions() logging.info('Select some files to get started.') def __createButtons(self): buttons = [{'name': 'Quit', 'text': 'QUIT', 'command': 'Quit', 'side': BOTTOM}, {'name': 'SpawnInputDialog', 'text': 'Add Input Files', 'side': LEFT}, {'name': 'RemoveFile', 'text': 'Remove Files', 'side': LEFT}, {'name': 'SpawnOutputDialog', 'text': 'Choose Output Directory', 'side': LEFT}, {'name': 'Parse', 'text': 'Parse!', 'side': LEFT} ] for b in buttons: button = tk.Button(self.ButtonFrame) button.config(text=b['text'], command=getattr(self, b['name']+'Click')) button.pack(side=b['side']) setattr(self, 'Button'+b['name'], button) def __createFileListBox(self): self.FileListBoxFrameLabelVar = StringVar() self.FileListBoxFrameLabel = tk.Label(self.FileListBoxFrame, textvariable=self.FileListBoxFrameLabelVar, font=Font(size=10, weight="bold")) self.FileListBoxFrameLabel.pack(side=TOP, fill=X) yScroll = tk.Scrollbar(self.FileListBoxFrame, orient=VERTICAL) yScroll.pack(side=RIGHT, fill=Y) xScroll = tk.Scrollbar(self.FileListBoxFrame, orient=HORIZONTAL) xScroll.pack(side=BOTTOM, fill=X) self.filelist = StringVar() self.FileListBox = Listbox(self.FileListBoxFrame, listvariable=self.filelist, selectmode=EXTENDED, height=20, width=0, relief=RAISED, bd=1, bg=WHITE, # font=Font(size=10), xscrollcommand=xScroll.set, yscrollcommand=yScroll.set) xScroll['command'] = self.FileListBox.xview yScroll['command'] = self.FileListBox.yview self.FileListBox.pack(side=LEFT, fill=BOTH, expand=True) self.UpdateFileListBoxFrameLabel() def __createOptions(self): '''Create the widgets for options and use setattr to assign them to self.''' self.checks = [{'name': 'plot', 'text': 'Plot', 'row': 1, 'tooltip': "Show summary plots after parsing."}, {'name': 'write', 'text': 'Write', 'row': 2, 'tooltip': "Write results to text files after parsing."}, {'name': 'truetemp', 'text': 'Use Real T', 'row': 3, 'tooltip': "Use the measured ΔT instead the ΔT field values."} ] for _c in self.checks: setattr(self, _c['name'], IntVar()) check = tk.Checkbutton(self.RightOptionsFrame, text=_c['text'], variable=getattr(self, _c['name']), command=self.checkOptions) check.grid(column=0, row=_c['row'], sticky=W) createToolTip(check, _c['tooltip']) setattr(self, 'Check_'+_c['name'], check) if getattr(self.opts, _c['name']): getattr(self, _c['name']).set(1) rowidx = len(self.checks)+1 tk.Label(self.RightOptionsFrame, text="Output file base name:").grid( column=0, row=rowidx) self.OutputFileName = tk.Entry(self.RightOptionsFrame, width=20, font=Font(size=10, slant='italic')) for n in ('<Return>', '<Leave>', '<Enter>'): self.OutputFileName.bind(n, self.checkOutputFileName) self.OutputFileName.grid(column=0, row=rowidx+1) if self.opts.out_file: self.OutputFileName.insert(0, self.opts.out_file) tk.Label(self.RightOptionsFrame, text="ΔT values:").grid( column=0, row=rowidx+2, sticky=W) self.DeltaTn = tk.Entry(self.RightOptionsFrame, width=20, font=Font(size=10)) self.DeltaTn.insert(0, '4,8,12') for n in ('<Return>', '<Leave>', '<Enter>'): self.DeltaTn.bind(None, self.checkOptions) self.DeltaTn.grid(column=0, row=rowidx+3, sticky=W) tk.Label(self.RightOptionsFrame, text="Column to plot:").grid( column=0, row=rowidx+4, sticky=W) self.OptionsColString = StringVar() self.OptionsColString.set(self.opts.col_to_parse) self.OptionCol = tk.OptionMenu(self.RightOptionsFrame, self.OptionsColString, self.colmap[self.opts.col_to_parse], command=self.checkOptions, *list(self.colmap.values())) # __menu = self.nametowidget(self.OptionCol) # __menu.config(font=Font(size=10)) # Set the dropdown menu's font self.OptionCol.grid(column=0, row=rowidx+5, sticky=W) # tk.Label(self.RightOptionsFrame, text="ΔT values:").grid( # column=0, row=rowidx+2, sticky=W) # self.OptionsdTnString = StringVar() # self.OptionsdTnString.set('.'.join(self.opts.dTn)) # self.OptionPlots = tk.OptionMenu(self.RightOptionsFrame, # self.OptionsdTnString, self.opts.dTn, 'J', 'R', # command=self.checkOptions) # self.OptionPlots.grid(column=0, row=rowidx+3, sticky=W) def RemoveFileClick(self): self.checkOptions() selected = [self.FileListBox.get(x) for x in self.FileListBox.curselection()] todel = [] filelist = [] for i in range(0, len(self.opts.in_files)): for _s in selected: if self.opts.in_files[i].replace(" ", "_") == _s: todel.append(i) for i in range(0, len(self.opts.in_files)): if i not in todel: filelist.append(self.opts.in_files[i]) self.opts.in_files = filelist self.updateFileListBox() self.FileListBox.selection_clear(0, END) def SpawnInputDialogClick(self): # self.checkOptions() self.opts.in_files += filedialog.askopenfilename( title="Files to parse", multiple=True, initialdir=self.last_input_path, filetypes=[('LabView Files', '*.lvm'), ('Data files', '*_data.txt'), ('Text files', '*.txt'), ('All files', '*')]) if self.opts.in_files: self.last_input_path = os.path.split(self.opts.in_files[0])[0] if not self.outdir: self.opts.out_dir = self.last_input_path self.updateFileListBox() if not self.opts.out_file: self.OutputFileName.delete(0, END) self.OutputFileName.insert(0, os.path.basename( self.opts.in_files[-1]).lower().replace('.lvm', '')) self.checkOutputFileName() self.updateFileListBox() def ParseClick(self): self.checkOptions() self.GUIParse() def checkOptions(self, m=None): for c in self.checks: setattr(self.opts, c['name'], self.boolmap[getattr(self, c['name']).get()]) if self.opts.truetemp: self.DeltaTn['state'] = DISABLED else: self.DeltaTn['state'] = NORMAL self.opts.dTn = self.DeltaTn.get().split(',') for __key in self.colmap: if self.colmap[__key] == self.OptionsColString.get(): self.opts.col_to_parse = __key def updateFileListBox(self): self.filelist.set(" ".join([x.replace(" ", "_") for x in self.opts.in_files])) def SpawnOutputDialogClick(self): outdir = filedialog.askdirectory(title="Select Output File(s)", initialdir=self.opts.out_dir) if not outdir: return if os.path.exists(outdir): self.opts.out_dir = outdir self.outdir = self.opts.out_dir # So we know the user set the output dir self.UpdateFileListBoxFrameLabel() def UpdateFileListBoxFrameLabel(self): self.FileListBoxFrameLabelVar.set( f"Output to: {self.opts.out_dir}/{self.opts.out_file}_*.txt") def checkOutputFileName(self, event=None): self.opts.out_file = self.OutputFileName.get() self.UpdateFileListBoxFrameLabel() def QuitClick(self): self.Quit() def Quit(self): self.master.destroy() def ToFront(self): '''Try to bring the main window to the front on different platforms''' if platform.system() == "Darwin": os.system( '''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "Python" to true' ''') else: self.master.attributes('-topmost', 1) self.master.attributes('-topmost', 0) self.master.lift()
class index_select(Frame): def __init__(self,controller,current_model,master,*args,**kwargs): self.controller = controller self.current_model = current_model self.filtered_list = None self.saved_selection = None from tkinter import EXTENDED,Scrollbar,Y #dibujar widget super().__init__(master, *args, **kwargs) f_st=Frame(self) Label(f_st,text="Current_index: ").pack() Label(f_st, text="Current_filter: ").pack() f_st.pack() frame_index_listbox = Frame(self) self.listbox = Listbox(frame_index_listbox, exportselection=False,selectmode=EXTENDED) self.listbox.pack(side=LEFT) scrollbar = Scrollbar(frame_index_listbox) scrollbar.pack(side=LEFT, fill=Y) frame_index_listbox.pack() # attach listbox to scrollbar self.listbox.config(yscrollcommand=scrollbar.set) scrollbar.config(command=self.listbox.yview) f=Frame(self) Label(f,text="Filtro: ").pack(side=LEFT) self.entry_w = Entry(f) self.entry_w.pack(side=LEFT) f.pack() f2=Frame(self) Button(f2, text='Filter',command=self.filter_indexs).pack(side=LEFT) Button(f2,text='Clean Filter',command=self.clean_filter).pack(side=LEFT) Button(f2, text='Export sel for gen',command=self.export_selection).pack(side=LEFT) f2.pack() f3=Frame(self) Button(self, text='<<',command=self.next_prev(-1)).pack(side=LEFT) Button(self, text='>>',command=self.next_prev(1)).pack(side=LEFT) f3.pack() self.update_model(current_model) self.listbox.select_set(0) self.listbox.event_generate("<<ListboxSelect>>") self.listbox.bind('<<ListboxSelect>>', self.selection) def filter_indexs(self): import random path_filter = self.entry_w.get() with open(path_filter,'r') as f: indexs_list_str = f.read().strip() random.seed(3) indexs_list = list(set(indexs_list_str.split('\n'))) print(len(indexs_list)) random.shuffle(indexs_list) self.filtered_list = indexs_list self.listbox.delete(0, END) for item in indexs_list: self.listbox.insert(END, item) def clean_filter(self): self.filtered_list = None self.update_model(self.current_model) self.listbox.delete(0, END) for item in sorted(self.index_list): self.listbox.insert(END, item) self.saved_selection = None def export_selection(self): sel_files_folder = os.path.join('config_files','select_files') os.makedirs(sel_files_folder,exist_ok=True) sel_list = self.listbox.curselection() index_list = [self.listbox.get(ind) for ind in sel_list] print("Exporting list for image generator. List: {0}".format(index_list)) now_s = now_string() out_selection_file = {'index_list' : index_list, 'train_result_path': self.current_model.current_config_file, 'details' : '', 'mask_file' : self.current_model.current_mask_file} sel_file_name = "{0}_{1}_{2}_selection.json".format(self.current_model.classifier_key,self.current_model.dataset_key,now_s) sel_path = os.path.join(sel_files_folder,sel_file_name) with open(sel_path,'w') as f: json.dump(out_selection_file,f) print("Select in {0}".format(sel_path)) def update_model(self, current_model): self.model = current_model self.index_list = self.model.get_index_list() self.current_index = self.model.get_current_index() self.mask_list = self.model.get_current_mask_index_list() indexs_list = self.filtered_list if self.filtered_list else sorted(self.index_list) self.listbox.delete(0, END) for item in indexs_list: self.listbox.insert(END, item) if self.saved_selection: ind,ypos = self.saved_selection self.listbox.selection_set(ind) self.listbox.yview_moveto(ypos[0]) # go back to that position def next_prev(self,x): def selection(): ind_l = self.index_list.index(self.current_index) n = len(self.index_list) n_ind_l = (ind_l+x) % n next = self.index_list[n_ind_l] self.current_index = next self.listbox.selection_clear(0, END) self.listbox.select_set(n_ind_l) self.controller.event_change_index(next) return selection def selection(self,event): w = event.widget index = int(w.curselection()[0]) value = w.get(index) print("v: {0}".format(value)) selected_index = value self.current_index = selected_index self.controller.event_change_index(selected_index) self.saved_selection = (index,self.listbox.yview()) pass
class DrtGlueDemo: def __init__(self, examples): # Set up the main window. self._top = Tk() self._top.title("DRT Glue Demo") # Set up key bindings. self._init_bindings() # Initialize the fonts.self._error = None self._init_fonts(self._top) self._examples = examples self._readingCache = [None for example in examples] # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Set the data to None self._curExample = -1 self._readings = [] self._drs = None self._drsWidget = None self._error = None self._init_glue() # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_exampleListbox(self._top) self._init_readingListbox(self._top) self._init_canvas(self._top) # Resize callback self._canvas.bind("<Configure>", self._configure) ######################################### ## Initialization Helpers ######################################### def _init_glue(self): tagger = RegexpTagger([ ("^(David|Mary|John)$", "NNP"), ( "^(walks|sees|eats|chases|believes|gives|sleeps|chases|persuades|tries|seems|leaves)$", "VB", ), ("^(go|order|vanish|find|approach)$", "VB"), ("^(a)$", "ex_quant"), ("^(every)$", "univ_quant"), ("^(sandwich|man|dog|pizza|unicorn|cat|senator)$", "NN"), ("^(big|gray|former)$", "JJ"), ("^(him|himself)$", "PRP"), ]) depparser = MaltParser(tagger=tagger) self._glue = DrtGlue(depparser=depparser, remove_duplicates=False) def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget("size")) self._boldfont = Font(family="helvetica", weight="bold", size=self._size.get()) self._font = Font(family="helvetica", size=self._size.get()) if self._size.get() < 0: big = self._size.get() - 2 else: big = self._size.get() + 2 self._bigfont = Font(family="helvetica", weight="bold", size=big) def _init_exampleListbox(self, parent): self._exampleFrame = listframe = Frame(parent) self._exampleFrame.pack(fill="both", side="left", padx=2) self._exampleList_label = Label(self._exampleFrame, font=self._boldfont, text="Examples") self._exampleList_label.pack() self._exampleList = Listbox( self._exampleFrame, selectmode="single", relief="groove", background="white", foreground="#909090", font=self._font, selectforeground="#004040", selectbackground="#c0f0c0", ) self._exampleList.pack(side="right", fill="both", expand=1) for example in self._examples: self._exampleList.insert("end", (" %s" % example)) self._exampleList.config(height=min(len(self._examples), 25), width=40) # Add a scrollbar if there are more than 25 examples. if len(self._examples) > 25: listscroll = Scrollbar(self._exampleFrame, orient="vertical") self._exampleList.config(yscrollcommand=listscroll.set) listscroll.config(command=self._exampleList.yview) listscroll.pack(side="left", fill="y") # If they select a example, apply it. self._exampleList.bind("<<ListboxSelect>>", self._exampleList_select) def _init_readingListbox(self, parent): self._readingFrame = listframe = Frame(parent) self._readingFrame.pack(fill="both", side="left", padx=2) self._readingList_label = Label(self._readingFrame, font=self._boldfont, text="Readings") self._readingList_label.pack() self._readingList = Listbox( self._readingFrame, selectmode="single", relief="groove", background="white", foreground="#909090", font=self._font, selectforeground="#004040", selectbackground="#c0f0c0", ) self._readingList.pack(side="right", fill="both", expand=1) # Add a scrollbar if there are more than 25 examples. listscroll = Scrollbar(self._readingFrame, orient="vertical") self._readingList.config(yscrollcommand=listscroll.set) listscroll.config(command=self._readingList.yview) listscroll.pack(side="right", fill="y") self._populate_readingListbox() def _populate_readingListbox(self): # Populate the listbox with integers self._readingList.delete(0, "end") for i in range(len(self._readings)): self._readingList.insert("end", (" %s" % (i + 1))) self._readingList.config(height=min(len(self._readings), 25), width=5) # If they select a example, apply it. self._readingList.bind("<<ListboxSelect>>", self._readingList_select) def _init_bindings(self): # Key bindings are a good thing. self._top.bind("<Control-q>", self.destroy) self._top.bind("<Control-x>", self.destroy) self._top.bind("<Escape>", self.destroy) self._top.bind("n", self.next) self._top.bind("<space>", self.next) self._top.bind("p", self.prev) self._top.bind("<BackSpace>", self.prev) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill="none", side="bottom", padx=3, pady=2) Button( buttonframe, text="Prev", background="#90c0d0", foreground="black", command=self.prev, ).pack(side="left") Button( buttonframe, text="Next", background="#90c0d0", foreground="black", command=self.next, ).pack(side="left") def _configure(self, event): self._autostep = 0 (x1, y1, x2, y2) = self._cframe.scrollregion() y2 = event.height - 6 self._canvas["scrollregion"] = "%d %d %d %d" % (x1, y1, x2, y2) self._redraw() def _init_canvas(self, parent): self._cframe = CanvasFrame( parent, background="white", # width=525, height=250, closeenough=10, border=2, relief="sunken", ) self._cframe.pack(expand=1, fill="both", side="top", pady=2) canvas = self._canvas = self._cframe.canvas() # Initially, there's no tree or text self._tree = None self._textwidgets = [] self._textline = None def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="Exit", underline=1, command=self.destroy, accelerator="q") menubar.add_cascade(label="File", underline=0, menu=filemenu) actionmenu = Menu(menubar, tearoff=0) actionmenu.add_command(label="Next", underline=0, command=self.next, accelerator="n, Space") actionmenu.add_command(label="Previous", underline=0, command=self.prev, accelerator="p, Backspace") menubar.add_cascade(label="Action", underline=0, menu=actionmenu) optionmenu = Menu(menubar, tearoff=0) optionmenu.add_checkbutton( label="Remove Duplicates", underline=0, variable=self._glue.remove_duplicates, command=self._toggle_remove_duplicates, accelerator="r", ) menubar.add_cascade(label="Options", underline=0, menu=optionmenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_radiobutton( label="Tiny", variable=self._size, underline=0, value=10, command=self.resize, ) viewmenu.add_radiobutton( label="Small", variable=self._size, underline=0, value=12, command=self.resize, ) viewmenu.add_radiobutton( label="Medium", variable=self._size, underline=0, value=14, command=self.resize, ) viewmenu.add_radiobutton( label="Large", variable=self._size, underline=0, value=18, command=self.resize, ) viewmenu.add_radiobutton( label="Huge", variable=self._size, underline=0, value=24, command=self.resize, ) menubar.add_cascade(label="View", underline=0, menu=viewmenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label="About", underline=0, command=self.about) menubar.add_cascade(label="Help", underline=0, menu=helpmenu) parent.config(menu=menubar) ######################################### ## Main draw procedure ######################################### def _redraw(self): canvas = self._canvas # Delete the old DRS, widgets, etc. if self._drsWidget is not None: self._drsWidget.clear() if self._drs: self._drsWidget = DrsWidget(self._canvas, self._drs) self._drsWidget.draw() if self._error: self._drsWidget = DrsWidget(self._canvas, self._error) self._drsWidget.draw() ######################################### ## Button Callbacks ######################################### def destroy(self, *e): self._autostep = 0 if self._top is None: return self._top.destroy() self._top = None def prev(self, *e): selection = self._readingList.curselection() readingListSize = self._readingList.size() # there are readings if readingListSize > 0: # if one reading is currently selected if len(selection) == 1: index = int(selection[0]) # if it's on (or before) the first item if index <= 0: self._select_previous_example() else: self._readingList_store_selection(index - 1) else: # select its first reading self._readingList_store_selection(readingListSize - 1) else: self._select_previous_example() def _select_previous_example(self): # if the current example is not the first example if self._curExample > 0: self._exampleList_store_selection(self._curExample - 1) else: # go to the last example self._exampleList_store_selection(len(self._examples) - 1) def next(self, *e): selection = self._readingList.curselection() readingListSize = self._readingList.size() # if there are readings if readingListSize > 0: # if one reading is currently selected if len(selection) == 1: index = int(selection[0]) # if it's on (or past) the last item if index >= (readingListSize - 1): self._select_next_example() else: self._readingList_store_selection(index + 1) else: # select its first reading self._readingList_store_selection(0) else: self._select_next_example() def _select_next_example(self): # if the current example is not the last example if self._curExample < len(self._examples) - 1: self._exampleList_store_selection(self._curExample + 1) else: # go to the first example self._exampleList_store_selection(0) def about(self, *e): ABOUT = ( "NLTK Discourse Representation Theory (DRT) Glue Semantics Demo\n" + "Written by Daniel H. Garrette") TITLE = "About: NLTK DRT Glue Demo" try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def postscript(self, *e): self._autostep = 0 self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) self._bigfont.configure(size=-(abs(size + 2))) self._redraw() def _toggle_remove_duplicates(self): self._glue.remove_duplicates = not self._glue.remove_duplicates self._exampleList.selection_clear(0, "end") self._readings = [] self._populate_readingListbox() self._readingCache = [None for ex in self._examples] self._curExample = -1 self._error = None self._drs = None self._redraw() def _exampleList_select(self, event): selection = self._exampleList.curselection() if len(selection) != 1: return self._exampleList_store_selection(int(selection[0])) def _exampleList_store_selection(self, index): self._curExample = index example = self._examples[index] self._exampleList.selection_clear(0, "end") if example: cache = self._readingCache[index] if cache: if isinstance(cache, list): self._readings = cache self._error = None else: self._readings = [] self._error = cache else: try: self._readings = self._glue.parse_to_meaning(example) self._error = None self._readingCache[index] = self._readings except Exception as e: self._readings = [] self._error = DrtVariableExpression( Variable("Error: " + str(e))) self._readingCache[index] = self._error # add a star to the end of the example self._exampleList.delete(index) self._exampleList.insert(index, (" %s *" % example)) self._exampleList.config(height=min( len(self._examples), 25), width=40) self._populate_readingListbox() self._exampleList.selection_set(index) self._drs = None self._redraw() def _readingList_select(self, event): selection = self._readingList.curselection() if len(selection) != 1: return self._readingList_store_selection(int(selection[0])) def _readingList_store_selection(self, index): reading = self._readings[index] self._readingList.selection_clear(0, "end") if reading: self._readingList.selection_set(index) self._drs = reading.simplify().normalize().resolve_anaphora() self._redraw()
class Trace(Module): ''' Module to manage all of the different traces (with unique names/colors) and the Crosshairs objects associated to each one. In particular, handles creation/modfication of traces and crosshairs. ''' def __init__(self, app): info(' - initializing module: Trace') self.app = app # some images for the buttons # Source for icons: https://material.io/tools/icons/?style=outline # License: Apache Version 2.0 www.apache.org/licenses/LICENSE-2.0.txt data_copy = '''R0lGODlhGAAYAPAAAAAAAAAAACH5BAEAAAEALAAAAAAYABgAAAJHjI+pCe3/1oHUSdOunmDvHFTWBYrjUnbMuWIqAqEqCMdt+HI25yrVTZMEcT3NMPXJEZckJdKorCWbU2H0JqvKTBErl+XZFAAAOw''' data_paste = '''R0lGODlhGAAYAPAAAAAAAAAAACH5BAEAAAEALAAAAAAYABgAAAJBjI+pq+DAonlPToqza7rv9FlBeJCSOUJpd3EXm7piDKoi+nkqvnttPaMhUAzeiwJMapJDm8U44+kynCkmiM1qZwUAOw''' self.img_copy = PhotoImage(data=data_copy) self.img_paste = PhotoImage(data=data_paste) self.displayedColour = None #self.app.Data.getCurrentTraceColor() # array of trace names for this directory self.available = self.app.Data.getTopLevel('traces') self.available = [] if self.available == None else self.available # dictionary to hold trace -> [crosshairs] data self.crosshairs = {} # set of currently selected crosshairs self.selected = set() # set of copied crosshairs self.copied = [] # declare & init trace string variable self.traceSV = StringVar() self.traceSV.set('') # frame for (most of) our widgets self.frame = Frame(self.app.LEFT) #, pady=7, padx=7) self.frame.grid(row=4) # listbox to contain all of our traces lbframe = Frame(self.frame) self.scrollbar = Scrollbar(lbframe) self.listbox = Listbox(lbframe, yscrollcommand=self.scrollbar.set, width=12, exportselection=False, takefocus=0) self.scrollbar.config(command=self.listbox.yview) for trace in self.available: self.listbox.insert('end', trace) for i, item in enumerate(self.listbox.get(0, 'end')): # select our "default trace" if item == self.app.Data.getTopLevel('defaultTraceName'): self.listbox.selection_clear(0, 'end') self.listbox.select_set(i) # this module is responsible for so many widgets that we need a different # strategy for keeping track of everything that needs constistent grid() / # grid_remove() behavior self.TkWidgets = [ self.getWidget(Header(self.frame, text="Choose a trace"), row=5, column=0, columnspan=4), self.getWidget(lbframe, row=10, column=0, rowspan=50), self.getWidget(Button(self.frame, text='Set as default', command=self.setDefaultTraceName, takefocus=0), row=10, column=2, columnspan=2), self.getWidget(Button(self.frame, text='Select all', command=self.selectAll, takefocus=0), row=11, column=2, columnspan=2), self.getWidget(Button(self.frame, image=self.img_copy, command=self.copy, takefocus=0), row=12, column=2), # FIXME: add tooltip for "Copy" self.getWidget(Button(self.frame, image=self.img_paste, command=self.paste, takefocus=0), row=12, column=3), # FIXME: add tooltip for "Paste" self.getWidget(Entry(self.frame, width=8, textvariable=self.displayedColour), row=13, column=1, columnspan=2, sticky='w'), self.getWidget(Button(self.frame, text='Recolor', command=self.recolor, takefocus=0), row=13, column=3), self.getWidget(Button(self.frame, text='Clear', command=self.clear, takefocus=0), row=15, column=2, columnspan=2), self.getWidget(Entry(self.frame, width=12, textvariable=self.traceSV), row=100, column=0, sticky='w'), self.getWidget(Button(self.frame, text='New', command=self.newTrace, takefocus=0), row=100, column=2), self.getWidget(Button(self.frame, text='Rename', command=self.renameTrace, takefocus=0), row=100, column=3) ] # there's probably a better way to do this than indexing into self.TkWidgets self.TkWidgets[6]['widget'].bind( '<Return>', lambda ev: self.TkWidgets[0]['widget'].focus()) self.TkWidgets[6]['widget'].bind( '<Escape>', lambda ev: self.TkWidgets[0]['widget'].focus()) self.TkWidgets[9]['widget'].bind( '<Return>', lambda ev: self.TkWidgets[0]['widget'].focus()) self.TkWidgets[9]['widget'].bind( '<Escape>', lambda ev: self.TkWidgets[0]['widget'].focus()) if util.get_platform() == 'Linux': self.app.bind('<Control-r>', self.recolor) self.app.bind('<Control-c>', self.copy) self.app.bind('<Control-v>', self.paste) else: self.app.bind('<Command-r>', self.recolor) self.app.bind('<Command-c>', self.copy) self.app.bind('<Command-v>', self.paste) self.grid() def update(self): ''' on change frames ''' # self.grid() #NOTE this is called during zoom and pan #this means the crosshairs are redrawn for every <Motion> call, which is a lot #we could probably just move them instead self.reset() # clear our crosshairs self.read() # read from file #self.frame.update() #debug("TraceModule", self.frame.winfo_width()) def reset(self): ''' on change files ''' # undraw all the crosshairs for trace in self.crosshairs: for ch in self.crosshairs[trace]: ch.undraw() # and empty out our trackers self.crosshairs = {} self.selected = set() def add(self, x, y, _trace=None, transform=True): ''' add a crosshair to the zoom frame canvas ''' trace = self.getCurrentTraceName() if _trace == None else _trace color = self.available[trace]['color'] ch = Crosshairs(self.app.Dicom.zframe, x, y, color, transform) if trace not in self.crosshairs: self.crosshairs[trace] = [] self.crosshairs[trace].append(ch) return ch def remove(self, ch, write=True): ''' remove a crosshair from the zoom frame canvas ... doesn't actually remove it but instead just makes it "invisible" ''' ch.undraw() if write: self.write() return ch def move(self): ''' called when window resizes to move to correct relative locations''' # trace = self.getCurrentTraceName() if self.crosshairs: for trace in self.crosshairs: for ch in self.crosshairs[trace]: truex, truey = ch.getTrueCoords() ch.x, ch.y = ch.transformTrueToCoords(truex, truey) ch.dragTo((ch.x, ch.y)) def read(self): ''' read a list of crosshair coordinates from the metadata file ''' frame = self.app.frame for trace in self.available: try: newCrosshairs = [] for item in self.app.Data.getTraceCurrentFrame(trace): ch = self.add(item['x'], item['y'], _trace=trace, transform=False) if trace not in self.crosshairs: self.crosshairs[trace] = [] self.crosshairs[trace].append(ch) newCrosshairs.append(ch) self.app.Control.push({'type': 'add', 'chs': newCrosshairs}) except KeyError: pass def write(self): ''' write out the coordinates of all of our crosshairs to the metadata file: ''' trace = self.getCurrentTraceName() traces = [] # prepare trace data in format for metadata array if trace in self.crosshairs: for ch in self.crosshairs[trace]: if ch.isVisible: x, y = ch.getTrueCoords() data = {'x': x, 'y': y} if data not in traces: # add trace to temporary array for including in metadata array traces.append(data) # add to metadata array and update file self.app.Data.setCurrentTraceCurrentFrame(traces) # update tier labels for number of annotated frames self.app.TextGrid.updateTierLabels() def getCurrentTraceName(self): ''' return string of current trace name ''' try: return self.listbox.get(self.listbox.curselection()) except Exception as e: # tkinter.TclError? error('Can\'t select from empty listbox!', e) def setDefaultTraceName(self): ''' wrapper for changing the default trace ''' self.app.Data.setTopLevel('defaultTraceName', self.getCurrentTraceName()) def select(self, ch): ''' select a crosshairs ''' ch.select() self.selected.add(ch) def selectAll(self): ''' select all crosshairs ''' if self.getCurrentTraceName() in self.crosshairs: for ch in self.crosshairs[self.getCurrentTraceName()]: self.select(ch) def unselect(self, ch): ''' unselect a crosshairs ''' ch.unselect() self.selected.remove(ch) def unselectAll(self): ''' unselect all crosshairs ''' for ch in self.selected: ch.unselect() self.selected = set() def getNearClickAllTraces(self, click): ''' takes a click object ( (x,y) tuple ) and returns a list of crosshairs within _CROSSHAIR_SELECT_RADIUS first searches for crosshairs matching the current trace iterates thru the other traces if it doesnt find anything if nothing is found for any trace, returns None ''' # get nearby crosshairs from this trace nearby = self.getNearClickOneTrace(click, self.getCurrentTraceName()) if nearby != None: return nearby # otherwise else: # ... check our other traces to see if they contain any nearby guys for trace in self.available: nearby = self.getNearClickOneTrace(click, trace) # if we got something if nearby != None: # switch to that trace and exit the loop for i, item in enumerate(self.listbox.get(0, 'end')): if item == trace: self.listbox.selection_clear(0, 'end') self.listbox.select_set(i) return nearby return None def getNearClickOneTrace(self, click, trace): ''' takes a click object and a trace and returns a list of crosshairs within util.CROSSHAIR_SELECT_RADIUS of that click ''' # see if we clicked near any existing crosshairs possibleSelections = {} if trace in self.crosshairs: for ch in self.crosshairs[trace]: d = ch.getDistance(click) if d < util.CROSSHAIR_SELECT_RADIUS: if d in possibleSelections: possibleSelections[d].append(ch) else: possibleSelections[d] = [ch] # if we did ... if possibleSelections != {}: # ... get the closest one ... dMin = sorted(possibleSelections.keys())[0] # ... in case of a tie, select a random one ch = random.choice(possibleSelections[dMin]) return ch return None def copy(self, event=None): ''' copies relative positions of selected crosshairs for pasting''' # debug('copy') self.copied = [] if len(self.selected) > 0: for ch in self.selected: self.copied.append(ch.getTrueCoords()) def paste(self, event=None): ''' pastes copied crosshairs and add them to undo/redo buffer ''' if len(self.copied) > 0: newChs = [] for xy in self.copied: ch = self.add(xy[0], xy[1], transform=False) newChs.append(ch) self.write() self.app.Control.push({'type': 'add', 'chs': newChs}) def recolor(self, event=None, trace=None, color=None): ''' change the color of a particular trace ''' trace = self.getCurrentTraceName() if trace == None else trace # grab a new color and save our old color (for generating Control data) newColor = self.getRandomHexColor() if color == None else color oldColor = self.app.Data.getCurrentTraceColor() self.available[trace]['color'] = newColor self.app.Data.setTraceColor(trace, newColor) if trace in self.crosshairs: for ch in self.crosshairs[trace]: ch.recolor(newColor) if trace == None or color == None: self.app.Control.push({ 'type': 'recolor', 'trace': self.getCurrentTraceName(), 'color': oldColor }) self.redoQueue = [] # FIXME: get this to update the widget self.app.Trace.displayedColour = newColor # FIXME: also get the widget to update the colour! return oldColor def clear(self): ''' remove all crosshairs for the current trace ''' # now we remove all the traces and save deleted = [] trace = self.getCurrentTraceName() if trace in self.crosshairs: for ch in self.crosshairs[trace]: if ch.isVisible: deleted.append(ch) self.remove(ch, write=False) self.write() self.app.Control.push({'type': 'delete', 'chs': deleted}) def newTrace(self): ''' add a new trace to our listbox ''' # max length 12 chars (so it displays nicely) name = self.traceSV.get()[:12] # don't want to add traces we already have or empty strings if name not in self.available and len(name) > 0: # choose a random color color = self.getRandomHexColor() # save the new trace name and color to metadata & update vars self.available[name] = {'color': color, 'files': {}} self.app.Data.setTopLevel('traces', self.available) self.traceSV.set('') # update our listbox self.listbox.insert('end', name) self.listbox.selection_clear(0, 'end') self.listbox.select_set(len(self.available) - 1) def renameTrace(self, oldName=None, newName=None): ''' change a trace name from oldName -> newName ''' fromUndo = (oldName != None or newName != None) oldName = self.getCurrentTraceName() if oldName == None else oldName newName = self.traceSV.get()[:12] if newName == None else newName # don't overwrite anything if newName not in self.available and len(newName) > 0: # get data from the old name and change the dictionary key in the metadata data = self.available.pop(oldName) self.available[newName] = data self.app.Data.setTopLevel('traces', self.available) if oldName == self.app.Data.getTopLevel('defaultTraceName'): self.app.Data.setTopLevel('defaultTraceName', newName) self.traceSV.set('') # update our listbox index = self.listbox.curselection() self.listbox.delete(index) self.listbox.insert(index, newName) self.listbox.selection_clear(0, 'end') self.listbox.select_set(index) if not (fromUndo): self.app.Control.push({ 'type': 'rename', 'old': oldName, 'new': newName }) def getRandomHexColor(self): ''' helper for getting a random color ''' return '#%06x' % random.randint(0, 0xFFFFFF) def getWidget(self, widget, row=0, column=0, rowspan=1, columnspan=1, sticky=()): ''' helper for managing all of our widgets ''' return { 'widget': widget, 'row': row, 'rowspan': rowspan, 'column': column, 'columnspan': columnspan, 'sticky': sticky } def grid(self): ''' grid all of our widgets ''' for item in self.TkWidgets: item['widget'].grid(row=item['row'], column=item['column'], rowspan=item['rowspan'], columnspan=item['columnspan'], sticky=item['sticky']) self.listbox.pack(side='left', fill='y') self.scrollbar.pack(side='right', fill='y') def grid_remove(self): ''' remove all of our widgets from the grid ''' for item in self.TkWidgets: item['widget'].grid_remove() self.listbox.packforget() self.scrollbar.packforget()
class ShiftReduceApp(object): """ A graphical tool for exploring the shift-reduce parser. The tool displays the parser's stack and the remaining text, and allows the user to control the parser's operation. In particular, the user can shift tokens onto the stack, and can perform reductions on the top elements of the stack. A "step" button simply steps through the parsing process, performing the operations that ``nltk.parse.ShiftReduceParser`` would use. """ def __init__(self, grammar, sent, trace=0): self._sent = sent self._parser = SteppingShiftReduceParser(grammar, trace) # Set up the main window. self._top = Tk() self._top.title('Shift Reduce Parser Application') # Animations. animating_lock is a lock to prevent the demo # from performing new operations while it's animating. self._animating_lock = 0 self._animate = IntVar(self._top) self._animate.set(10) # = medium # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Initialize fonts. self._init_fonts(self._top) # Set up key bindings. self._init_bindings() # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_feedback(self._top) self._init_grammar(self._top) self._init_canvas(self._top) # A popup menu for reducing. self._reduce_menu = Menu(self._canvas, tearoff=0) # Reset the demo, and set the feedback frame to empty. self.reset() self._lastoper1['text'] = '' ######################################### ## Initialization Helpers ######################################### def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = tkinter.font.Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget('size')) self._boldfont = tkinter.font.Font(family='helvetica', weight='bold', size=self._size.get()) self._font = tkinter.font.Font(family='helvetica', size=self._size.get()) def _init_grammar(self, parent): # Grammar view. self._prodframe = listframe = Frame(parent) self._prodframe.pack(fill='both', side='left', padx=2) self._prodlist_label = Label(self._prodframe, font=self._boldfont, text='Available Reductions') self._prodlist_label.pack() self._prodlist = Listbox(self._prodframe, selectmode='single', relief='groove', background='white', foreground='#909090', font=self._font, selectforeground='#004040', selectbackground='#c0f0c0') self._prodlist.pack(side='right', fill='both', expand=1) self._productions = list(self._parser.grammar().productions()) for production in self._productions: self._prodlist.insert('end', (' %s' % production)) self._prodlist.config(height=min(len(self._productions), 25)) # Add a scrollbar if there are more than 25 productions. if 1:#len(self._productions) > 25: listscroll = Scrollbar(self._prodframe, orient='vertical') self._prodlist.config(yscrollcommand = listscroll.set) listscroll.config(command=self._prodlist.yview) listscroll.pack(side='left', fill='y') # If they select a production, apply it. self._prodlist.bind('<<ListboxSelect>>', self._prodlist_select) # When they hover over a production, highlight it. self._hover = -1 self._prodlist.bind('<Motion>', self._highlight_hover) self._prodlist.bind('<Leave>', self._clear_hover) def _init_bindings(self): # Quit self._top.bind('<Control-q>', self.destroy) self._top.bind('<Control-x>', self.destroy) self._top.bind('<Alt-q>', self.destroy) self._top.bind('<Alt-x>', self.destroy) # Ops (step, shift, reduce, undo) self._top.bind('<space>', self.step) self._top.bind('<s>', self.shift) self._top.bind('<Alt-s>', self.shift) self._top.bind('<Control-s>', self.shift) self._top.bind('<r>', self.reduce) self._top.bind('<Alt-r>', self.reduce) self._top.bind('<Control-r>', self.reduce) self._top.bind('<Delete>', self.reset) self._top.bind('<u>', self.undo) self._top.bind('<Alt-u>', self.undo) self._top.bind('<Control-u>', self.undo) self._top.bind('<Control-z>', self.undo) self._top.bind('<BackSpace>', self.undo) # Misc self._top.bind('<Control-p>', self.postscript) self._top.bind('<Control-h>', self.help) self._top.bind('<F1>', self.help) self._top.bind('<Control-g>', self.edit_grammar) self._top.bind('<Control-t>', self.edit_sentence) # Animation speed control self._top.bind('-', lambda e,a=self._animate:a.set(20)) self._top.bind('=', lambda e,a=self._animate:a.set(10)) self._top.bind('+', lambda e,a=self._animate:a.set(4)) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill='none', side='bottom') Button(buttonframe, text='Step', background='#90c0d0', foreground='black', command=self.step,).pack(side='left') Button(buttonframe, text='Shift', underline=0, background='#90f090', foreground='black', command=self.shift).pack(side='left') Button(buttonframe, text='Reduce', underline=0, background='#90f090', foreground='black', command=self.reduce).pack(side='left') Button(buttonframe, text='Undo', underline=0, background='#f0a0a0', foreground='black', command=self.undo).pack(side='left') def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label='Reset Parser', underline=0, command=self.reset, accelerator='Del') filemenu.add_command(label='Print to Postscript', underline=0, command=self.postscript, accelerator='Ctrl-p') filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-x') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) editmenu.add_command(label='Edit Grammar', underline=5, command=self.edit_grammar, accelerator='Ctrl-g') editmenu.add_command(label='Edit Text', underline=5, command=self.edit_sentence, accelerator='Ctrl-t') menubar.add_cascade(label='Edit', underline=0, menu=editmenu) rulemenu = Menu(menubar, tearoff=0) rulemenu.add_command(label='Step', underline=1, command=self.step, accelerator='Space') rulemenu.add_separator() rulemenu.add_command(label='Shift', underline=0, command=self.shift, accelerator='Ctrl-s') rulemenu.add_command(label='Reduce', underline=0, command=self.reduce, accelerator='Ctrl-r') rulemenu.add_separator() rulemenu.add_command(label='Undo', underline=0, command=self.undo, accelerator='Ctrl-u') menubar.add_cascade(label='Apply', underline=0, menu=rulemenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_checkbutton(label="Show Grammar", underline=0, variable=self._show_grammar, command=self._toggle_grammar) viewmenu.add_separator() viewmenu.add_radiobutton(label='Tiny', variable=self._size, underline=0, value=10, command=self.resize) viewmenu.add_radiobutton(label='Small', variable=self._size, underline=0, value=12, command=self.resize) viewmenu.add_radiobutton(label='Medium', variable=self._size, underline=0, value=14, command=self.resize) viewmenu.add_radiobutton(label='Large', variable=self._size, underline=0, value=18, command=self.resize) viewmenu.add_radiobutton(label='Huge', variable=self._size, underline=0, value=24, command=self.resize) menubar.add_cascade(label='View', underline=0, menu=viewmenu) animatemenu = Menu(menubar, tearoff=0) animatemenu.add_radiobutton(label="No Animation", underline=0, variable=self._animate, value=0) animatemenu.add_radiobutton(label="Slow Animation", underline=0, variable=self._animate, value=20, accelerator='-') animatemenu.add_radiobutton(label="Normal Animation", underline=0, variable=self._animate, value=10, accelerator='=') animatemenu.add_radiobutton(label="Fast Animation", underline=0, variable=self._animate, value=4, accelerator='+') menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label='About', underline=0, command=self.about) helpmenu.add_command(label='Instructions', underline=0, command=self.help, accelerator='F1') menubar.add_cascade(label='Help', underline=0, menu=helpmenu) parent.config(menu=menubar) def _init_feedback(self, parent): self._feedbackframe = feedbackframe = Frame(parent) feedbackframe.pack(fill='x', side='bottom', padx=3, pady=3) self._lastoper_label = Label(feedbackframe, text='Last Operation:', font=self._font) self._lastoper_label.pack(side='left') lastoperframe = Frame(feedbackframe, relief='sunken', border=1) lastoperframe.pack(fill='x', side='right', expand=1, padx=5) self._lastoper1 = Label(lastoperframe, foreground='#007070', background='#f0f0f0', font=self._font) self._lastoper2 = Label(lastoperframe, anchor='w', width=30, foreground='#004040', background='#f0f0f0', font=self._font) self._lastoper1.pack(side='left') self._lastoper2.pack(side='left', fill='x', expand=1) def _init_canvas(self, parent): self._cframe = CanvasFrame(parent, background='white', width=525, closeenough=10, border=2, relief='sunken') self._cframe.pack(expand=1, fill='both', side='top', pady=2) canvas = self._canvas = self._cframe.canvas() self._stackwidgets = [] self._rtextwidgets = [] self._titlebar = canvas.create_rectangle(0,0,0,0, fill='#c0f0f0', outline='black') self._exprline = canvas.create_line(0,0,0,0, dash='.') self._stacktop = canvas.create_line(0,0,0,0, fill='#408080') size = self._size.get()+4 self._stacklabel = TextWidget(canvas, 'Stack', color='#004040', font=self._boldfont) self._rtextlabel = TextWidget(canvas, 'Remaining Text', color='#004040', font=self._boldfont) self._cframe.add_widget(self._stacklabel) self._cframe.add_widget(self._rtextlabel) ######################################### ## Main draw procedure ######################################### def _redraw(self): scrollregion = self._canvas['scrollregion'].split() (cx1, cy1, cx2, cy2) = [int(c) for c in scrollregion] # Delete the old stack & rtext widgets. for stackwidget in self._stackwidgets: self._cframe.destroy_widget(stackwidget) self._stackwidgets = [] for rtextwidget in self._rtextwidgets: self._cframe.destroy_widget(rtextwidget) self._rtextwidgets = [] # Position the titlebar & exprline (x1, y1, x2, y2) = self._stacklabel.bbox() y = y2-y1+10 self._canvas.coords(self._titlebar, -5000, 0, 5000, y-4) self._canvas.coords(self._exprline, 0, y*2-10, 5000, y*2-10) # Position the titlebar labels.. (x1, y1, x2, y2) = self._stacklabel.bbox() self._stacklabel.move(5-x1, 3-y1) (x1, y1, x2, y2) = self._rtextlabel.bbox() self._rtextlabel.move(cx2-x2-5, 3-y1) # Draw the stack. stackx = 5 for tok in self._parser.stack(): if isinstance(tok, Tree): attribs = {'tree_color': '#4080a0', 'tree_width': 2, 'node_font': self._boldfont, 'node_color': '#006060', 'leaf_color': '#006060', 'leaf_font':self._font} widget = tree_to_treesegment(self._canvas, tok, **attribs) widget.label()['color'] = '#000000' else: widget = TextWidget(self._canvas, tok, color='#000000', font=self._font) widget.bind_click(self._popup_reduce) self._stackwidgets.append(widget) self._cframe.add_widget(widget, stackx, y) stackx = widget.bbox()[2] + 10 # Draw the remaining text. rtextwidth = 0 for tok in self._parser.remaining_text(): widget = TextWidget(self._canvas, tok, color='#000000', font=self._font) self._rtextwidgets.append(widget) self._cframe.add_widget(widget, rtextwidth, y) rtextwidth = widget.bbox()[2] + 4 # Allow enough room to shift the next token (for animations) if len(self._rtextwidgets) > 0: stackx += self._rtextwidgets[0].width() # Move the remaining text to the correct location (keep it # right-justified, when possible); and move the remaining text # label, if necessary. stackx = max(stackx, self._stacklabel.width()+25) rlabelwidth = self._rtextlabel.width()+10 if stackx >= cx2-max(rtextwidth, rlabelwidth): cx2 = stackx + max(rtextwidth, rlabelwidth) for rtextwidget in self._rtextwidgets: rtextwidget.move(4+cx2-rtextwidth, 0) self._rtextlabel.move(cx2-self._rtextlabel.bbox()[2]-5, 0) midx = (stackx + cx2-max(rtextwidth, rlabelwidth))/2 self._canvas.coords(self._stacktop, midx, 0, midx, 5000) (x1, y1, x2, y2) = self._stacklabel.bbox() # Set up binding to allow them to shift a token by dragging it. if len(self._rtextwidgets) > 0: def drag_shift(widget, midx=midx, self=self): if widget.bbox()[0] < midx: self.shift() else: self._redraw() self._rtextwidgets[0].bind_drag(drag_shift) self._rtextwidgets[0].bind_click(self.shift) # Draw the stack top. self._highlight_productions() def _draw_stack_top(self, widget): # hack.. midx = widget.bbox()[2]+50 self._canvas.coords(self._stacktop, midx, 0, midx, 5000) def _highlight_productions(self): # Highlight the productions that can be reduced. self._prodlist.selection_clear(0, 'end') for prod in self._parser.reducible_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) ######################################### ## Button Callbacks ######################################### def destroy(self, *e): if self._top is None: return self._top.destroy() self._top = None def reset(self, *e): self._parser.initialize(self._sent) self._lastoper1['text'] = 'Reset App' self._lastoper2['text'] = '' self._redraw() def step(self, *e): if self.reduce(): return True elif self.shift(): return True else: if list(self._parser.parses()): self._lastoper1['text'] = 'Finished:' self._lastoper2['text'] = 'Success' else: self._lastoper1['text'] = 'Finished:' self._lastoper2['text'] = 'Failure' def shift(self, *e): if self._animating_lock: return if self._parser.shift(): tok = self._parser.stack()[-1] self._lastoper1['text'] = 'Shift:' self._lastoper2['text'] = '%r' % tok if self._animate.get(): self._animate_shift() else: self._redraw() return True return False def reduce(self, *e): if self._animating_lock: return production = self._parser.reduce() if production: self._lastoper1['text'] = 'Reduce:' self._lastoper2['text'] = '%s' % production if self._animate.get(): self._animate_reduce() else: self._redraw() return production def undo(self, *e): if self._animating_lock: return if self._parser.undo(): self._redraw() def postscript(self, *e): self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) ######################################### ## Menubar callbacks ######################################### def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) #self._stacklabel['font'] = ('helvetica', -size-4, 'bold') #self._rtextlabel['font'] = ('helvetica', -size-4, 'bold') #self._lastoper_label['font'] = ('helvetica', -size) #self._lastoper1['font'] = ('helvetica', -size) #self._lastoper2['font'] = ('helvetica', -size) #self._prodlist['font'] = ('helvetica', -size) #self._prodlist_label['font'] = ('helvetica', -size-2, 'bold') self._redraw() def help(self, *e): # The default font's not very legible; try using 'fixed' instead. try: ShowText(self._top, 'Help: Shift-Reduce Parser Application', (__doc__ or '').strip(), width=75, font='fixed') except: ShowText(self._top, 'Help: Shift-Reduce Parser Application', (__doc__ or '').strip(), width=75) def about(self, *e): ABOUT = ("NLTK Shift-Reduce Parser Application\n"+ "Written by Edward Loper") TITLE = 'About: Shift-Reduce Parser Application' try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def edit_grammar(self, *e): CFGEditor(self._top, self._parser.grammar(), self.set_grammar) def set_grammar(self, grammar): self._parser.set_grammar(grammar) self._productions = list(grammar.productions()) self._prodlist.delete(0, 'end') for production in self._productions: self._prodlist.insert('end', (' %s' % production)) def edit_sentence(self, *e): sentence = " ".join(self._sent) title = 'Edit Text' instr = 'Enter a new sentence to parse.' EntryDialog(self._top, sentence, instr, self.set_sentence, title) def set_sentence(self, sent): self._sent = sent.split() #[XX] use tagged? self.reset() ######################################### ## Reduce Production Selection ######################################### def _toggle_grammar(self, *e): if self._show_grammar.get(): self._prodframe.pack(fill='both', side='left', padx=2, after=self._feedbackframe) self._lastoper1['text'] = 'Show Grammar' else: self._prodframe.pack_forget() self._lastoper1['text'] = 'Hide Grammar' self._lastoper2['text'] = '' def _prodlist_select(self, event): selection = self._prodlist.curselection() if len(selection) != 1: return index = int(selection[0]) production = self._parser.reduce(self._productions[index]) if production: self._lastoper1['text'] = 'Reduce:' self._lastoper2['text'] = '%s' % production if self._animate.get(): self._animate_reduce() else: self._redraw() else: # Reset the production selections. self._prodlist.selection_clear(0, 'end') for prod in self._parser.reducible_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) def _popup_reduce(self, widget): # Remove old commands. productions = self._parser.reducible_productions() if len(productions) == 0: return self._reduce_menu.delete(0, 'end') for production in productions: self._reduce_menu.add_command(label=str(production), command=self.reduce) self._reduce_menu.post(self._canvas.winfo_pointerx(), self._canvas.winfo_pointery()) ######################################### ## Animations ######################################### def _animate_shift(self): # What widget are we shifting? widget = self._rtextwidgets[0] # Where are we shifting from & to? right = widget.bbox()[0] if len(self._stackwidgets) == 0: left = 5 else: left = self._stackwidgets[-1].bbox()[2]+10 # Start animating. dt = self._animate.get() dx = (left-right)*1.0/dt self._animate_shift_frame(dt, widget, dx) def _animate_shift_frame(self, frame, widget, dx): if frame > 0: self._animating_lock = 1 widget.move(dx, 0) self._top.after(10, self._animate_shift_frame, frame-1, widget, dx) else: # but: stacktop?? # Shift the widget to the stack. del self._rtextwidgets[0] self._stackwidgets.append(widget) self._animating_lock = 0 # Display the available productions. self._draw_stack_top(widget) self._highlight_productions() def _animate_reduce(self): # What widgets are we shifting? numwidgets = len(self._parser.stack()[-1]) # number of children widgets = self._stackwidgets[-numwidgets:] # How far are we moving? if isinstance(widgets[0], TreeSegmentWidget): ydist = 15 + widgets[0].label().height() else: ydist = 15 + widgets[0].height() # Start animating. dt = self._animate.get() dy = ydist*2.0/dt self._animate_reduce_frame(dt/2, widgets, dy) def _animate_reduce_frame(self, frame, widgets, dy): if frame > 0: self._animating_lock = 1 for widget in widgets: widget.move(0, dy) self._top.after(10, self._animate_reduce_frame, frame-1, widgets, dy) else: del self._stackwidgets[-len(widgets):] for widget in widgets: self._cframe.remove_widget(widget) tok = self._parser.stack()[-1] if not isinstance(tok, Tree): raise ValueError() label = TextWidget(self._canvas, str(tok.label()), color='#006060', font=self._boldfont) widget = TreeSegmentWidget(self._canvas, label, widgets, width=2) (x1, y1, x2, y2) = self._stacklabel.bbox() y = y2-y1+10 if not self._stackwidgets: x = 5 else: x = self._stackwidgets[-1].bbox()[2] + 10 self._cframe.add_widget(widget, x, y) self._stackwidgets.append(widget) # Display the available productions. self._draw_stack_top(widget) self._highlight_productions() # # Delete the old widgets.. # del self._stackwidgets[-len(widgets):] # for widget in widgets: # self._cframe.destroy_widget(widget) # # # Make a new one. # tok = self._parser.stack()[-1] # if isinstance(tok, Tree): # attribs = {'tree_color': '#4080a0', 'tree_width': 2, # 'node_font': bold, 'node_color': '#006060', # 'leaf_color': '#006060', 'leaf_font':self._font} # widget = tree_to_treesegment(self._canvas, tok.type(), # **attribs) # widget.node()['color'] = '#000000' # else: # widget = TextWidget(self._canvas, tok.type(), # color='#000000', font=self._font) # widget.bind_click(self._popup_reduce) # (x1, y1, x2, y2) = self._stacklabel.bbox() # y = y2-y1+10 # if not self._stackwidgets: x = 5 # else: x = self._stackwidgets[-1].bbox()[2] + 10 # self._cframe.add_widget(widget, x, y) # self._stackwidgets.append(widget) #self._redraw() self._animating_lock = 0 ######################################### ## Hovering. ######################################### def _highlight_hover(self, event): # What production are we hovering over? index = self._prodlist.nearest(event.y) if self._hover == index: return # Clear any previous hover highlighting. self._clear_hover() # If the production corresponds to an available reduction, # highlight the stack. selection = [int(s) for s in self._prodlist.curselection()] if index in selection: rhslen = len(self._productions[index].rhs()) for stackwidget in self._stackwidgets[-rhslen:]: if isinstance(stackwidget, TreeSegmentWidget): stackwidget.label()['color'] = '#00a000' else: stackwidget['color'] = '#00a000' # Remember what production we're hovering over. self._hover = index def _clear_hover(self, *event): # Clear any previous hover highlighting. if self._hover == -1: return self._hover = -1 for stackwidget in self._stackwidgets: if isinstance(stackwidget, TreeSegmentWidget): stackwidget.label()['color'] = 'black' else: stackwidget['color'] = 'black'
class AutocompleteEntry(Entry): def __init__(self, lista, *args, **kwargs): Entry.__init__(self, *args, **kwargs) self.lista = lista self.var = self["textvariable"] if self.var == '': self.var = self["textvariable"] = StringVar() self.var.trace('w', self.changed) self.bind("<Right>", self.selection) self.bind("<Up>", self.up) self.bind("<Down>", self.down) self.lb_up = False def changed(self, name, index, mode): if self.var.get() == '': self.lb.destroy() self.lb_up = False else: words = self.comparison() if words: if not self.lb_up: self.lb = Listbox() self.lb.bind("<Double-Button-1>", self.selection) self.lb.bind("<Right>", self.selection) self.lb.place(x=self.winfo_x(), y=self.winfo_y() + self.winfo_height()) self.lb_up = True self.lb.delete(0, END) for w in words: self.lb.insert(END, w) else: if self.lb_up: self.lb.destroy() self.lb_up = False def selection(self, event): if self.lb_up: self.var.set(self.lb.get(ACTIVE)) self.lb.destroy() self.lb_up = False self.icursor(END) def up(self, event): if self.lb_up: if self.lb.curselection() == (): index = '0' else: index = self.lb.curselection()[0] if index != '0': self.lb.selection_clear(first=index) index = str(int(index) - 1) self.lb.selection_set(first=index) self.lb.activate(index) def down(self, event): if self.lb_up: if self.lb.curselection() == (): index = '0' else: index = self.lb.curselection()[0] if index != END: self.lb.selection_clear(first=index) index = str(int(index) + 1) self.lb.selection_set(first=index) self.lb.activate(index) def comparison(self): pattern = re.compile('.*' + self.var.get() + '.*') return [w for w in self.lista if re.match(pattern, w)]
class Ufd: """ Universal File Dialog - "UFD" Unopinionated, minimalist, reusable, slightly configurable, general-purpose file-dialog. """ def __init__(self, title: str = "Universal File Dialog", icon: str = "", show_hidden: bool = False, include_files: bool = True, multiselect: bool = True, select_dirs: bool = True, select_files: bool = True, unix_delimiter: bool = True, stdout: bool = False): """ Init kwargs as object attributes, save references to Tk PhotoImages, & define the widgets + layout """ if not isinstance(title, str): raise TypeError("Argument title must be type string.") self.title = title if icon: if not isinstance(icon, str): raise TypeError("Argument icon must be type string.") if not isfile(icon): raise FileNotFoundError(f"File not found: {icon}") self.icon = icon else: self.icon = "" if show_hidden: self.show_hidden = True else: self.show_hidden = False if include_files: self.include_files = True else: self.include_files = False if multiselect: self.multiselect = True else: self.multiselect = False if select_dirs: self.select_dirs = True else: self.select_dirs = False if select_files: self.select_files = True else: self.select_files = False if unix_delimiter: self.unix_delimiter = True else: self.unix_delimiter = False if stdout: self.stdout = True else: self.stdout = False # Tkinter: self.dialog = Tk() self.dialog.withdraw() self.dialog.title(self.title) self.dialog.minsize(width=300, height=200) self.dialog.geometry("500x300") self.dialog.update_idletasks() self.file_icon = PhotoImage(file=f"{dirname(__file__)}/file.gif", master=self.dialog).subsample(50) self.folder_icon = PhotoImage(file=f"{dirname(__file__)}/folder.gif", master=self.dialog).subsample(15) self.disk_icon = PhotoImage(file=f"{dirname(__file__)}/disk.gif", master=self.dialog).subsample(15) if self.icon: self.dialog.iconbitmap(self.icon) else: self.dialog.iconbitmap(f"{dirname(__file__)}/icon.ico") # Widgets: self.paneview = PanedWindow( self.dialog, sashwidth=7, bg="#cccccc", bd=0, ) self.left_pane = PanedWindow(self.paneview) self.right_pane = PanedWindow(self.paneview) self.paneview.add(self.left_pane) self.paneview.add(self.right_pane) self.treeview_x_scrollbar = Scrollbar(self.left_pane, orient="horizontal") self.treeview_y_scrollbar = Scrollbar(self.left_pane, orient="vertical") self.list_box_x_scrollbar = Scrollbar(self.right_pane, orient="horizontal") self.list_box_y_scrollbar = Scrollbar(self.right_pane, orient="vertical") # tstyle = Style().configure(".", ) self.treeview = Treeview( self.left_pane, xscrollcommand=self.treeview_x_scrollbar.set, yscrollcommand=self.treeview_y_scrollbar.set, show="tree", selectmode="browse", # style=tstyle ) self.list_box = Listbox(self.right_pane, xscrollcommand=self.list_box_x_scrollbar.set, yscrollcommand=self.list_box_y_scrollbar.set, width=34, highlightthickness=0, bd=2, relief="ridge") if self.multiselect: self.list_box.config(selectmode="extended") else: self.list_box.config(selectmode="browse") self.cancel_button = Button(self.left_pane, text="Cancel", command=self.cancel) self.submit_button = Button(self.right_pane, text="Submit", command=self.submit) self.treeview_x_scrollbar.config(command=self.treeview.xview) self.treeview_y_scrollbar.config(command=self.treeview.yview) self.list_box_x_scrollbar.config(command=self.list_box.xview) self.list_box_y_scrollbar.config(command=self.list_box.yview) #Layout: self.dialog.rowconfigure(0, weight=1) self.dialog.columnconfigure(0, weight=1) self.left_pane.grid_rowconfigure(0, weight=1) self.left_pane.grid_columnconfigure(0, weight=1) self.right_pane.grid_rowconfigure(0, weight=1) self.right_pane.grid_columnconfigure(0, weight=1) self.paneview.paneconfigure( self.left_pane, minsize=100, #Start off w/ the sash centered in the GUI: width=(self.dialog.winfo_width() / 2) - ceil( (self.paneview.cget("sashwidth") * 1.5)), ) self.paneview.paneconfigure(self.right_pane, minsize=100) self.paneview.grid(row=0, column=0, sticky="nsew") self.treeview.grid(row=0, column=0, sticky="nsew") self.treeview_y_scrollbar.grid(row=0, column=1, sticky="ns") self.treeview_x_scrollbar.grid(row=1, column=0, columnspan=2, sticky="ew") self.list_box.grid(row=0, column=0, sticky="nsew") self.list_box_y_scrollbar.grid(row=0, column=1, sticky="ns") self.list_box_x_scrollbar.grid(row=1, column=0, columnspan=2, sticky="ew") self.cancel_button.grid(row=2, column=0, sticky="w", padx=10, pady=10) self.submit_button.grid(row=2, column=0, columnspan=2, sticky="e", padx=10, pady=10) #Bindings, Protocols, & Misc: self.dialog.bind("<Control-w>", self.cancel) self.treeview.bind("<<TreeviewSelect>>", self.treeview_select) self.treeview.bind("<Double-Button-1>", self.dialog_populate) self.treeview.bind("<Return>", self.dialog_populate) self.treeview.bind("<Right>", self.dialog_populate) self.list_box.bind("<<ListboxSelect>>", self.list_box_select) self.list_box.bind("<Return>", self.submit) self.dialog.protocol("WM_DELETE_WINDOW", self.cancel) self.dialog_selection = deque() self.selection_paths = deque() for disk in self.get_disks(): self.treeview.insert( "", index="end", text=disk, image=self.disk_icon, ) self.dialog.focus() def __call__(self): """ Display dialog & return selection """ (width_offset, height_offset) = self.get_offset(self.dialog) self.dialog.geometry(f"+{width_offset}+{height_offset}") self.dialog.update_idletasks() self.dialog.deiconify() self.dialog.wait_window() for i, path in enumerate(self.dialog_selection): if self.unix_delimiter: self.dialog_selection[i] = sub("\\\\", "/", path) else: self.dialog_selection[i] = sub("/", "\\\\", path) if self.stdout: [print(item) for item in self.dialog_selection] return list(self.dialog_selection) def __str__(self): """ Return own address """ return "Universal File Dialog"\ f" @ {hex(id(self))}" def __repr__(self): """ Return full string representation of constructor signature """ return f"Ufd("\ f"title=\"{self.title}\","\ f" icon=\"{self.icon}\","\ f" show_hidden={self.show_hidden},"\ f" include_files={self.include_files},"\ f" multiselect={self.multiselect},"\ f" select_dirs={self.select_dirs},"\ f" select_files={self.select_files},"\ f" unix_delimiter={self.unix_delimiter})"\ f" stdout={self.stdout})"\ f" @ {hex(id(self))}" @staticmethod def get_offset(tk_window): """ Returns an appropriate offset for a given tkinter toplevel, such that it always is created center screen on the primary display. """ width_offset = int((tk_window.winfo_screenwidth() / 2) - (tk_window.winfo_width() / 2)) height_offset = int((tk_window.winfo_screenheight() / 2) - (tk_window.winfo_height() / 2)) return (width_offset, height_offset) @staticmethod def get_disks(): """ Returns all mounted disks (for Windows) >> ["A:", "B:", "C:"] """ if system() != "Windows": raise OSError("For use with Windows platforms.") logicaldisks = run(["wmic", "logicaldisk", "get", "name"], capture_output=True) return findall("[A-Z]:", str(logicaldisks.stdout)) @staticmethod def list_dir(path, force=False): """ Reads a directory with a shell call to dir. Truthiness of bool force determines whether hidden items are returned or not. (For Windows) """ path = sub("/", "\\\\", path) if force: dir_listing = run(["dir", path, "/b", "/a"], shell=True, capture_output=True) else: dir_listing = run(["dir", path, "/b"], shell=True, capture_output=True) output = dir_listing.stdout err = dir_listing.stderr if not output: return [] if err: err = err.decode("utf-8") raise Exception(err) str_output = output.decode("utf-8") list_output = re_split("\r\n", str_output) return sorted([item for item in list_output if item]) def climb(self, item): """ Builds & returns a complete path to root directory, including the item name itself as the path tail. An extra delimiter is appeneded for the subsequent child node, which is normalized in dialog_populate() """ item_text = self.treeview.item(item)["text"] parent = self.treeview.parent(item) path = "" parents = deque() while parent: parents.append(self.treeview.item(parent)["text"] + "/") parent = self.treeview.parent(parent) for parent in reversed(parents): path += parent path += item_text + "/" return path def dialog_populate(self, event=None): """ Dynamically populates & updates the treeview, listbox, and keeps track of the full paths corresponding to each item in the listbox """ if not self.treeview.focus(): return self.treeview.column("#0", width=1000) existing_children = self.treeview.get_children(self.treeview.focus()) [self.treeview.delete(child) for child in existing_children] self.list_box.delete(0, "end") self.selection_paths.clear() focus_item = self.treeview.focus() path = self.climb(focus_item) if self.show_hidden: children = self.list_dir(path, force=True) else: children = self.list_dir(path) for child in children: if isdir(path + child): self.treeview.insert(focus_item, index="end", text=child, image=self.folder_icon) if self.select_dirs: self.list_box.insert("end", child) self.selection_paths.append(path + child) elif isfile(path + child): if self.include_files: self.treeview.insert(focus_item, index="end", text=child, image=self.file_icon) if self.select_files: self.list_box.insert("end", child) self.list_box.itemconfig("end", {"bg": "#EAEAEA"}) self.selection_paths.append(path + child) if isfile(normpath(path)): (head, tail) = path_split(normpath(path)) head = sub("\\\\", "/", head) self.list_box.insert("end", tail) self.selection_paths.append(head + "/" + tail) self.list_box.itemconfig("end", {"bg": "#EAEAEA"}) def list_box_select(self, event=None): """ Dynamically refresh the dialog selection with what's selected in the listbox (Callback for <<ListboxSelect>>). """ self.dialog_selection.clear() for i in self.list_box.curselection(): self.dialog_selection.append(self.selection_paths[i]) def treeview_select(self, event=None): """ Dynamically refresh the dialog selection with what's selected in the treeview (Callback for <<TreeviewSelect>>). """ for i in self.list_box.curselection(): self.list_box.selection_clear(i) self.dialog_selection.clear() item = normpath(self.climb(self.treeview.focus())) self.dialog_selection.append(item) def submit(self, event=None): """ Satisfies wait_window() in self.__call__() and validates selection (Callback for <Return>, <Button-1> on file_list, submit_button) """ if self.select_dirs == False: for item in self.dialog_selection: if isdir(item): messagebox.showwarning( "Error - Invalid Selection", "Unable to select directory. Please select a file(s).") return if self.select_files == False: for item in self.dialog_selection: if isfile(item): messagebox.showwarning( "Error - Invalid Selection", "Unable to select file. Please select a folder(s)") return self.dialog.destroy() def cancel(self, event=None): """ Satisfies wait_window() in self.__call__() (Callback for <Button-1> on cancel_button) (Callback for protocol "WM_DELETE_WINDOW" on self.dialog) """ self.dialog_selection.clear() self.dialog.destroy()
class DrtGlueDemo(object): def __init__(self, examples): # Set up the main window. self._top = Tk() self._top.title("DRT Glue Demo") # Set up key bindings. self._init_bindings() # Initialize the fonts.self._error = None self._init_fonts(self._top) self._examples = examples self._readingCache = [None for example in examples] # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Set the data to None self._curExample = -1 self._readings = [] self._drs = None self._drsWidget = None self._error = None self._init_glue() # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_exampleListbox(self._top) self._init_readingListbox(self._top) self._init_canvas(self._top) # Resize callback self._canvas.bind("<Configure>", self._configure) ######################################### ## Initialization Helpers ######################################### def _init_glue(self): tagger = RegexpTagger( [ ("^(David|Mary|John)$", "NNP"), ("^(walks|sees|eats|chases|believes|gives|sleeps|chases|persuades|tries|seems|leaves)$", "VB"), ("^(go|order|vanish|find|approach)$", "VB"), ("^(a)$", "ex_quant"), ("^(every)$", "univ_quant"), ("^(sandwich|man|dog|pizza|unicorn|cat|senator)$", "NN"), ("^(big|gray|former)$", "JJ"), ("^(him|himself)$", "PRP"), ] ) depparser = MaltParser(tagger=tagger) self._glue = DrtGlue(depparser=depparser, remove_duplicates=False) def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget("size")) self._boldfont = Font(family="helvetica", weight="bold", size=self._size.get()) self._font = Font(family="helvetica", size=self._size.get()) if self._size.get() < 0: big = self._size.get() - 2 else: big = self._size.get() + 2 self._bigfont = Font(family="helvetica", weight="bold", size=big) def _init_exampleListbox(self, parent): self._exampleFrame = listframe = Frame(parent) self._exampleFrame.pack(fill="both", side="left", padx=2) self._exampleList_label = Label(self._exampleFrame, font=self._boldfont, text="Examples") self._exampleList_label.pack() self._exampleList = Listbox( self._exampleFrame, selectmode="single", relief="groove", background="white", foreground="#909090", font=self._font, selectforeground="#004040", selectbackground="#c0f0c0", ) self._exampleList.pack(side="right", fill="both", expand=1) for example in self._examples: self._exampleList.insert("end", (" %s" % example)) self._exampleList.config(height=min(len(self._examples), 25), width=40) # Add a scrollbar if there are more than 25 examples. if len(self._examples) > 25: listscroll = Scrollbar(self._exampleFrame, orient="vertical") self._exampleList.config(yscrollcommand=listscroll.set) listscroll.config(command=self._exampleList.yview) listscroll.pack(side="left", fill="y") # If they select a example, apply it. self._exampleList.bind("<<ListboxSelect>>", self._exampleList_select) def _init_readingListbox(self, parent): self._readingFrame = listframe = Frame(parent) self._readingFrame.pack(fill="both", side="left", padx=2) self._readingList_label = Label(self._readingFrame, font=self._boldfont, text="Readings") self._readingList_label.pack() self._readingList = Listbox( self._readingFrame, selectmode="single", relief="groove", background="white", foreground="#909090", font=self._font, selectforeground="#004040", selectbackground="#c0f0c0", ) self._readingList.pack(side="right", fill="both", expand=1) # Add a scrollbar if there are more than 25 examples. listscroll = Scrollbar(self._readingFrame, orient="vertical") self._readingList.config(yscrollcommand=listscroll.set) listscroll.config(command=self._readingList.yview) listscroll.pack(side="right", fill="y") self._populate_readingListbox() def _populate_readingListbox(self): # Populate the listbox with integers self._readingList.delete(0, "end") for i in range(len(self._readings)): self._readingList.insert("end", (" %s" % (i + 1))) self._readingList.config(height=min(len(self._readings), 25), width=5) # If they select a example, apply it. self._readingList.bind("<<ListboxSelect>>", self._readingList_select) def _init_bindings(self): # Key bindings are a good thing. self._top.bind("<Control-q>", self.destroy) self._top.bind("<Control-x>", self.destroy) self._top.bind("<Escape>", self.destroy) self._top.bind("n", self.next) self._top.bind("<space>", self.next) self._top.bind("p", self.prev) self._top.bind("<BackSpace>", self.prev) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill="none", side="bottom", padx=3, pady=2) Button(buttonframe, text="Prev", background="#90c0d0", foreground="black", command=self.prev).pack(side="left") Button(buttonframe, text="Next", background="#90c0d0", foreground="black", command=self.next).pack(side="left") def _configure(self, event): self._autostep = 0 (x1, y1, x2, y2) = self._cframe.scrollregion() y2 = event.height - 6 self._canvas["scrollregion"] = "%d %d %d %d" % (x1, y1, x2, y2) self._redraw() def _init_canvas(self, parent): self._cframe = CanvasFrame( parent, background="white", # width=525, height=250, closeenough=10, border=2, relief="sunken", ) self._cframe.pack(expand=1, fill="both", side="top", pady=2) canvas = self._canvas = self._cframe.canvas() # Initially, there's no tree or text self._tree = None self._textwidgets = [] self._textline = None def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="Exit", underline=1, command=self.destroy, accelerator="q") menubar.add_cascade(label="File", underline=0, menu=filemenu) actionmenu = Menu(menubar, tearoff=0) actionmenu.add_command(label="Next", underline=0, command=self.next, accelerator="n, Space") actionmenu.add_command(label="Previous", underline=0, command=self.prev, accelerator="p, Backspace") menubar.add_cascade(label="Action", underline=0, menu=actionmenu) optionmenu = Menu(menubar, tearoff=0) optionmenu.add_checkbutton( label="Remove Duplicates", underline=0, variable=self._glue.remove_duplicates, command=self._toggle_remove_duplicates, accelerator="r", ) menubar.add_cascade(label="Options", underline=0, menu=optionmenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_radiobutton(label="Tiny", variable=self._size, underline=0, value=10, command=self.resize) viewmenu.add_radiobutton(label="Small", variable=self._size, underline=0, value=12, command=self.resize) viewmenu.add_radiobutton(label="Medium", variable=self._size, underline=0, value=14, command=self.resize) viewmenu.add_radiobutton(label="Large", variable=self._size, underline=0, value=18, command=self.resize) viewmenu.add_radiobutton(label="Huge", variable=self._size, underline=0, value=24, command=self.resize) menubar.add_cascade(label="View", underline=0, menu=viewmenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label="About", underline=0, command=self.about) menubar.add_cascade(label="Help", underline=0, menu=helpmenu) parent.config(menu=menubar) ######################################### ## Main draw procedure ######################################### def _redraw(self): canvas = self._canvas # Delete the old DRS, widgets, etc. if self._drsWidget is not None: self._drsWidget.clear() if self._drs: self._drsWidget = DrsWidget(self._canvas, self._drs) self._drsWidget.draw() if self._error: self._drsWidget = DrsWidget(self._canvas, self._error) self._drsWidget.draw() ######################################### ## Button Callbacks ######################################### def destroy(self, *e): self._autostep = 0 if self._top is None: return self._top.destroy() self._top = None def prev(self, *e): selection = self._readingList.curselection() readingListSize = self._readingList.size() # there are readings if readingListSize > 0: # if one reading is currently selected if len(selection) == 1: index = int(selection[0]) # if it's on (or before) the first item if index <= 0: self._select_previous_example() else: self._readingList_store_selection(index - 1) else: # select its first reading self._readingList_store_selection(readingListSize - 1) else: self._select_previous_example() def _select_previous_example(self): # if the current example is not the first example if self._curExample > 0: self._exampleList_store_selection(self._curExample - 1) else: # go to the last example self._exampleList_store_selection(len(self._examples) - 1) def next(self, *e): selection = self._readingList.curselection() readingListSize = self._readingList.size() # if there are readings if readingListSize > 0: # if one reading is currently selected if len(selection) == 1: index = int(selection[0]) # if it's on (or past) the last item if index >= (readingListSize - 1): self._select_next_example() else: self._readingList_store_selection(index + 1) else: # select its first reading self._readingList_store_selection(0) else: self._select_next_example() def _select_next_example(self): # if the current example is not the last example if self._curExample < len(self._examples) - 1: self._exampleList_store_selection(self._curExample + 1) else: # go to the first example self._exampleList_store_selection(0) def about(self, *e): ABOUT = "NLTK Discourse Representation Theory (DRT) Glue Semantics Demo\n" + "Written by Daniel H. Garrette" TITLE = "About: NLTK DRT Glue Demo" try: from tkMessageBox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def postscript(self, *e): self._autostep = 0 self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) self._bigfont.configure(size=-(abs(size + 2))) self._redraw() def _toggle_remove_duplicates(self): self._glue.remove_duplicates = not self._glue.remove_duplicates self._exampleList.selection_clear(0, "end") self._readings = [] self._populate_readingListbox() self._readingCache = [None for ex in self._examples] self._curExample = -1 self._error = None self._drs = None self._redraw() def _exampleList_select(self, event): selection = self._exampleList.curselection() if len(selection) != 1: return self._exampleList_store_selection(int(selection[0])) def _exampleList_store_selection(self, index): self._curExample = index example = self._examples[index] self._exampleList.selection_clear(0, "end") if example: cache = self._readingCache[index] if cache: if isinstance(cache, list): self._readings = cache self._error = None else: self._readings = [] self._error = cache else: try: self._readings = self._glue.parse_to_meaning(example) self._error = None self._readingCache[index] = self._readings except Exception as e: self._readings = [] self._error = DrtVariableExpression(Variable("Error: " + str(e))) self._readingCache[index] = self._error # add a star to the end of the example self._exampleList.delete(index) self._exampleList.insert(index, (" %s *" % example)) self._exampleList.config(height=min(len(self._examples), 25), width=40) self._populate_readingListbox() self._exampleList.selection_set(index) self._drs = None self._redraw() def _readingList_select(self, event): selection = self._readingList.curselection() if len(selection) != 1: return self._readingList_store_selection(int(selection[0])) def _readingList_store_selection(self, index): reading = self._readings[index] self._readingList.selection_clear(0, "end") if reading: self._readingList.selection_set(index) self._drs = reading.simplify().normalize().resolve_anaphora() self._redraw()
class ShiftReduceApp(object): """ A graphical tool for exploring the shift-reduce parser. The tool displays the parser's stack and the remaining text, and allows the user to control the parser's operation. In particular, the user can shift tokens onto the stack, and can perform reductions on the top elements of the stack. A "step" button simply steps through the parsing process, performing the operations that ``nltk.parse.ShiftReduceParser`` would use. """ def __init__(self, grammar, sent, trace=0): self._sent = sent self._parser = SteppingShiftReduceParser(grammar, trace) # Set up the main window. self._top = Tk() self._top.title('Shift Reduce Parser Application') # Animations. animating_lock is a lock to prevent the demo # from performing new operations while it's animating. self._animating_lock = 0 self._animate = IntVar(self._top) self._animate.set(10) # = medium # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Initialize fonts. self._init_fonts(self._top) # Set up key bindings. self._init_bindings() # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_feedback(self._top) self._init_grammar(self._top) self._init_canvas(self._top) # A popup menu for reducing. self._reduce_menu = Menu(self._canvas, tearoff=0) # Reset the demo, and set the feedback frame to empty. self.reset() self._lastoper1['text'] = '' ######################################### ## Initialization Helpers ######################################### def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = tkinter.font.Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget('size')) self._boldfont = tkinter.font.Font(family='helvetica', weight='bold', size=self._size.get()) self._font = tkinter.font.Font(family='helvetica', size=self._size.get()) def _init_grammar(self, parent): # Grammar view. self._prodframe = listframe = Frame(parent) self._prodframe.pack(fill='both', side='left', padx=2) self._prodlist_label = Label(self._prodframe, font=self._boldfont, text='Available Reductions') self._prodlist_label.pack() self._prodlist = Listbox(self._prodframe, selectmode='single', relief='groove', background='white', foreground='#909090', font=self._font, selectforeground='#004040', selectbackground='#c0f0c0') self._prodlist.pack(side='right', fill='both', expand=1) self._productions = list(self._parser.grammar().productions()) for production in self._productions: self._prodlist.insert('end', (' %s' % production)) self._prodlist.config(height=min(len(self._productions), 25)) # Add a scrollbar if there are more than 25 productions. if 1: #len(self._productions) > 25: listscroll = Scrollbar(self._prodframe, orient='vertical') self._prodlist.config(yscrollcommand=listscroll.set) listscroll.config(command=self._prodlist.yview) listscroll.pack(side='left', fill='y') # If they select a production, apply it. self._prodlist.bind('<<ListboxSelect>>', self._prodlist_select) # When they hover over a production, highlight it. self._hover = -1 self._prodlist.bind('<Motion>', self._highlight_hover) self._prodlist.bind('<Leave>', self._clear_hover) def _init_bindings(self): # Quit self._top.bind('<Control-q>', self.destroy) self._top.bind('<Control-x>', self.destroy) self._top.bind('<Alt-q>', self.destroy) self._top.bind('<Alt-x>', self.destroy) # Ops (step, shift, reduce, undo) self._top.bind('<space>', self.step) self._top.bind('<s>', self.shift) self._top.bind('<Alt-s>', self.shift) self._top.bind('<Control-s>', self.shift) self._top.bind('<r>', self.reduce) self._top.bind('<Alt-r>', self.reduce) self._top.bind('<Control-r>', self.reduce) self._top.bind('<Delete>', self.reset) self._top.bind('<u>', self.undo) self._top.bind('<Alt-u>', self.undo) self._top.bind('<Control-u>', self.undo) self._top.bind('<Control-z>', self.undo) self._top.bind('<BackSpace>', self.undo) # Misc self._top.bind('<Control-p>', self.postscript) self._top.bind('<Control-h>', self.help) self._top.bind('<F1>', self.help) self._top.bind('<Control-g>', self.edit_grammar) self._top.bind('<Control-t>', self.edit_sentence) # Animation speed control self._top.bind('-', lambda e, a=self._animate: a.set(20)) self._top.bind('=', lambda e, a=self._animate: a.set(10)) self._top.bind('+', lambda e, a=self._animate: a.set(4)) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill='none', side='bottom') Button( buttonframe, text='Step', background='#90c0d0', foreground='black', command=self.step, ).pack(side='left') Button(buttonframe, text='Shift', underline=0, background='#90f090', foreground='black', command=self.shift).pack(side='left') Button(buttonframe, text='Reduce', underline=0, background='#90f090', foreground='black', command=self.reduce).pack(side='left') Button(buttonframe, text='Undo', underline=0, background='#f0a0a0', foreground='black', command=self.undo).pack(side='left') def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label='Reset Parser', underline=0, command=self.reset, accelerator='Del') filemenu.add_command(label='Print to Postscript', underline=0, command=self.postscript, accelerator='Ctrl-p') filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-x') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) editmenu.add_command(label='Edit Grammar', underline=5, command=self.edit_grammar, accelerator='Ctrl-g') editmenu.add_command(label='Edit Text', underline=5, command=self.edit_sentence, accelerator='Ctrl-t') menubar.add_cascade(label='Edit', underline=0, menu=editmenu) rulemenu = Menu(menubar, tearoff=0) rulemenu.add_command(label='Step', underline=1, command=self.step, accelerator='Space') rulemenu.add_separator() rulemenu.add_command(label='Shift', underline=0, command=self.shift, accelerator='Ctrl-s') rulemenu.add_command(label='Reduce', underline=0, command=self.reduce, accelerator='Ctrl-r') rulemenu.add_separator() rulemenu.add_command(label='Undo', underline=0, command=self.undo, accelerator='Ctrl-u') menubar.add_cascade(label='Apply', underline=0, menu=rulemenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_checkbutton(label="Show Grammar", underline=0, variable=self._show_grammar, command=self._toggle_grammar) viewmenu.add_separator() viewmenu.add_radiobutton(label='Tiny', variable=self._size, underline=0, value=10, command=self.resize) viewmenu.add_radiobutton(label='Small', variable=self._size, underline=0, value=12, command=self.resize) viewmenu.add_radiobutton(label='Medium', variable=self._size, underline=0, value=14, command=self.resize) viewmenu.add_radiobutton(label='Large', variable=self._size, underline=0, value=18, command=self.resize) viewmenu.add_radiobutton(label='Huge', variable=self._size, underline=0, value=24, command=self.resize) menubar.add_cascade(label='View', underline=0, menu=viewmenu) animatemenu = Menu(menubar, tearoff=0) animatemenu.add_radiobutton(label="No Animation", underline=0, variable=self._animate, value=0) animatemenu.add_radiobutton(label="Slow Animation", underline=0, variable=self._animate, value=20, accelerator='-') animatemenu.add_radiobutton(label="Normal Animation", underline=0, variable=self._animate, value=10, accelerator='=') animatemenu.add_radiobutton(label="Fast Animation", underline=0, variable=self._animate, value=4, accelerator='+') menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label='About', underline=0, command=self.about) helpmenu.add_command(label='Instructions', underline=0, command=self.help, accelerator='F1') menubar.add_cascade(label='Help', underline=0, menu=helpmenu) parent.config(menu=menubar) def _init_feedback(self, parent): self._feedbackframe = feedbackframe = Frame(parent) feedbackframe.pack(fill='x', side='bottom', padx=3, pady=3) self._lastoper_label = Label(feedbackframe, text='Last Operation:', font=self._font) self._lastoper_label.pack(side='left') lastoperframe = Frame(feedbackframe, relief='sunken', border=1) lastoperframe.pack(fill='x', side='right', expand=1, padx=5) self._lastoper1 = Label(lastoperframe, foreground='#007070', background='#f0f0f0', font=self._font) self._lastoper2 = Label(lastoperframe, anchor='w', width=30, foreground='#004040', background='#f0f0f0', font=self._font) self._lastoper1.pack(side='left') self._lastoper2.pack(side='left', fill='x', expand=1) def _init_canvas(self, parent): self._cframe = CanvasFrame(parent, background='white', width=525, closeenough=10, border=2, relief='sunken') self._cframe.pack(expand=1, fill='both', side='top', pady=2) canvas = self._canvas = self._cframe.canvas() self._stackwidgets = [] self._rtextwidgets = [] self._titlebar = canvas.create_rectangle(0, 0, 0, 0, fill='#c0f0f0', outline='black') self._exprline = canvas.create_line(0, 0, 0, 0, dash='.') self._stacktop = canvas.create_line(0, 0, 0, 0, fill='#408080') size = self._size.get() + 4 self._stacklabel = TextWidget(canvas, 'Stack', color='#004040', font=self._boldfont) self._rtextlabel = TextWidget(canvas, 'Remaining Text', color='#004040', font=self._boldfont) self._cframe.add_widget(self._stacklabel) self._cframe.add_widget(self._rtextlabel) ######################################### ## Main draw procedure ######################################### def _redraw(self): scrollregion = self._canvas['scrollregion'].split() (cx1, cy1, cx2, cy2) = [int(c) for c in scrollregion] # Delete the old stack & rtext widgets. for stackwidget in self._stackwidgets: self._cframe.destroy_widget(stackwidget) self._stackwidgets = [] for rtextwidget in self._rtextwidgets: self._cframe.destroy_widget(rtextwidget) self._rtextwidgets = [] # Position the titlebar & exprline (x1, y1, x2, y2) = self._stacklabel.bbox() y = y2 - y1 + 10 self._canvas.coords(self._titlebar, -5000, 0, 5000, y - 4) self._canvas.coords(self._exprline, 0, y * 2 - 10, 5000, y * 2 - 10) # Position the titlebar labels.. (x1, y1, x2, y2) = self._stacklabel.bbox() self._stacklabel.move(5 - x1, 3 - y1) (x1, y1, x2, y2) = self._rtextlabel.bbox() self._rtextlabel.move(cx2 - x2 - 5, 3 - y1) # Draw the stack. stackx = 5 for tok in self._parser.stack(): if isinstance(tok, Tree): attribs = { 'tree_color': '#4080a0', 'tree_width': 2, 'node_font': self._boldfont, 'node_color': '#006060', 'leaf_color': '#006060', 'leaf_font': self._font } widget = tree_to_treesegment(self._canvas, tok, **attribs) widget.label()['color'] = '#000000' else: widget = TextWidget(self._canvas, tok, color='#000000', font=self._font) widget.bind_click(self._popup_reduce) self._stackwidgets.append(widget) self._cframe.add_widget(widget, stackx, y) stackx = widget.bbox()[2] + 10 # Draw the remaining text. rtextwidth = 0 for tok in self._parser.remaining_text(): widget = TextWidget(self._canvas, tok, color='#000000', font=self._font) self._rtextwidgets.append(widget) self._cframe.add_widget(widget, rtextwidth, y) rtextwidth = widget.bbox()[2] + 4 # Allow enough room to shift the next token (for animations) if len(self._rtextwidgets) > 0: stackx += self._rtextwidgets[0].width() # Move the remaining text to the correct location (keep it # right-justified, when possible); and move the remaining text # label, if necessary. stackx = max(stackx, self._stacklabel.width() + 25) rlabelwidth = self._rtextlabel.width() + 10 if stackx >= cx2 - max(rtextwidth, rlabelwidth): cx2 = stackx + max(rtextwidth, rlabelwidth) for rtextwidget in self._rtextwidgets: rtextwidget.move(4 + cx2 - rtextwidth, 0) self._rtextlabel.move(cx2 - self._rtextlabel.bbox()[2] - 5, 0) midx = (stackx + cx2 - max(rtextwidth, rlabelwidth)) / 2 self._canvas.coords(self._stacktop, midx, 0, midx, 5000) (x1, y1, x2, y2) = self._stacklabel.bbox() # Set up binding to allow them to shift a token by dragging it. if len(self._rtextwidgets) > 0: def drag_shift(widget, midx=midx, self=self): if widget.bbox()[0] < midx: self.shift() else: self._redraw() self._rtextwidgets[0].bind_drag(drag_shift) self._rtextwidgets[0].bind_click(self.shift) # Draw the stack top. self._highlight_productions() def _draw_stack_top(self, widget): # hack.. midx = widget.bbox()[2] + 50 self._canvas.coords(self._stacktop, midx, 0, midx, 5000) def _highlight_productions(self): # Highlight the productions that can be reduced. self._prodlist.selection_clear(0, 'end') for prod in self._parser.reducible_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) ######################################### ## Button Callbacks ######################################### def destroy(self, *e): if self._top is None: return self._top.destroy() self._top = None def reset(self, *e): self._parser.initialize(self._sent) self._lastoper1['text'] = 'Reset App' self._lastoper2['text'] = '' self._redraw() def step(self, *e): if self.reduce(): return True elif self.shift(): return True else: if list(self._parser.parses()): self._lastoper1['text'] = 'Finished:' self._lastoper2['text'] = 'Success' else: self._lastoper1['text'] = 'Finished:' self._lastoper2['text'] = 'Failure' def shift(self, *e): if self._animating_lock: return if self._parser.shift(): tok = self._parser.stack()[-1] self._lastoper1['text'] = 'Shift:' self._lastoper2['text'] = '%r' % tok if self._animate.get(): self._animate_shift() else: self._redraw() return True return False def reduce(self, *e): if self._animating_lock: return production = self._parser.reduce() if production: self._lastoper1['text'] = 'Reduce:' self._lastoper2['text'] = '%s' % production if self._animate.get(): self._animate_reduce() else: self._redraw() return production def undo(self, *e): if self._animating_lock: return if self._parser.undo(): self._redraw() def postscript(self, *e): self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) ######################################### ## Menubar callbacks ######################################### def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) #self._stacklabel['font'] = ('helvetica', -size-4, 'bold') #self._rtextlabel['font'] = ('helvetica', -size-4, 'bold') #self._lastoper_label['font'] = ('helvetica', -size) #self._lastoper1['font'] = ('helvetica', -size) #self._lastoper2['font'] = ('helvetica', -size) #self._prodlist['font'] = ('helvetica', -size) #self._prodlist_label['font'] = ('helvetica', -size-2, 'bold') self._redraw() def help(self, *e): # The default font's not very legible; try using 'fixed' instead. try: ShowText(self._top, 'Help: Shift-Reduce Parser Application', (__doc__ or '').strip(), width=75, font='fixed') except: ShowText(self._top, 'Help: Shift-Reduce Parser Application', (__doc__ or '').strip(), width=75) def about(self, *e): ABOUT = ("NLTK Shift-Reduce Parser Application\n" + "Written by Edward Loper") TITLE = 'About: Shift-Reduce Parser Application' try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def edit_grammar(self, *e): CFGEditor(self._top, self._parser.grammar(), self.set_grammar) def set_grammar(self, grammar): self._parser.set_grammar(grammar) self._productions = list(grammar.productions()) self._prodlist.delete(0, 'end') for production in self._productions: self._prodlist.insert('end', (' %s' % production)) def edit_sentence(self, *e): sentence = " ".join(self._sent) title = 'Edit Text' instr = 'Enter a new sentence to parse.' EntryDialog(self._top, sentence, instr, self.set_sentence, title) def set_sentence(self, sent): self._sent = sent.split() #[XX] use tagged? self.reset() ######################################### ## Reduce Production Selection ######################################### def _toggle_grammar(self, *e): if self._show_grammar.get(): self._prodframe.pack(fill='both', side='left', padx=2, after=self._feedbackframe) self._lastoper1['text'] = 'Show Grammar' else: self._prodframe.pack_forget() self._lastoper1['text'] = 'Hide Grammar' self._lastoper2['text'] = '' def _prodlist_select(self, event): selection = self._prodlist.curselection() if len(selection) != 1: return index = int(selection[0]) production = self._parser.reduce(self._productions[index]) if production: self._lastoper1['text'] = 'Reduce:' self._lastoper2['text'] = '%s' % production if self._animate.get(): self._animate_reduce() else: self._redraw() else: # Reset the production selections. self._prodlist.selection_clear(0, 'end') for prod in self._parser.reducible_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) def _popup_reduce(self, widget): # Remove old commands. productions = self._parser.reducible_productions() if len(productions) == 0: return self._reduce_menu.delete(0, 'end') for production in productions: self._reduce_menu.add_command(label=str(production), command=self.reduce) self._reduce_menu.post(self._canvas.winfo_pointerx(), self._canvas.winfo_pointery()) ######################################### ## Animations ######################################### def _animate_shift(self): # What widget are we shifting? widget = self._rtextwidgets[0] # Where are we shifting from & to? right = widget.bbox()[0] if len(self._stackwidgets) == 0: left = 5 else: left = self._stackwidgets[-1].bbox()[2] + 10 # Start animating. dt = self._animate.get() dx = (left - right) * 1.0 / dt self._animate_shift_frame(dt, widget, dx) def _animate_shift_frame(self, frame, widget, dx): if frame > 0: self._animating_lock = 1 widget.move(dx, 0) self._top.after(10, self._animate_shift_frame, frame - 1, widget, dx) else: # but: stacktop?? # Shift the widget to the stack. del self._rtextwidgets[0] self._stackwidgets.append(widget) self._animating_lock = 0 # Display the available productions. self._draw_stack_top(widget) self._highlight_productions() def _animate_reduce(self): # What widgets are we shifting? numwidgets = len(self._parser.stack()[-1]) # number of children widgets = self._stackwidgets[-numwidgets:] # How far are we moving? if isinstance(widgets[0], TreeSegmentWidget): ydist = 15 + widgets[0].label().height() else: ydist = 15 + widgets[0].height() # Start animating. dt = self._animate.get() dy = ydist * 2.0 / dt self._animate_reduce_frame(dt / 2, widgets, dy) def _animate_reduce_frame(self, frame, widgets, dy): if frame > 0: self._animating_lock = 1 for widget in widgets: widget.move(0, dy) self._top.after(10, self._animate_reduce_frame, frame - 1, widgets, dy) else: del self._stackwidgets[-len(widgets):] for widget in widgets: self._cframe.remove_widget(widget) tok = self._parser.stack()[-1] if not isinstance(tok, Tree): raise ValueError() label = TextWidget(self._canvas, str(tok.label()), color='#006060', font=self._boldfont) widget = TreeSegmentWidget(self._canvas, label, widgets, width=2) (x1, y1, x2, y2) = self._stacklabel.bbox() y = y2 - y1 + 10 if not self._stackwidgets: x = 5 else: x = self._stackwidgets[-1].bbox()[2] + 10 self._cframe.add_widget(widget, x, y) self._stackwidgets.append(widget) # Display the available productions. self._draw_stack_top(widget) self._highlight_productions() # # Delete the old widgets.. # del self._stackwidgets[-len(widgets):] # for widget in widgets: # self._cframe.destroy_widget(widget) # # # Make a new one. # tok = self._parser.stack()[-1] # if isinstance(tok, Tree): # attribs = {'tree_color': '#4080a0', 'tree_width': 2, # 'node_font': bold, 'node_color': '#006060', # 'leaf_color': '#006060', 'leaf_font':self._font} # widget = tree_to_treesegment(self._canvas, tok.type(), # **attribs) # widget.node()['color'] = '#000000' # else: # widget = TextWidget(self._canvas, tok.type(), # color='#000000', font=self._font) # widget.bind_click(self._popup_reduce) # (x1, y1, x2, y2) = self._stacklabel.bbox() # y = y2-y1+10 # if not self._stackwidgets: x = 5 # else: x = self._stackwidgets[-1].bbox()[2] + 10 # self._cframe.add_widget(widget, x, y) # self._stackwidgets.append(widget) #self._redraw() self._animating_lock = 0 ######################################### ## Hovering. ######################################### def _highlight_hover(self, event): # What production are we hovering over? index = self._prodlist.nearest(event.y) if self._hover == index: return # Clear any previous hover highlighting. self._clear_hover() # If the production corresponds to an available reduction, # highlight the stack. selection = [int(s) for s in self._prodlist.curselection()] if index in selection: rhslen = len(self._productions[index].rhs()) for stackwidget in self._stackwidgets[-rhslen:]: if isinstance(stackwidget, TreeSegmentWidget): stackwidget.label()['color'] = '#00a000' else: stackwidget['color'] = '#00a000' # Remember what production we're hovering over. self._hover = index def _clear_hover(self, *event): # Clear any previous hover highlighting. if self._hover == -1: return self._hover = -1 for stackwidget in self._stackwidgets: if isinstance(stackwidget, TreeSegmentWidget): stackwidget.label()['color'] = 'black' else: stackwidget['color'] = 'black'
class DataDisplay(Frame): def __init__(self, widget_options): super().__init__() self.widget_options = widget_options self.init_gui() def init_gui(self): self.columnconfigure(0, weight=3) self.columnconfigure(1, weight=2) self.columnconfigure(2, weight=2) self.rowconfigure(1, weight=1) self.names = Listbox(self, **self.widget_options) self.date_added = Listbox(self, **self.widget_options) self.expdate = Listbox(self, **self.widget_options) self.names.bind("<ButtonRelease-1>", self.select_fields) self.date_added.bind("<ButtonRelease-1>", self.select_fields) self.expdate.bind("<ButtonRelease-1>", self.select_fields) self.name_label = Label(self, text="Food Name", **self.widget_options) self.added_label = Label(self, text="Date Added", **self.widget_options) self.exp_label = Label(self, text="Expiration Date", **self.widget_options) self.name_label.grid(row=0, column=0, sticky=N + S + E + W) self.added_label.grid(row=0, column=1, sticky=N + S + E + W) self.exp_label.grid(row=0, column=2, sticky=N + S + E + W) self.names.grid(row=1, column=0, sticky=N + S + E + W) self.date_added.grid(row=1, column=1, sticky=N + S + E + W) self.expdate.grid(row=1, column=2, sticky=N + S + E + W) def delete(self, *args): self.names.delete(*args) self.date_added.delete(*args) self.expdate.delete(*args) def insert(self, where, item): self.names.insert(where, item[0]) self.date_added.insert(where, item[1]) self.expdate.insert(where, item[2]) def get(self, *args): name_select = self.names.curselection() date_added_select = self.date_added.curselection() expdate_select = self.expdate.curselection() item = (self.names.get(*args), self.date_added.get(*args), self.expdate.get(*args)) return "{} {} {}".format(*item) def select_fields(self, event=None): instance = event.widget sel_ind = instance.curselection()[0] if instance.curselection() else 0 self.names.selection_clear(0, END) self.date_added.selection_clear(0, END) self.expdate.selection_clear(0, END) self.date_added.selection_set(sel_ind) self.expdate.selection_set(sel_ind) self.names.selection_set(sel_ind)
class FilePane: def __init__(self, root, items=list(), on_selection=list(), on_close=list()): self.items = items self.item_count = len(items) self.on_selection = on_selection self.on_close = on_close self.window = Toplevel() self.window.title('Files') self.window.transient(root) self.window.protocol("WM_DELETE_WINDOW", self.on_window_close) self.menubar = Menu(self.window) self.menubar.add_command(label='Previous', command=self.previous) self.menubar.add_command(label='Next', command=self.next) # Display the menu self.window.config(menu=self.menubar) self.scrollbar = Scrollbar(self.window, orient='vertical') self.listbox = Listbox(self.window, yscrollcommand=self.scrollbar.set, exportselection=False) self.set_items(items) self.scrollbar.config(command=self.listbox.yview) self.scrollbar.pack(side='right', fill='y') self.listbox.pack(side='left', fill='both', expand=1) self.listbox.bind('<<ListboxSelect>>', self.on_select) def on_window_close(self): for callback in self.on_close: callback() self.window.withdraw() def hide(self): self.window.withdraw() def show(self): self.window.deiconify() def selected_item(self): return self.items[self.listbox.curselection()[0]] def select_index(self, index): self.listbox.selection_clear(0, END) # From https://stackoverflow.com/a/25451279/11628429 self.listbox.select_set( index) #This only sets focus on the first item. self.listbox.event_generate("<<ListboxSelect>>") def set_items(self, items): self.items = items self.item_count = len(items) self.listbox.delete(0, END) for item in items: item = path.split(item)[1] self.listbox.insert(END, item) self.select_index(0) def previous(self): index = self.listbox.curselection()[0] - 1 if index < 0: index = self.item_count - 1 self.select_index(index) def next(self): index = self.listbox.curselection()[0] + 1 if index >= self.item_count: index = 0 self.select_index(index) def on_select(self, event): if self.on_selection: for callback in self.on_selection: callback(self.selected_item())
class ContactsPage(Frame): """ Contacts Page: Contains the list of currently added contacts === Public Attributes === master: The frame containing all information on this page controller: Reference to the Controller Class page_name: String containing a name for this page; used for setting the tabs in the containing notebook contacts_list: Dictionary of contacts, each contact is a Contact class instance, each key is the name of the contact current_contact: Contains the contact that was selected the last time we clicked on show info. scroll_bar: Scroll bar that controls what is viewable in the contacts list; won't scroll if nothing is in the list, or everything is already shown. contacts_field: Area where contacts are shown; 10 at a time letters_field: Listbox that shows each letter of the alphabet to help the user find contact they're looking for show_info: Button that updates the info_field with the information of the currently selected contact wheel_spin: WheelSpinner object for the Show Contact Button. delete: Button that deletes the selected contact info_field: Listbox that contains the information of the currently selected contact info_scroll: Scrollbar that controls what is viewable in the info_field; won't scroll if nothing is in the list, or everything is already shown self.load: Button to load contacts self.save: Button to save contacts === Methods === create: Initializes objects & places them on the page insert_contact: Adds a contact's name to the end of the contacts field show_contact_info: Shows the information of the selected contact in the info listbox delete_contact: Deletes the selected contact & reloads the contacts Listbox clear_fields: Clears both fields on the contacts page load_contacts: Loads contacts in from a file save_contacts: Saves contacts as a file yview: Adjusts the view of contacts_field & letters_field at the same time on_mouse_wheel: Adjusts the view of contacts_field and letters_field at the same time, for the mouse wheel """ def __init__(self, master, controller, **kw): super().__init__(master, **kw) self.master = master self.controller = controller self.page_name = "View Contacts" # Initialize object names self.contacts_list = {} self.current_contact = None self.alphabetical_order = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] self.scroll_bar = None self.contacts_field = None self.letters_field = None self.show_info = None self.wheel_spin = None self.delete = None self.info_field = None self.info_scroll = None self.bind("<<Finish Spinning Wheel>>", self.show_winning_info) self.bind("<Visibility>", self.__on_visibility) self.load = None self.save = None self.create() def create(self) -> None: self.info_scroll = Scrollbar(self, orient=VERTICAL) self.info_field = Listbox( self, yscrollcommand=self.info_scroll.set ) self.delete = Button(self, text="Delete", command=lambda: self.delete_contact()) self.delete.grid(row=2, column=3, columnspan=3, sticky=N + S + E + W) self.show_info = Button(self, text="Show Info", command=lambda: self.show_contact_info()) self.show_info.grid(row=2, column=0, columnspan=3, sticky=N + S + E + W) wheel_spin_options = ['Name', 'Home Phone Numbers', 'Work Phone Numbers', 'Personal Phone Numbers', 'Emails', 'Home Addresses', 'Notes'] self.wheel_spin = WheelSpinner(self, wheel_spin_options, width=50, height=200, radius=60) self.wheel_spin.grid(row=3, column=0, columnspan=5) self.scroll_bar = Scrollbar(self) self.contacts_field = Listbox( self, yscrollcommand=self.scroll_bar.set, selectmode=SINGLE, exportselection=0 ) self.letters_field = Listbox( self, width=2, selectmode=NONE, exportselection=0 ) self.letters_field.bind('<<ListboxSelect>>', self.scroll_to_letter) self.contacts_field.grid(row=1, column=0, columnspan=5, sticky=N + S + E + W) self.letters_field.grid(row=1, column=4, sticky=N + S + E + W) self.scroll_bar.grid(row=1, column=5, sticky=N + S + E + W) self.scroll_bar.config(command=self.yview) self.contacts_field.bind("<MouseWheel>", self.on_mouse_wheel) self.letters_field.bind("<MouseWheel>", self.on_letter_mouse_wheel) self.save = Button( self, text="Save Contacts", command=lambda: self.save_contacts() ) self.save.grid(row=0, column=3, columnspan=4, sticky=N + S + E + W) self.load = Button( self, text="Load Contacts", command=lambda: self.load_contacts() ) self.load.grid(row=0, column=0, columnspan=3, sticky=N + S + E + W) for i in range(3): self.grid_rowconfigure(i, weight=1) for i in range(4): self.grid_columnconfigure(i, weight=1) def scroll_to_letter(self, event): id = 0 for contact in self.order_contact(): if contact[0] == self.letters_field.get(self.letters_field.curselection()[0]): self.contacts_field.see(id) self.contacts_field.selection_clear(0, END) self.contacts_field.selection_set(id) self.letters_field.selection_clear(0, END) return id += 1 self.letters_field.selection_clear(0, END) def on_mouse_wheel(self, event) -> str: self.contacts_field.yview("scroll", int(-event.delta / 80), "units") return "break" def on_letter_mouse_wheel(self, event) -> str: self.letters_field.yview("scroll", int(-event.delta / 80), "units") return "break" def yview(self, *args) -> None: self.contacts_field.yview(*args) self.letters_field.yview(*args) def delete_contact(self) -> None: name = self.contacts_field.get(self.contacts_field.curselection()[0]) del self.contacts_list[name] self.clear_fields() for contact in sorted(self.contacts_list): self.insert_contact(contact) def clear_fields(self) -> None: for field in [self.contacts_field, self.info_field, self.letters_field]: field.delete(0, END) def refresh_fields(self) -> None: self.clear_fields() for contact in self.order_contact(): self.contacts_field.insert(END, contact) for letter in self.alphabetical_order: self.letters_field.insert(END, letter.upper()) def load_contacts(self) -> None: self.randomize_alphabetical_order() with open("project/contacts_pickle", 'rb') as infile: self.contacts_list = pickle.load(infile) self.refresh_fields() def save_contacts(self) -> None: with open("project/contacts_pickle", 'wb') as outfile: pickle.dump(self.contacts_list, outfile) def insert_contact(self, contact) -> None: self.contacts_field.insert(END, contact) def show_contact_info(self) -> None: """ This method shows the spinning wheel if a contact is selected and if the wheel isn't already rotating It is called on the Show Contact Info button. :return: None """ if len(self.contacts_field.curselection()) == 0 or self.wheel_spin.is_rotating: return name = self.contacts_field.get(self.contacts_field.curselection()[0]) self.current_contact = self.contacts_list[name] self.wheel_spin.draw() def show_winning_info(self, event) -> None: """ This method is called when the event <<Finish Spinning Wheel>> is invoked. It displays the current contact information that was selected by the spinning wheel. :return: None """ self.randomize_alphabetical_order() self.refresh_fields() winner = self.wheel_spin.winner label = self.wheel_spin.display_label text0 = self.current_contact.name + "'s " + winner.lower() + ':\n' text1 = '' if winner == 'Name': text1 = self.current_contact.name elif winner == 'Home Phone Numbers': for elem in self.current_contact.phone_numbers["Home"]: text1 = text1 + elem + ", " elif winner == 'Work Phone Numbers': for elem in self.current_contact.phone_numbers["Work"]: text1 = text1 + elem + ', ' elif winner == 'Personal Phone Numbers': for elem in self.current_contact.phone_numbers["Personal"]: text1 = text1 + elem + ', ' elif winner == 'Emails': for elem in self.current_contact.email_addresses: text1 = text1 + elem + ', ' elif winner == 'Home Addresses': for elem in self.current_contact.addresses: text1 = text1 + elem + ', ' elif winner == 'Notes': for elem in self.current_contact.notes: text1 = text1 + elem + ', ' label['text'] = text0 + text1 def randomize_alphabetical_order(self): random.shuffle(self.alphabetical_order) def order_contact(self) -> list: """ This function takes all the contacts and order them in the order stored in self.alphabetical order :return: The ordered list """ i = 0 order = self.alphabetical_order contacts = list(self.contacts_list) ordered_list = [] # We loop until we have all the contact ordered. while i < len(self.contacts_list): current_next_contact = None for contact in contacts: if current_next_contact is None: current_next_contact = contact continue # If the first letter is higher in the order than the current next contact, we # change the contact. if order.index(contact[0].lower()) < order.index(current_next_contact[0].lower()): current_next_contact = contact continue # If the first character is the same, we loop through the other character to find # which on should be # added first. if order.index(contact[0].lower()) == order.index(current_next_contact[0].lower()): for current_character in range(1, min(len(contact), len(current_next_contact))): if order.index(contact[current_character].lower()) < \ order.index(current_next_contact[current_character].lower()): current_next_contact = contact break if order.index(contact[current_character].lower()) > \ order.index(current_next_contact[current_character].lower()): break # we append the contact to the list and remove it from the remaining contact to order. contacts.remove(current_next_contact) ordered_list.append(current_next_contact) i += 1 return ordered_list def __on_visibility(self, event) -> None: """ This function is called when the user click on the contact tab. It randomizes the alphabetical order :param event: :return: None """ self.randomize_alphabetical_order() self.refresh_fields()