class GroupNameLabel(Label): """Label with group name""" def __init__(self, parent, group, **kw): self._group = group kw['text'] = str(group) # kw.setdefault('anchor', 'w') kw.setdefault('font', tkFont.Font(size=20)) Label.__init__(self, parent, **kw) self._init_menu() def _init_menu(self): self._menu = Menu(self, tearoff=0) self._menu.add_command(label="Copy LaTeX", command=self._copy_latex) # right button on Mac and other systems button = '2' if tools.IS_MAC else '3' self.bind("<Button-{}>".format(button), self._right_click) def _right_click(self, event): self._menu.post(event.x_root, event.y_root) def _copy_latex(self): pyperclip.setcb(self._group.str_latex()) @property def group(self): return self._group @group.setter def group(self, group): self._group = group self['text'] = str(group)
class Example(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.initUI() def initUI(self): self.parent.title("Popup menu") self.menu = Menu(self.parent, tearoff=0) self.menu.add_command(label="Beep", command=self.bell()) self.menu.add_command(label="Exit", command=self.onExit) self.parent.bind("<Button-3>", self.showMenu) self.pack() def showMenu(self, e): self.menu.post(e.x_root, e.y_root) def onExit(self): self.quit()
class Example(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.initUI() def initUI(self): self.parent.title("Popup menu") self.menu = Menu(self.parent, tearoff=0) self.menu.add_command(label="Beep", command=self.bell()) self.menu.add_command(label="Exit", command=self.onExit) self.parent.bind("<Button-2>", self.showMenu) self.pack() def showMenu(self, e): self.menu.post(e.x_root, e.y_root) def onExit(self): self.quit()
class Example(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.initUI() def initUI(self): self.parent.title("Popup menu") self.menu = Menu(self.parent, tearoff=0) #A context menu is a regular Menu widget. #The tearoff feature is turned off. #Now it is not possible to separate the menu into a new toplevel window. self.menu.add_command(label="Beep", command=self.bell()) self.menu.add_command(label="Exit", command=self.onExit) self.parent.bind("<Button-3>", self.showMenu) #We bind the <Button-3> event to the showMenu() method. #The event is generated when we right click on the client area of the window. self.pack() #The showMenu() method shows the context menu. #The popup menu is shown at the x and y coordinates of the mouse click. def showMenu(self, e): self.menu.post(e.x_root, e.y_root) def onExit(self): self.quit()
def _handle_r_click(self, event): """ Handle mouse right click as a callback function Handle mouse right click by displaying a context menu for this widget, allow the addition and removal of window pics :param event: The event generated by the window manager, automatically provided """ Debug.printe(event, Debug.Level.INFO) # Create the menu to display p_menu = Menu(self._parent) p_menu.add_command(label="Add Picture", command=lambda: self._add_new_wall_pic()) p_menu.add_command(label="Delete Picture", command=lambda: self._remove_wall_pic()) p_menu.add_command(label="Delete All", command=lambda: self.remove_all()) p_menu.post(event.x_root, event.y_root)
class Window(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.startUI() def startUI(self): self.menu = Menu(self.parent, tearoff=0) self.menu.add_command(label="Beep", command=self.bell) self.menu.add_command(label="exit", command=self.quit) self.parent.bind("<Button-3>", self.showMenu) self.pack() def showMenu(self, e): self.menu.post(e.x_root, e.y_root) def bell(self): print "beep"
class TreeNode: def __init__(self, canvas, parent, item, menuList=[]): self.canvas = canvas self.parent = parent self.item = item self.state = 'collapsed' self.selected = 0 self.children = {} self.kidKeys = [] self.x = self.y = None self.iconimages = {} # cache of PhotoImage instances for icons self.menuList = menuList self.menuVar = IntVar() self.menuVar.set(0) self._popupMenu = None self.image_id = None if self.menuList: if self.menuList[-1] == 'Separator': self.menuList = self.menuList[:-1] self._popupMenu = Menu(self.canvas, tearoff=0) for i in range(len(self.menuList)): item = self.menuList[i] if item == 'Separator': self._popupMenu.add_separator() else: self._popupMenu.add_radiobutton( label=item, variable=self.menuVar, value=i, indicatoron=0, command=self.popupMenuCommand) def destroy(self): for key in self.kidKeys: c = self.children[key] del self.children[key] c.destroy() self.parent = None def geticonimage(self, name): try: return self.iconimages[name] except KeyError: pass file, ext = os.path.splitext(name) ext = ext or ".gif" fullname = os.path.join(ICONDIR, file + ext) image = PhotoImage(master=self.canvas, file=fullname) self.iconimages[name] = image return image def select(self, event=None): if self.selected: return self.deselectall() self.selected = 1 if self.parent != None: if self.parent.state == 'expanded': self.canvas.delete(self.image_id) self.drawicon() self.drawtext() self.item.OnSelect(event) def deselect(self, event=None): if not self.selected: return self.selected = 0 if self.parent != None: if self.parent.state == 'expanded': self.canvas.delete(self.image_id) self.drawicon() self.drawtext() def deselectall(self): if self.parent: self.parent.deselectall() else: self.deselecttree() def deselecttree(self): if self.selected: self.deselect() for key in self.kidKeys: child = self.children[key] child.deselecttree() def flip(self, event=None): if self.state == 'expanded': self.collapse() else: self.expand() self.item.OnDoubleClick() return "break" def popupMenu(self, event=None): if self._popupMenu: self._popupMenu.post(event.widget.winfo_pointerx(), event.widget.winfo_pointery()) return "break" def popupMenuCommand(self): command = self.menuList[self.menuVar.get()] self.item.MenuCommand(command) if self.parent and (command != 'Update Explorer'): # Update parent to try to keep explorer up to date self.parent.update() def expand(self, event=None): if not self.item.IsExpandable(): return if self.state != 'expanded': self.state = 'expanded' self.update() self.view() def collapse(self, event=None): if self.state != 'collapsed': self.state = 'collapsed' self.update() def view(self): top = self.y - 2 bottom = self.lastvisiblechild().y + 17 height = bottom - top visible_top = self.canvas.canvasy(0) visible_height = self.canvas.winfo_height() visible_bottom = self.canvas.canvasy(visible_height) if visible_top <= top and bottom <= visible_bottom: return x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion']) if top >= visible_top and height <= visible_height: fraction = top + height - visible_height else: fraction = top fraction = float(fraction) / y1 self.canvas.yview_moveto(fraction) def reveal(self): # Make sure all parent nodes are marked as expanded parent = self.parent while parent: if parent.state == 'collapsed': parent.state = 'expanded' parent = parent.parent else: break # Redraw tree accordingly self.update() # Bring this item into view self.view() def lastvisiblechild(self): if self.kidKeys and self.state == 'expanded': return self.children[self.kidKeys[-1]].lastvisiblechild() else: return self def update(self): if self.parent: self.parent.update() else: oldcursor = self.canvas['cursor'] self.canvas['cursor'] = "watch" self.canvas.update() self.canvas.delete(Tkinter.ALL) # XXX could be more subtle self.draw(7, 2) x0, y0, x1, y1 = self.canvas.bbox(Tkinter.ALL) self.canvas.configure(scrollregion=(0, 0, x1, y1)) self.canvas['cursor'] = oldcursor def draw(self, x, y): # XXX This hard-codes too many geometry constants! self.x, self.y = x, y self.drawicon() self.drawtext() if self.state != 'expanded': return y + 17 # draw children sublist = self.item._GetSubList() if not sublist: # IsExpandable() was mistaken; that's allowed return y + 17 self.kidKeys = [] for item in sublist: key = item.GetKey() if self.children.has_key(key): child = self.children[key] else: child = TreeNode(self.canvas, self, item, self.menuList) self.children[key] = child self.kidKeys.append(key) # Remove unused children for key in self.children.keys(): if key not in self.kidKeys: del (self.children[key]) cx = x + 20 cy = y + 17 cylast = 0 for key in self.kidKeys: child = self.children[key] cylast = cy self.canvas.create_line(x + 9, cy + 7, cx, cy + 7, fill="gray50") cy = child.draw(cx, cy) if child.item.IsExpandable(): if child.state == 'expanded': iconname = "minusnode" callback = child.collapse else: iconname = "plusnode" callback = child.expand image = self.geticonimage(iconname) id = self.canvas.create_image(x + 9, cylast + 7, image=image) # XXX This leaks bindings until canvas is deleted: self.canvas.tag_bind(id, "<1>", callback) self.canvas.tag_bind(id, "<Double-1>", lambda x: None) id = self.canvas.create_line( x + 9, y + 10, x + 9, cylast + 7, ##stipple="gray50", # XXX Seems broken in Tk 8.0.x fill="gray50") self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2 return cy def drawicon(self): if self.selected: imagename = (self.item.GetSelectedIconName() or self.item.GetIconName() or "openfolder") else: imagename = self.item.GetIconName() or "folder" image = self.geticonimage(imagename) id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image) self.image_id = id self.canvas.tag_bind(id, "<1>", self.select) self.canvas.tag_bind(id, "<Double-1>", self.flip) self.canvas.tag_bind(id, "<3>", self.popupMenu) def drawtext(self, text=None): textx = self.x + 20 - 1 texty = self.y - 1 labeltext = self.item.GetLabelText() if labeltext: id = self.canvas.create_text(textx, texty, anchor="nw", text=labeltext) self.canvas.tag_bind(id, "<1>", self.select) self.canvas.tag_bind(id, "<Double-1>", self.flip) x0, y0, x1, y1 = self.canvas.bbox(id) textx = max(x1, 200) + 10 if text == None: text = self.item.GetText() or "<no text>" try: self.entry except AttributeError: pass else: self.edit_finish() try: label = self.label except AttributeError: # padding carefully selected (on Windows) to match Entry widget: self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2) if self.selected: self.label.configure(fg="white", bg="darkblue") else: fg = self.item.GetTextFg() self.label.configure(fg=fg, bg="white") id = self.canvas.create_window(textx, texty, anchor="nw", window=self.label) self.label.bind("<1>", self.select_or_edit) self.label.bind("<Double-1>", self.flip) self.label.bind("<3>", self.popupMenu) # Update text if necessary if text != self.label['text']: self.label['text'] = text self.text_id = id def select_or_edit(self, event=None): if self.selected and self.item.IsEditable(): text = self.item.GetTextForEdit() self.label['text'] = text self.drawtext(text) self.edit(event) else: self.select(event) def edit(self, event=None): self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0) self.entry.insert(0, self.label['text']) self.entry.selection_range(0, Tkinter.END) self.entry.pack(ipadx=5) self.entry.focus_set() self.entry.bind("<Return>", self.edit_finish) self.entry.bind("<Escape>", self.edit_cancel) def edit_finish(self, event=None): try: entry = self.entry del self.entry except AttributeError: return text = entry.get() entry.destroy() if text and text != self.item.GetText(): self.item.SetText(text) text = self.item.GetText() self.label['text'] = text self.drawtext() self.canvas.focus_set() def edit_cancel(self, event=None): self.drawtext() self.canvas.focus_set() def find(self, searchKey): # Search for a node who's key matches the given key # Is it this node if searchKey == self.item.GetKey(): return self # Nope, check the children sublist = self.item._GetSubList() for item in sublist: key = item.GetKey() # Use existing child or create new TreeNode if none exists if self.children.has_key(key): child = self.children[key] else: child = TreeNode(self.canvas, self, item, self.menuList) # Update local list of children and keys self.children[key] = child self.kidKeys.append(key) # See if node is child (or one of child's descendants) retVal = child.find(searchKey) if retVal: return retVal # Not here return None
class PmaConvert: def __init__(self, config): root = Tk() # Root Definition root.geometry('1100x700') root.title('PMA Convert') # Configuration and Variables self.file_selection = '' self.is_converting = False self.options = config['option_definitions'] gui_config = config['gui_config'] # UI ## Frames self.main_frame = Frame(root) self.position_main_frame(gui_config['screen_orientation']) ## Components self.log = Text(self.main_frame, bd=1, relief=SUNKEN, width=140, height=23, state=DISABLED, spacing3=1, wrap=WORD) choose_text = ('1. Choose XLSForm (.xls or .xlsx) file(s) for ' 'conversion.') self.choose_files_label = Label(self.main_frame, text=choose_text) # TODO: Get spacing to work. # self.choose_files_label.grid(row=3, column=3, padx=(50, 50)) # self.choose_files_label.grid(row=3, column=3, pady=(50, 50)) self.choose_files_label.pack() self.choose_files_button = Button(self.main_frame, text='Choose file...', fg='black', command=self.on_open) self.choose_files_button.pack() out_text = 'Choose location for output file(s).' self.output_location_label = Label(self.main_frame, text=out_text) self.output_location_button = Button(self.main_frame, text='Choose location...', fg='black') if gui_config['output_location_on'] is True: self.output_location_label.pack() self.output_location_button.pack() self.choose_options_label = Label(self.main_frame, text='2. Choose conversion options.') self.choose_options_label.pack() ### Create Options Checkboxes # Task: Dynamically generate: http://stackoverflow.com/questions/... # ...553784/can-you-use-a-string-to-instantiate-a-class-in-python self.preexisting = BooleanVar() pre_text = self.options['preexisting']['label'] self.preexisting_opt = Checkbutton(self.main_frame, text=pre_text, variable=self.preexisting) self.preexisting_opt.pack() self.regular = BooleanVar() reg_text = self.options['regular']['label'] self.regular_opt = Checkbutton(self.main_frame, text=reg_text, variable=self.regular) self.regular_opt.pack() self.novalidate = BooleanVar() noval_text = self.options['novalidate']['label'] self.novalidate_opt = Checkbutton(self.main_frame, text=noval_text, variable=self.novalidate) self.novalidate_opt.pack() self.ignore_version = BooleanVar() ig_text = self.options['ignore_version']['label'] self.ignore_version_opt = Checkbutton(self.main_frame, text=ig_text, variable=self.ignore_version) self.ignore_version_opt.pack() self.linking_warn = BooleanVar() link_text = self.options['linking_warn']['label'] self.linking_warn_option = Checkbutton(self.main_frame, text=link_text, variable=self.linking_warn) self.linking_warn_option.pack() self.debug = BooleanVar() debug_text = self.options['debug']['label'] self.debug_option = Checkbutton(self.main_frame, text=debug_text, variable=self.debug) self.debug_option.pack() self.extras = BooleanVar() extras_text = self.options['extras']['label'] self.extras_option = Checkbutton(self.main_frame, text=extras_text, variable=self.extras) self.extras_option.pack() self.convert_label = Label(self.main_frame, text='3. Run conversion.') self.convert_label.pack() # Task: Add xscrollcommand and yscrollcommand. self.convert_button = Button(self.main_frame, text='Convert', fg='black', command=self.convert) self.convert_button.pack() self.log.pack(fill=X, expand=1) self.log_text('PMA Convert allows you to convert .xls or .xlsx form ' 'definition files to files which are compatible with ODK ' 'Collect.\n\nIf you need to copy and paste from this ' 'log, highlight the text and press CTRL+C to copy. Then ' 'press CTRL+V to paste.\n\n' '====================================================\n\n' 'Awaiting file selection.') # Task: Fix menus. They're not working. self.context_menu = Menu(self.main_frame, tearoff=0) self.context_menu.add_command(label="Convert", command=self.convert) self.main_frame.bind("<Button-3>", self.popup) # - Note: Strangely this stopped anchoring to bottom suddenly, for some # reason. So it is temporarily disabled. self.status_bar = Label(self.main_frame, text='Awaiting file selection.', bd=1, relief=SUNKEN, anchor=W) if gui_config['status_bar_on'] is True: self.status_bar.pack(side=BOTTOM, fill=X) # Run root.mainloop() # Functions def popup(self, event): # Note: Currently doesn't work. self.context_menu.post(event.x_root, event.y_root) # display the popup menu try: self.context_menu.tk_popup(event.x_root, event.y_root, 0) finally: # make sure to release the grab (Tk 8.0a1 only) self.context_menu.grab_release() def position_main_frame(self, orientation): if orientation == 'center': x, y, a = .5, .5, 'c' return self.main_frame.place(relx=x, rely=y, anchor=a) elif orientation == 'top': return self.main_frame.pack() else: return self.main_frame.pack() def on_open(self): file_types = [ ('XLS Files', '*.xls'), ('XLSX Files', '*.xlsx'), ('All files', '*') ] try: self.file_selection = tkFileDialog.askopenfilename( filetypes=file_types, title='Open one or more files.', message='Open one or more files', multiple=1 ) except: self.file_selection = tkFileDialog.askopenfilename( filetypes=file_types, title='Open one or more files.', multiple=1 ) if self.file_selection != '': self.set_status('Click on Convert to convert files.') log_output = 'Ready for conversion: \n' for file in self.file_selection: log_output += '* ' + str(file) + '\n' log_output = log_output[:-1] # Removes the last '\n'. self.log.configure(self.log_text(log_output)) def set_status(self, new_status): self.status_bar.configure(text=new_status) def log_text(self, new_text): self.log.configure(state=NORMAL) self.log.insert(END, str(new_text) + '\n\n') self.log.configure(state=DISABLED) self.log.bind("<1>", lambda event: self.log.focus_set()) def convert(self): if self.file_selection != '': f = self.file_selection kwargs = { SUFFIX: u'', PREEXISTING: self.preexisting.get(), PMA: not self.regular.get(), CHECK_VERSIONING: not self.ignore_version.get(), STRICT_LINKING: not self.linking_warn.get(), VALIDATE: not self.novalidate.get(), EXTRAS: self.extras.get(), DEBUG: self.debug.get() } buffer = StringIO.StringIO() if not kwargs[DEBUG]: sys.stdout = buffer sys.stderr = buffer else: self.log_text('--> DEBUG MODE: check console output') try: xlsform_convert(f, **kwargs) except ConvertError as e: print unicode(e) except OSError as e: # Should catch WindowsError, impossible to test on Mac traceback.print_exc() print e if not kwargs[DEBUG]: sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ self.log_text(buffer.getvalue())
class Text(mx.AllMixins, tk.Text): def __init__(self, parent, scrollbar=True, **kw): parent = mx.get_master(parent) self.parent = parent frame = Frame(parent) frame.pack(fill='both', expand=True) # text widget if "wrap" not in kw: kw["wrap"] = "word" tk.Text.__init__(self, frame, **kw) #self.pack(side='left', fill='both', expand=True) mx.AllMixins.__init__(self, parent) # scrollbar if scrollbar: scrb = Scrollbar(frame, orient='vertical', command=self.yview) self.config(yscrollcommand=scrb.set) scrb.pack(side='right', fill='y') # pop-up menu self.popup = Menu(self, tearoff=0) self.popup.add_command(label='Cut', command=self._cut) self.popup.add_command(label='Copy', command=self._copy) self.popup.add_command(label='Paste', command=self._paste) self.popup.add_separator() self.popup.add_command(label='Select All', command=self._select_all) self.popup.add_command(label='Clear All', command=self._clear_all) self.bind('<Button-3>', self._show_popup) # only allow mouse scroll when mouse inside text self.bind("<Leave>", lambda event: self.winfo_toplevel().focus_set(), "+") self.bind("<Enter>", lambda event: self.focus_set(), "+") def apply_theme(self, theme='standard'): '''theme=['standard', 'typewriter', 'terminal']''' if theme == 'typewriter': '''takes all inserted text and inserts it one char every 100ms''' options = {"font": ('Times', 10, 'bold')} self.config(options) text = self.get('1.0', 'end') self.delete('1.0', 'end') self.char_index = 0 self._typewriter([char for char in text]) elif theme == 'terminal': '''blocky insert cursor''' options = {'bg': 'black', 'fg': 'white', 'font': ('Courier', 10)} self.config(options) self.cursor = '1.0' self.fg = self.cget('fg') self.bg = self.cget('bg') self.switch = self.fg self.config(insertwidth=0) self._blink_cursor() self._place_cursor() elif theme == 'matrix': '''blocky insert cursor''' options = {'bg': 'black', 'fg': 'green', 'font': ('Courier', 10)} self.config(options) self.cursor = '1.0' self.fg = self.cget('fg') self.bg = self.cget('bg') self.switch = self.fg self.config(insertwidth=0) self._blink_cursor() self._place_cursor() def highlight_pattern(self, pattern, tag, start="1.0", end="end", regexp=False, nocase=True, exact=True, **kwargs): '''Apply the given tag to all text that matches the given pattern If 'regexp' is set to True, pattern will be treated as a regular expression. From: http://stackoverflow.com/questions/3781670/how-to-highlight-text-in-a-tkinter-text-widget ''' if not pattern: return 0 start = self.index(start) end = self.index(end) self.mark_set("matchStart", start) self.mark_set("matchEnd", start) self.mark_set("searchLimit", end) count = tk.IntVar() while True: index = self.search(pattern, "matchEnd", "searchLimit", count=count, regexp=regexp, nocase=nocase, exact=exact, **kwargs) if index: self.mark_set("matchStart", index) self.mark_set("matchEnd", "%s+%sc" % (index, count.get())) self.tag_add(tag, "matchStart", "matchEnd") else: break return count.get() def clear_highlights(self, *tagnames): names = tagnames or self.tag_names() self.tag_delete(*names) def next_pattern(self, pattern, current, regexp=False, nocase=True, exact=True, **kwargs): if not pattern: return None start = self.index(current) end = self.index("end") count = tk.IntVar() index = self.search(pattern, start, end, count=count, regexp=regexp, nocase=nocase, exact=exact, **kwargs) if index: return index, index+"+%sc"%count.get() else: return None def prev_pattern(self, pattern, current, regexp=False, nocase=True, exact=True, **kwargs): if not pattern: return None start = self.index(current) end = self.index("1.0") count = tk.IntVar() index = self.search(pattern, start, end, count=count, regexp=regexp, nocase=nocase, exact=exact, backwards=True, **kwargs) if index: return index, index+"-%sc"%count.get() else: return None # MISC INTERNALS def _show_popup(self, event): '''right-click popup menu''' if self.parent.focus_get() != self: self.focus_set() try: self.popup.post(event.x_root, event.y_root) finally: self.popup.grab_release() def _cut(self): try: selection = self.get(*self.tag_ranges('sel')) self.clipboard_clear() self.clipboard_append(selection) self.delete(*self.tag_ranges('sel')) except TypeError: pass def _copy(self): try: selection = self.get(*self.tag_ranges('sel')) self.clipboard_clear() self.clipboard_append(selection) except TypeError: pass def _paste(self): self.insert('insert', self.selection_get(selection='CLIPBOARD')) def _select_all(self): '''selects all text''' self.tag_add('sel', '1.0', 'end-1c') def _clear_all(self): '''erases all text''' isok = askokcancel('Clear All', 'Erase all text?', parent=self, default='ok') if isok: self.delete('1.0', 'end') def _typewriter(self, text): # theme: typewriter '''after the theme is applied, this method takes all the inserted text and types it out one character every 100ms''' self.insert('insert', text[self.char_index]) self.char_index += 1 if hasattr(self, "typer") and self.char_index == len(text): self.after_cancel(self.typer) else: self.typer = self.after(100, self._typewriter, text) def _place_cursor(self): # theme: terminal '''check the position of the cursor against the last known position every 15ms and update the cursorblock tag as needed''' current_index = self.index('insert') if self.cursor != current_index: self.cursor = current_index self.tag_delete('cursorblock') start = self.index('insert') end = self.index('insert+1c') if start[0] != end[0]: self.insert(start, ' ') end = self.index('insert') self.tag_add('cursorblock', start, end) self.mark_set('insert', self.cursor) self.after(15, self._place_cursor) def _blink_cursor(self): # theme: terminal '''alternate the background color of the cursorblock tagged text every 600 milliseconds''' if self.switch == self.fg: self.switch = self.bg else: self.switch = self.fg self.tag_config('cursorblock', background=self.switch) self.after(600, self._blink_cursor)
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 = tkFont.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 = tkFont.Font(family='helvetica', weight='bold', size=self._size.get()) self._font = tkFont.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.node()['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 1 elif self.shift(): return 1 else: if len(self._parser.parses()) > 0: 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 1 return 0 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 tkMessageBox 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 = string.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].node().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.node), 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.node()['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.node()['color'] = 'black' else: stackwidget['color'] = 'black'
class PopupMenuCreator: def __init__(self, atom3i): self.master = atom3i.parent self.atom3i = atom3i self.cb = atom3i.cb self.optionsDatabase = atom3i.optionsDatabase self.popupLogoPhotoimage = Embedded_Images().getPopupLogo() self.popupMenu = None self.event = None # --------------------------- Popup Utilities ------------------------------- def initilizePopupMenu(self, event): """ Create a new popup menu """ if (self.popupMenu): self.popupMenu.unpost() self.popupMenu = Menu(self.master, tearoff=0, bg="white") self.event = event def showPopupMenu(self): """ Display the popup menu """ if (self.popupMenu): self.popupMenu.post(self.event.x_root, self.event.y_root) def swapMenu(self, menu): """ This is a fix for a problem that no longer exists :p It essentially takes one menu and slaps another one in its place. """ raise Exception, "No one uses this method! But if you see this, maybe not so..." self.popupMenu.unpost() self.popupMenu = menu self.showPopupMenu() def popupRemover(self): """ Goodbye popup! """ if (self.popupMenu): self.popupMenu.unpost() self.popupMenu = None # ---------------------- Context Sensitive Menus -------------------------- def NoCursorNoSelectPopup(self, event): """ Popup menu to show when no items under the mouse, and no items selected """ self.initilizePopupMenu(event) addLogo(self) #......................... addSeperator(self) #......................... addModelAction(self) addSelectAll(self) addPaste(self) addUndo(self) addRedo(self) #......................... addSeperator(self) #......................... addFileMenu(self) addModelMenu(self) addTransformationMenu(self) addLayoutMenu(self) addExportMenu(self) #......................... addSeperator(self) #......................... addOpenLastModel(self) addOpenLastMetaModel(self) addSourcePath(self) #......................... addSeperator(self) #......................... addToggleSmoothMode(self) #......................... addSeperator(self) #......................... addExit(self) self.showPopupMenu() def NoCursorMultiSelectPopup(self, event): """ Popup menu to show when no items under the mouse, and multiple items selected """ self.initilizePopupMenu(event) addLogo(self) #......................... addSeperator(self) #......................... addLayoutMenu(self) addResizeEntity(self) addNodeLabelDragToggle(self) #......................... addSeperator(self) #......................... addSelectAll(self) addDeselectAll(self) #......................... addSeperator(self) #......................... addCut(self) addCopy(self) addPaste(self) #......................... addSeperator(self) #......................... addUndo(self) addRedo(self) #......................... addSeperator(self) #......................... addClear(self) self.showPopupMenu() def EntityAtCursorMultiSelectPopup(self, event): """ A graphical entity is under the mouse cursor, along with multiple selected items """ self.initilizePopupMenu(event) addLogo(self) #......................... addSeperator(self) #......................... addLayoutMenu(self) addEditEntity(self) addDragOverlap(self) addDrawArrow(self) addResizeEntity(self) addNodeLabelDragToggle(self) #......................... addSeperator(self) #......................... addSelectAll(self) addDeselectAll(self) #......................... addSeperator(self) #......................... addCut(self) addCopy(self) addPaste(self) #......................... addSeperator(self) #......................... addCopyAttributes(self) addPasteAttributes(self) #......................... addSeperator(self) #......................... addUndo(self) addRedo(self) #......................... addSeperator(self) #......................... addClear(self) self.showPopupMenu() def EntityAtCursorNoSelectPopup(self, event): """ A graphical entity is under the mouse cursor, but no selected items """ self.initilizePopupMenu(event) addLogo(self) #......................... addSeperator(self) #......................... addEditEntity(self) addDragOverlap(self) addDrawArrow(self) addResizeEntity(self) #......................... addSeperator(self) #......................... addSelectAll(self) addPaste(self) #......................... addSeperator(self) #......................... addCopyAttributes(self) addPasteAttributes(self) #......................... addSeperator(self) #......................... addUndo(self) addRedo(self) self.showPopupMenu() def LinkAtCursorMultiSelectPopup(self, event): """ A graphical link/connection is under the mouse cursor, along with multiple selected items """ self.initilizePopupMenu(event) addLogo(self) #......................... addSeperator(self) #......................... addLayoutMenu(self) addEditEntity(self) addDragOverlap(self) addArrowEditor(self) addResizeEntity(self) addNodeLabelDragToggle(self) #......................... addSeperator(self) #......................... addSmoothSelected(self) addToggleSmoothMode(self) #......................... addSeperator(self) #......................... addSelectAll(self) addDeselectAll(self) #......................... addSeperator(self) #......................... addCut(self) addCopy(self) addPaste(self) #......................... addSeperator(self) #......................... addCopyAttributes(self) addPasteAttributes(self) #......................... addSeperator(self) #......................... addUndo(self) addRedo(self) #......................... addSeperator(self) #......................... addClear(self) self.showPopupMenu() def LinkAtCursorNoSelectPopup(self, event): """ A graphical link/connection is under the mouse cursor, but there are no selected items """ self.initilizePopupMenu(event) addLogo(self) #......................... addSeperator(self) #......................... addEditEntity(self) addDragOverlap(self) addArrowEditor(self) #......................... addSeperator(self) #......................... addSelectAll(self) addToggleSmoothMode(self) addPaste(self) #......................... addSeperator(self) #......................... addCopyAttributes(self) addPasteAttributes(self) #......................... addSeperator(self) #......................... addUndo(self) addRedo(self) self.showPopupMenu() def ArrowEditorPopup(self, event): """ Menu for the arrow editor """ self.initilizePopupMenu(event) addLogo(self) #......................... addSeperator(self) #......................... addEditEntity(self) addInsertPoint(self) addDeletePoint(self) addSmoothSelected(self) addNodeLabelMoveToggle(self) #......................... addSeperator(self) #......................... addArrowEditorExit(self) self.showPopupMenu() # ----------------------- Popup a specific submenu ------------------------- def LayoutPopup(self, event): self.initilizePopupMenu(event) self.popupMenu = self.atom3i.layoutMenu self.showPopupMenu() def ExportPopup(self, event): self.initilizePopupMenu(event) self.popupMenu = self.atom3i.exportMenu self.showPopupMenu() def ModelPopup(self, event): self.initilizePopupMenu(event) self.popupMenu = self.atom3i.modelMenu self.showPopupMenu() def TransformationPopup(self, event): self.initilizePopupMenu(event) self.popupMenu = self.atom3i.transMenu self.showPopupMenu() def FilePopup(self, event): self.initilizePopupMenu(event) self.popupMenu = self.atom3i.filemenu self.showPopupMenu() def LastModelPopup(self, event): self.initilizePopupMenu(event) addOpenLastModelSubroutine(self, self.popupMenu) self.showPopupMenu() def LastMetaModelPopup(self, event): self.initilizePopupMenu(event) addOpenLastMetaModelSubroutine(self, self.popupMenu) self.showPopupMenu() def SourcePathPopup(self, event): self.initilizePopupMenu(event) addSourcePathSubroutine(self, self.popupMenu) self.showPopupMenu() # ------------------------ String List to PopupMenu --------------------------------- def listChoicePopup(self, title, stringList, unused=None): """ Creates a popup menu with radiobuttons labeled from the stringList. Returns the index of the label that was chosen. NOTE: choosing outside the popup implicitly chooses index 0 """ # Remove any existing popups first self.popupRemover() self.popupMenu = Menu(self.master, tearoff=0) integerVar = IntVar() self.popupMenu.add_command(label=title, command=self.popupRemover) self.popupMenu.add_separator() i = 1 for label in stringList: self.popupMenu.add_radiobutton(label=label, variable=integerVar, value=i, indicatoron=False) i += 1 # This gets the last known co-ordinates of the mouse :D # NOTE: We get co-ordinates in terms of canvas space, convert back into # screenspace first before using them... x, y = self.atom3i.cb.getLastClickCoord() dc = self.atom3i.cb.getCanvas() x, y = [x - dc.canvasx(0), y - dc.canvasy(0)] # These offsets place the menu just where I like it... x = int(x) + 40 #+ 100 y = int(y) + 40 #+ 20 # Posts the menu, and blocks program execution here on win32 only self.popupMenu.post(x, y) # Blocks program execution (all platforms) & waits for integerVar to be updated # Not ideal: If we close the popup without selecting anything this will # wait forever and execution will never get anywhere beyond this point!!! # Moreover: AToM3 will not shutdown properly! #self.master.wait_variable( integerVar ) # THEORY: This will work whether or not the post() blocks or not # Practice: Works great on WinXP with Python 2.3 # Linux? while (1): self.master.update() value = integerVar.get() # Hapiness, we got the value we wanted if (value > 0): return value # The user killed the popup! O_O elif (self.popupMenu == None): return 0 # Unhapiness, the user avoided selecting anything elif (value == 0): self.popupMenu.unpost() self.popupMenu.post(x, y) self.master.update() time.sleep(0.4) return 0 # We won't get here, but just in case... def listChoicePopupAlternative(self, title, stringList, actionLabel): """ OBSOLETE --- Delete this """ raise Exception, "No one uses this method! But if you see this, maybe not so..." """
class SyntaxHighlightingText(ScrolledText2): # constructor def __init__(self, root, change_hook = None, highlighter = None, grammar=None): ScrolledText2.__init__(self,root,change_hook) # Non-wrapping, no border, undo turned on, max undo 50 self.text = self # For the methods taken from IDLE self.root = root self.change_hook = change_hook self.characters = ascii_letters + digits + punctuation self.tabwidth = 8 # for IDLE use, must remain 8 until Tk is fixed self.indentwidth = 4 self.indention = 0 # The current indention level self.set_tabwidth(self.indentwidth) # IDLE... self.previous_line = "0" # create a popup menu self.menu = Menu(root, tearoff=0) self.menu.add_command(label="Undo", command=self.edit_undo) self.menu.add_command(label="Redo", command=self.edit_redo) #self.menu.add_command(type="separator") self.menu.add_command(label="Cut", command=self.cut) self.menu.add_command(label="Copy", command=self.copy) self.menu.add_command(label="Paste", command=self.paste) self.bind('<KeyRelease>', self.key_release) # For scanning input self.bind('<Return>',self.autoindent) # Overides default binding #self.bind('<Tab>',self.autoindent) # increments self.indention #self.bind('<BackSpace>',self.autoindent) # decrements self.indention self.bind('<Button-3>', self.popup) # right mouse button opens popup self.bind('<Button-1>', self.recolorCurrentLine) # left mouse can reposition cursor, so recolor (e.g. bracket highlighting necessary) self.bind('<Control-Any-KeyPress>', self.ctrl) self.grammar = grammar self.setHighlighter(highlighter) def setHighlighter(self, highlighter): if highlighter == None: highlighter = Highlighter() self.highlighter = highlighter # sets up the tags for tag, settings in self.highlighter.tags.items(): self.tag_config(tag, **settings) def popup(self, event): self.menu.post(event.x_root, event.y_root) def get_tabwidth(self): # From IDLE current = self['tabs'] or 5000 return int(current) def set_tabwidth(self, newtabwidth): # From IDLE text = self if self.get_tabwidth() != newtabwidth: pixels = text.tk.call("font", "measure", text["font"], "-displayof", text.master, "n" * newtabwidth) text.configure(tabs=pixels) def remove_singleline_tags(self, start, end): for tag in self.highlighter.tags.keys(): if tag[:2] != 'ml': self.tag_remove(tag, start, end) def get_selection_indices(self): # If a selection is defined in the text widget, return (start, # end) as Tkinter text indices, otherwise return (None, None) try: first = self.text.index("sel.first") last = self.text.index("sel.last") return first, last except TclError: return None # Select all the text in textbox def select_all(self): self.tag_add(SEL, "1.0", END) self.mark_set(INSERT, END) self.see(INSERT) self.focus_set() return 'break' def cut(self,event=0): self.clipboard_clear() Selection=self.get_selection_indices() if Selection is not None: SelectedText = self.get(Selection[0],Selection[1]) self.delete(Selection[0],Selection[1]) self.clipboard_append(SelectedText) self.onChange() def copy(self,event=0): self.clipboard_clear() Selection=self.get_selection_indices() if Selection is not None: SelectedText = self.get(Selection[0],Selection[1]) self.clipboard_append(SelectedText) def paste(self,event=0): # This should call colorize for the pasted lines. SelectedText = self.root.selection_get(selection='CLIPBOARD') Selection=self.get_selection_indices() if Selection is not None: self.delete(Selection[0],Selection[1]) self.insert(INSERT, SelectedText) self.onChange() return "break" def autoindent(self,event): if event.keysym == 'Return': self.edit_separator() # For undo/redo index = self.index(INSERT).split('.') #print index line = int(index[0]) column = int(index[1]) if self.get('%s.%d'%(line, column-1)) == ':': self.indention += 1 #print '\n', #print '\t'*self.indention self.insert(INSERT,'\n') self.insert(INSERT,'\t'*self.indention) return 'break' # Overides standard bindings elif event.keysym == 'Tab': self.edit_separator() self.indention += 1 #print self.indention elif event.keysym == 'BackSpace': self.edit_separator() index = self.index(INSERT).split('.') #print index line = int(index[0]) column = int(index[1]) if self.get('%s.%d'%(line, column-1)) == '\t': self.indention -= 1 def recolorCurrentLine(self, *foo): pos = self.index(INSERT) cline = pos.split('.')[0] #print "recoloring %s, %s" % (cline, self.previous_line) if cline != self.previous_line: self.colorize(self.previous_line) self.colorize(cline) self.previous_line = cline def key_release(self, key): #print "pressed", key.keysym, dir(key) if key.char in ' :[(]),"\'': self.edit_separator() # For undo/redo # recolorize the current line and the previous line (if it's a different one) self.recolorCurrentLine() # if delete or backspace were pressed, check if a multiline comment has to be removed pos = self.index(INSERT) if key.keysym in ("BackSpace", "Delete"): #print "removal at %s" % pos ranges = self.tag_ranges('mlcom') i = 0 while i < len(ranges): range = ranges[i:i+2] second_range = (self.index(str(range[0]) + " + 1 char"), self.index(str(range[1]) + " - 1 char")) #print pos, range, second_range if pos in range or pos in second_range: self.tag_remove('mlcom', range[0], range[1]) i += 2 # notify of change if any. masks for the key.state variable # 0x0001 Shift. # 0x0002 Caps Lock. # 0x0004 Control. # 0x0008 Left-hand Alt. # 0x0010 Num Lock. # 0x0080 Right-hand Alt. # 0x0100 Mouse button 1. # 0x0200 Mouse button 2. # 0x0400 Mouse button 3. if key.char != '' and not (key.state & 4) or key.keysym in ("BackSpace", "Delete"): self.onChange() else: pass #print key def onChange(self): if self.change_hook is not None: self.change_hook() def delete_current_line(self): selection = self.get_selection_indices() if selection is None: start = int(self.index(INSERT).split('.')[0]) end = start else: start = int(selection[0].split('.')[0]) end = int(selection[1].split('.')[0]) self.delete('%d.0' % start, '%d.end' % end) self.onChange() # return 'break' def ctrl(self, key): if key.keysym == 'c': return self.copy() elif key.keysym == 'x': return self.cut() elif key.keysym == 'v': return self.paste() elif key.keysym == 'a': return self.select_all() elif key.keysym == 'd': return self.delete_current_line() #pass # apparently now implemented in the control itself # edit: yes, but with counterintuitive behavior def colorize(self, cline): cursorPos = self.index(INSERT) buffer = self.get('%s.%d' % (cline,0), '%s.end' % (cline)) # remove non-multiline tags self.remove_singleline_tags('%s.%d'% (cline, 0), '%s.end'% (cline)) in_quote = False quote_start = 0 for i in range(len(buffer)): here = '%s.%d' % (cline, i) # strings if buffer[i] in ['"',"'"]: # Doesn't distinguish between single and double quotes... if in_quote: self.tag_add('str', '%s.%d' % (cline, quote_start), '%s.%d' % (cline, i+1)) in_quote = False else: quote_start = i in_quote = True if not in_quote: # operators if False: for op in self.highlighter.operators: if buffer[i:i+len(op)] == op: self.tag_add('op', "%s.%d" % (cline, i), "%s.%d" % (cline, i+len(op))) # comments if buffer[i:i+2] == "//": self.tag_add('com', '%s.%d' % (cline, i), '%s.end' % (cline)) # multiline comments elif buffer[i:i+2] == "/*": if not here in self.tag_ranges('mlcom'): end_pos = self.search("*/", here, forwards=True) # get the end of the comment if not end_pos: continue if self.search("/*", here + " + 2 chars", stopindex=end_pos): # if there's a nested comment, ignore it (it might just be a nested /* with a */) continue #!!! make sure the area does not contain any "/*", because the "*/" is not the right one otherwise #print "multiline comment from %s to %s" % (here, str(end_pos)) self.tag_add('mlcom', here, str(end_pos) + " + 2 chars") elif buffer[i:i+2] == "*/": end_pos = self.index(here + " + 2 chars") if not end_pos in self.tag_ranges('mlcom'): start_pos = self.search("/*", here, backwards=True) # get the beginning of the comment if not start_pos: continue if self.search("*/", here, stopindex=start_pos, backwards=True): # if there's a nested comment, ignore it (it might just be a nested */ without a /*) continue #print "multiline comment from %s to %s" % (start_pos, end_pos) self.tag_add('mlcom', start_pos, end_pos) # bracket highlighting elif buffer[i] in self.highlighter.open_brackets and here == cursorPos: idxBracketType = self.highlighter.open_brackets.index(buffer[i]) openb, closeb = self.highlighter.brackets[idxBracketType] stack = 1 for j,c in enumerate(buffer[i+1:]): if c == openb: stack += 1 elif c == closeb: stack -= 1 if stack == 0: self.tag_add('bracket_hl', here, here + " + 1 char") self.tag_add('bracket_hl', "%s.%d" % (cline, i+1+j), "%s.%d" % (cline, i+1+j+1)) break elif buffer[i] in self.highlighter.close_brackets and self.index(here + " + 1 char") == cursorPos: idxBracketType = self.highlighter.close_brackets.index(buffer[i]) openb, closeb = self.highlighter.brackets[idxBracketType] stack = 1 l = list(buffer[:i]) l.reverse() for j,c in enumerate(l): if c == closeb: stack += 1 elif c == openb: stack -= 1 if stack == 0: self.tag_add('bracket_hl', here, here + " + 1 char") self.tag_add('bracket_hl', "%s.%d" % (cline, i-1-j), "%s.%d" % (cline, i-1-j+1)) break # tokens start, end = 0, 0 obj_flag = 0 # variable and predicate highlighting for match in re.finditer('(\\?[a-zA-Z0-9]+|[\w]*[a-zA-Z]\\()', buffer): token = match.group(0) if self.grammar is not None and self.grammar.isvar(token): self.tag_add('var', '%s.%d' % (cline, match.start()), '%s.%d' % (cline, match.end())) elif token[-1] == '(': self.tag_add('pred', '%s.%d' % (cline, match.start()), '%s.%d' % (cline, match.end()-1)) for token in buffer.split(' '): end = start + len(token) start_index = '%s.%d' % (cline, start) end_index = '%s.%d' % (cline, end) if obj_flag: self.tag_add('obj', start_index, end_index) obj_flag = 0 # keywords if token.strip() in self.highlighter.keywords: self.tag_add('kw', start_index, end_index) if token.strip() in ['def','class']: obj_flag = 1 else: # numbers try: float(token) except ValueError: pass else: self.tag_add('number', '%s.%d' % (cline, start), "%s.%d" % (cline, end)) start += len(token)+1 def insert(self, index, text, *args): line = int(self.index(index).split(".")[0]) Text.insert(self, index, text, *args) for i in range(text.count("\n")): self.colorize(str(line+i)) def disable(self, disable): Text.config(self, state=DISABLED if disable else NORMAL)
class TreeNode: def __init__(self, canvas, parent, item, menuList = []): self.canvas = canvas self.parent = parent self.item = item self.state = 'collapsed' self.selected = 0 self.children = {} self.kidKeys = [] self.x = self.y = None self.iconimages = {} # cache of PhotoImage instances for icons self.menuList = menuList self.menuVar = IntVar() self.menuVar.set(0) self._popupMenu = None self.image_id = None if self.menuList: if self.menuList[-1] == 'Separator': self.menuList = self.menuList[:-1] self._popupMenu = Menu(self.canvas, tearoff = 0) for i in range(len(self.menuList)): item = self.menuList[i] if item == 'Separator': self._popupMenu.add_separator() else: self._popupMenu.add_radiobutton( label = item, variable = self.menuVar, value = i, indicatoron = 0, command = self.popupMenuCommand) def destroy(self): for key in self.kidKeys: c = self.children[key] del self.children[key] c.destroy() self.parent = None def geticonimage(self, name): try: return self.iconimages[name] except KeyError: pass file, ext = os.path.splitext(name) ext = ext or ".gif" fullname = os.path.join(ICONDIR, file + ext) image = PhotoImage(master=self.canvas, file=fullname) self.iconimages[name] = image return image def select(self, event=None): if self.selected: return self.deselectall() self.selected = 1 if self.parent != None: if self.parent.state == 'expanded': self.canvas.delete(self.image_id) self.drawicon() self.drawtext() self.item.OnSelect(event) def deselect(self, event=None): if not self.selected: return self.selected = 0 if self.parent != None: if self.parent.state == 'expanded': self.canvas.delete(self.image_id) self.drawicon() self.drawtext() def deselectall(self): if self.parent: self.parent.deselectall() else: self.deselecttree() def deselecttree(self): if self.selected: self.deselect() for key in self.kidKeys: child = self.children[key] child.deselecttree() def flip(self, event=None): if self.state == 'expanded': self.collapse() else: self.expand() self.item.OnDoubleClick() return "break" def popupMenu(self, event=None): if self._popupMenu: self._popupMenu.post(event.widget.winfo_pointerx(), event.widget.winfo_pointery()) return "break" def popupMenuCommand(self): command = self.menuList[self.menuVar.get()] self.item.MenuCommand(command) if self.parent and (command != 'Update Explorer'): # Update parent to try to keep explorer up to date self.parent.update() def expand(self, event=None): if not self.item.IsExpandable(): return if self.state != 'expanded': self.state = 'expanded' self.update() self.view() def collapse(self, event=None): if self.state != 'collapsed': self.state = 'collapsed' self.update() def view(self): top = self.y - 2 bottom = self.lastvisiblechild().y + 17 height = bottom - top visible_top = self.canvas.canvasy(0) visible_height = self.canvas.winfo_height() visible_bottom = self.canvas.canvasy(visible_height) if visible_top <= top and bottom <= visible_bottom: return x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion']) if top >= visible_top and height <= visible_height: fraction = top + height - visible_height else: fraction = top fraction = float(fraction) / y1 self.canvas.yview_moveto(fraction) def reveal(self): # Make sure all parent nodes are marked as expanded parent = self.parent while parent: if parent.state == 'collapsed': parent.state = 'expanded' parent = parent.parent else: break # Redraw tree accordingly self.update() # Bring this item into view self.view() def lastvisiblechild(self): if self.kidKeys and self.state == 'expanded': return self.children[self.kidKeys[-1]].lastvisiblechild() else: return self def update(self): if self.parent: self.parent.update() else: oldcursor = self.canvas['cursor'] self.canvas['cursor'] = "watch" self.canvas.update() self.canvas.delete(Tkinter.ALL) # XXX could be more subtle self.draw(7, 2) x0, y0, x1, y1 = self.canvas.bbox(Tkinter.ALL) self.canvas.configure(scrollregion=(0, 0, x1, y1)) self.canvas['cursor'] = oldcursor def draw(self, x, y): # XXX This hard-codes too many geometry constants! self.x, self.y = x, y self.drawicon() self.drawtext() if self.state != 'expanded': return y+17 # draw children sublist = self.item._GetSubList() if not sublist: # IsExpandable() was mistaken; that's allowed return y+17 self.kidKeys = [] for item in sublist: key = item.GetKey() if self.children.has_key(key): child = self.children[key] else: child = TreeNode(self.canvas, self, item, self.menuList) self.children[key] = child self.kidKeys.append(key) # Remove unused children for key in self.children.keys(): if key not in self.kidKeys: del(self.children[key]) cx = x+20 cy = y+17 cylast = 0 for key in self.kidKeys: child = self.children[key] cylast = cy self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50") cy = child.draw(cx, cy) if child.item.IsExpandable(): if child.state == 'expanded': iconname = "minusnode" callback = child.collapse else: iconname = "plusnode" callback = child.expand image = self.geticonimage(iconname) id = self.canvas.create_image(x+9, cylast+7, image=image) # XXX This leaks bindings until canvas is deleted: self.canvas.tag_bind(id, "<1>", callback) self.canvas.tag_bind(id, "<Double-1>", lambda x: None) id = self.canvas.create_line(x+9, y+10, x+9, cylast+7, ##stipple="gray50", # XXX Seems broken in Tk 8.0.x fill="gray50") self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2 return cy def drawicon(self): if self.selected: imagename = (self.item.GetSelectedIconName() or self.item.GetIconName() or "openfolder") else: imagename = self.item.GetIconName() or "folder" image = self.geticonimage(imagename) id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image) self.image_id = id self.canvas.tag_bind(id, "<1>", self.select) self.canvas.tag_bind(id, "<Double-1>", self.flip) self.canvas.tag_bind(id, "<3>", self.popupMenu) def drawtext(self, text=None): textx = self.x+20-1 texty = self.y-1 labeltext = self.item.GetLabelText() if labeltext: id = self.canvas.create_text(textx, texty, anchor="nw", text=labeltext) self.canvas.tag_bind(id, "<1>", self.select) self.canvas.tag_bind(id, "<Double-1>", self.flip) x0, y0, x1, y1 = self.canvas.bbox(id) textx = max(x1, 200) + 10 if text==None: text = self.item.GetText() or "<no text>" try: self.entry except AttributeError: pass else: self.edit_finish() try: label = self.label except AttributeError: # padding carefully selected (on Windows) to match Entry widget: self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2) if self.selected: self.label.configure(fg="white", bg="darkblue") else: fg = self.item.GetTextFg() self.label.configure(fg=fg, bg="white") id = self.canvas.create_window(textx, texty, anchor="nw", window=self.label) self.label.bind("<1>", self.select_or_edit) self.label.bind("<Double-1>", self.flip) self.label.bind("<3>", self.popupMenu) # Update text if necessary if text != self.label['text']: self.label['text'] = text self.text_id = id def select_or_edit(self, event=None): if self.selected and self.item.IsEditable(): text = self.item.GetTextForEdit() self.label['text'] = text self.drawtext(text) self.edit(event) else: self.select(event) def edit(self, event=None): self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0) self.entry.insert(0, self.label['text']) self.entry.selection_range(0, Tkinter.END) self.entry.pack(ipadx=5) self.entry.focus_set() self.entry.bind("<Return>", self.edit_finish) self.entry.bind("<Escape>", self.edit_cancel) def edit_finish(self, event=None): try: entry = self.entry del self.entry except AttributeError: return text = entry.get() entry.destroy() if text and text != self.item.GetText(): self.item.SetText(text) text = self.item.GetText() self.label['text'] = text self.drawtext() self.canvas.focus_set() def edit_cancel(self, event=None): self.drawtext() self.canvas.focus_set() def find(self, searchKey): # Search for a node who's key matches the given key # Is it this node if searchKey == self.item.GetKey(): return self # Nope, check the children sublist = self.item._GetSubList() for item in sublist: key = item.GetKey() # Use existing child or create new TreeNode if none exists if self.children.has_key(key): child = self.children[key] else: child = TreeNode(self.canvas, self, item, self.menuList) # Update local list of children and keys self.children[key] = child self.kidKeys.append(key) # See if node is child (or one of child's descendants) retVal = child.find(searchKey) if retVal: return retVal # Not here return None
class ListFrame(LabelFrame): """ A Frame representing one of the search term lists (e.g. Hashtags, Excluded Users). Displays all the items in the list, and allows the user to add or remove items. Methods should not be called directly; instead they should be bound as event handlers. """ def __init__(self, name, add_handler, remove_handler, master=None): """ Creates a ListFrame with the given name as its title. add_handler and remove_handler are functions to be called when items are added or removed, and should relay the information back to the Searcher (or whatever object actually uses the list). """ LabelFrame.__init__(self, master) self['text'] = name self.add_handler = add_handler self.remove_handler = remove_handler self.list = Listbox(self) self.list.grid(row=0, columnspan=2) # Tkinter does not automatically close the right-click menu for us, # so we must close it when the user clicks away from the menu. self.list.bind("<Button-1>", lambda event: self.context_menu.unpost()) self.list.bind("<Button-3>", self.open_menu) self.context_menu = Menu(self, tearoff=0) self.context_menu.add_command(label="Remove", command=self.remove) self.input = Entry(self) self.input.bind("<Return>", lambda event: self.add()) self.input.grid(row=1, columnspan=2) self.add_button = Button(self) self.add_button['text'] = "Add" self.add_button['command'] = self.add self.add_button.grid(row=2, column=0, sticky=W + E) self.remove_button = Button(self) self.remove_button['text'] = "Remove" self.remove_button['command'] = self.remove self.remove_button.grid(row=2, column=1, sticky=W + E) def add(self): """ Add the item in the input line to the list. """ self.list.insert(END, self.input.get()) self.add_handler(self.input.get()) self.input.delete(0, END) def remove(self): """ Remove the active (highlighted) item from the list. """ deleted = self.list.get(ACTIVE) self.list.delete(ACTIVE) self.remove_handler(deleted) def open_menu(self, event): """ Opens a right-click menu for the selected item. Currently the menu only has an option for removing the item. """ index = self.list.index("@" + str(event.x) + "," + str(event.y)) if index < 0: return self.context_menu.post(event.x_root, event.y_root) self.list.activate(index) self.list.selection_clear(0, END) self.list.selection_set(ACTIVE)
class SyntaxHighlightingText(ScrolledText2): # constructor def __init__(self, root, change_hook=None, highlighter=None, grammar=None): ScrolledText2.__init__(self, root, change_hook) # Non-wrapping, no border, undo turned on, max undo 50 self.text = self # For the methods taken from IDLE self.root = root self.change_hook = change_hook self.characters = ascii_letters + digits + punctuation self.tabwidth = 8 # for IDLE use, must remain 8 until Tk is fixed self.indentwidth = 4 self.indention = 0 # The current indention level self.set_tabwidth(self.indentwidth) # IDLE... self.previous_line = "0" # create a popup menu self.menu = Menu(root, tearoff=0) self.menu.add_command(label="Undo", command=self.edit_undo) self.menu.add_command(label="Redo", command=self.edit_redo) #self.menu.add_command(type="separator") self.menu.add_command(label="Cut", command=self.cut) self.menu.add_command(label="Copy", command=self.copy) self.menu.add_command(label="Paste", command=self.paste) self.bind('<KeyRelease>', self.key_release) # For scanning input self.bind('<Return>', self.autoindent) # Overides default binding #self.bind('<Tab>',self.autoindent) # increments self.indention #self.bind('<BackSpace>',self.autoindent) # decrements self.indention self.bind('<Button-3>', self.popup) # right mouse button opens popup self.bind( '<Button-1>', self.recolorCurrentLine ) # left mouse can reposition cursor, so recolor (e.g. bracket highlighting necessary) self.bind('<Control-Any-KeyPress>', self.ctrl) self.grammar = grammar self.setHighlighter(highlighter) def setHighlighter(self, highlighter): if highlighter == None: highlighter = Highlighter() self.highlighter = highlighter # sets up the tags for tag, settings in self.highlighter.tags.items(): self.tag_config(tag, **settings) def popup(self, event): self.menu.post(event.x_root, event.y_root) def get_tabwidth(self): # From IDLE current = self['tabs'] or 5000 return int(current) def set_tabwidth(self, newtabwidth): # From IDLE text = self if self.get_tabwidth() != newtabwidth: pixels = text.tk.call("font", "measure", text["font"], "-displayof", text.master, "n" * newtabwidth) text.configure(tabs=pixels) def remove_singleline_tags(self, start, end): for tag in self.highlighter.tags.keys(): if tag[:2] != 'ml': self.tag_remove(tag, start, end) def get_selection_indices(self): # If a selection is defined in the text widget, return (start, # end) as Tkinter text indices, otherwise return (None, None) try: first = self.text.index("sel.first") last = self.text.index("sel.last") return first, last except TclError: return None # Select all the text in textbox def select_all(self): self.tag_add(SEL, "1.0", END) self.mark_set(INSERT, END) self.see(INSERT) self.focus_set() return 'break' def cut(self, event=0): self.clipboard_clear() Selection = self.get_selection_indices() if Selection is not None: SelectedText = self.get(Selection[0], Selection[1]) self.delete(Selection[0], Selection[1]) self.clipboard_append(SelectedText) self.onChange() def copy(self, event=0): self.clipboard_clear() Selection = self.get_selection_indices() if Selection is not None: SelectedText = self.get(Selection[0], Selection[1]) self.clipboard_append(SelectedText) def paste(self, event=0): # This should call colorize for the pasted lines. SelectedText = self.root.selection_get(selection='CLIPBOARD') Selection = self.get_selection_indices() if Selection is not None: self.delete(Selection[0], Selection[1]) self.insert(INSERT, SelectedText) self.onChange() return "break" def autoindent(self, event): if event.keysym == 'Return': self.edit_separator() # For undo/redo index = self.index(INSERT).split('.') #print index line = int(index[0]) column = int(index[1]) if self.get('%s.%d' % (line, column - 1)) == ':': self.indention += 1 #print '\n', #print '\t'*self.indention self.insert(INSERT, '\n') self.insert(INSERT, '\t' * self.indention) return 'break' # Overides standard bindings elif event.keysym == 'Tab': self.edit_separator() self.indention += 1 #print self.indention elif event.keysym == 'BackSpace': self.edit_separator() index = self.index(INSERT).split('.') #print index line = int(index[0]) column = int(index[1]) if self.get('%s.%d' % (line, column - 1)) == '\t': self.indention -= 1 def recolorCurrentLine(self, *foo): pos = self.index(INSERT) cline = pos.split('.')[0] #print "recoloring %s, %s" % (cline, self.previous_line) if cline != self.previous_line: self.colorize(self.previous_line) self.colorize(cline) self.previous_line = cline def key_release(self, key): #print "pressed", key.keysym, dir(key) if key.char in ' :[(]),"\'': self.edit_separator() # For undo/redo # recolorize the current line and the previous line (if it's a different one) self.recolorCurrentLine() # if delete or backspace were pressed, check if a multiline comment has to be removed pos = self.index(INSERT) if key.keysym in ("BackSpace", "Delete"): #print "removal at %s" % pos ranges = self.tag_ranges('mlcom') i = 0 while i < len(ranges): range = ranges[i:i + 2] second_range = (self.index(str(range[0]) + " + 1 char"), self.index(str(range[1]) + " - 1 char")) #print pos, range, second_range if pos in range or pos in second_range: self.tag_remove('mlcom', range[0], range[1]) i += 2 # notify of change if any. masks for the key.state variable # 0x0001 Shift. # 0x0002 Caps Lock. # 0x0004 Control. # 0x0008 Left-hand Alt. # 0x0010 Num Lock. # 0x0080 Right-hand Alt. # 0x0100 Mouse button 1. # 0x0200 Mouse button 2. # 0x0400 Mouse button 3. if key.char != '' and not (key.state & 4) or key.keysym in ( "BackSpace", "Delete"): self.onChange() else: pass #print key def onChange(self): if self.change_hook is not None: self.change_hook() def delete_current_line(self): selection = self.get_selection_indices() if selection is None: start = int(self.index(INSERT).split('.')[0]) end = start else: start = int(selection[0].split('.')[0]) end = int(selection[1].split('.')[0]) self.delete('%d.0' % start, '%d.end' % end) self.onChange() # return 'break' def ctrl(self, key): if key.keysym == 'c': return self.copy() elif key.keysym == 'x': return self.cut() elif key.keysym == 'v': return self.paste() elif key.keysym == 'a': return self.select_all() elif key.keysym == 'd': return self.delete_current_line() #pass # apparently now implemented in the control itself # edit: yes, but with counterintuitive behavior def colorize(self, cline): cursorPos = self.index(INSERT) buffer = self.get('%s.%d' % (cline, 0), '%s.end' % (cline)) # remove non-multiline tags self.remove_singleline_tags('%s.%d' % (cline, 0), '%s.end' % (cline)) in_quote = False quote_start = 0 for i in range(len(buffer)): here = '%s.%d' % (cline, i) # strings if buffer[i] in [ '"', "'" ]: # Doesn't distinguish between single and double quotes... if in_quote: self.tag_add('str', '%s.%d' % (cline, quote_start), '%s.%d' % (cline, i + 1)) in_quote = False else: quote_start = i in_quote = True if not in_quote: # operators if False: for op in self.highlighter.operators: if buffer[i:i + len(op)] == op: self.tag_add('op', "%s.%d" % (cline, i), "%s.%d" % (cline, i + len(op))) # comments if buffer[i:i + 2] == "//": self.tag_add('com', '%s.%d' % (cline, i), '%s.end' % (cline)) # multiline comments elif buffer[i:i + 2] == "/*": if not here in self.tag_ranges('mlcom'): end_pos = self.search( "*/", here, forwards=True) # get the end of the comment if not end_pos: continue if self.search( "/*", here + " + 2 chars", stopindex=end_pos ): # if there's a nested comment, ignore it (it might just be a nested /* with a */) continue #!!! make sure the area does not contain any "/*", because the "*/" is not the right one otherwise #print "multiline comment from %s to %s" % (here, str(end_pos)) self.tag_add('mlcom', here, str(end_pos) + " + 2 chars") elif buffer[i:i + 2] == "*/": end_pos = self.index(here + " + 2 chars") if not end_pos in self.tag_ranges('mlcom'): start_pos = self.search( "/*", here, backwards=True) # get the beginning of the comment if not start_pos: continue if self.search( "*/", here, stopindex=start_pos, backwards=True ): # if there's a nested comment, ignore it (it might just be a nested */ without a /*) continue #print "multiline comment from %s to %s" % (start_pos, end_pos) self.tag_add('mlcom', start_pos, end_pos) # bracket highlighting elif buffer[ i] in self.highlighter.open_brackets and here == cursorPos: idxBracketType = self.highlighter.open_brackets.index( buffer[i]) openb, closeb = self.highlighter.brackets[idxBracketType] stack = 1 for j, c in enumerate(buffer[i + 1:]): if c == openb: stack += 1 elif c == closeb: stack -= 1 if stack == 0: self.tag_add('bracket_hl', here, here + " + 1 char") self.tag_add('bracket_hl', "%s.%d" % (cline, i + 1 + j), "%s.%d" % (cline, i + 1 + j + 1)) break elif buffer[ i] in self.highlighter.close_brackets and self.index( here + " + 1 char") == cursorPos: idxBracketType = self.highlighter.close_brackets.index( buffer[i]) openb, closeb = self.highlighter.brackets[idxBracketType] stack = 1 l = list(buffer[:i]) l.reverse() for j, c in enumerate(l): if c == closeb: stack += 1 elif c == openb: stack -= 1 if stack == 0: self.tag_add('bracket_hl', here, here + " + 1 char") self.tag_add('bracket_hl', "%s.%d" % (cline, i - 1 - j), "%s.%d" % (cline, i - 1 - j + 1)) break # tokens start, end = 0, 0 obj_flag = 0 # variable and predicate highlighting for match in re.finditer('(\\?[a-zA-Z0-9]+|[\w]*[a-zA-Z]\\()', buffer): token = match.group(0) if self.grammar is not None and self.grammar.isvar(token): self.tag_add('var', '%s.%d' % (cline, match.start()), '%s.%d' % (cline, match.end())) elif token[-1] == '(': self.tag_add('pred', '%s.%d' % (cline, match.start()), '%s.%d' % (cline, match.end() - 1)) for token in buffer.split(' '): end = start + len(token) start_index = '%s.%d' % (cline, start) end_index = '%s.%d' % (cline, end) if obj_flag: self.tag_add('obj', start_index, end_index) obj_flag = 0 # keywords if token.strip() in self.highlighter.keywords: self.tag_add('kw', start_index, end_index) if token.strip() in ['def', 'class']: obj_flag = 1 else: # numbers try: float(token) except ValueError: pass else: self.tag_add('number', '%s.%d' % (cline, start), "%s.%d" % (cline, end)) start += len(token) + 1 def insert(self, index, text, *args): line = int(self.index(index).split(".")[0]) Text.insert(self, index, text, *args) for i in range(text.count("\n")): self.colorize(str(line + i)) def disable(self, disable): Text.config(self, state=DISABLED if disable else NORMAL)
class Text(mx.AllMixins, tk.Text): def __init__(self, parent, scrollbar=True, **kw): parent = mx.get_master(parent) self.parent = parent frame = Frame(parent) frame.pack(fill='both', expand=True) # text widget if "wrap" not in kw: kw["wrap"] = "word" tk.Text.__init__(self, frame, **kw) #self.pack(side='left', fill='both', expand=True) mx.AllMixins.__init__(self, parent) # scrollbar if scrollbar: scrb = Scrollbar(frame, orient='vertical', command=self.yview) self.config(yscrollcommand=scrb.set) scrb.pack(side='right', fill='y') # pop-up menu self.popup = Menu(self, tearoff=0) self.popup.add_command(label='Cut', command=self._cut) self.popup.add_command(label='Copy', command=self._copy) self.popup.add_command(label='Paste', command=self._paste) self.popup.add_separator() self.popup.add_command(label='Select All', command=self._select_all) self.popup.add_command(label='Clear All', command=self._clear_all) self.bind('<Button-3>', self._show_popup) # only allow mouse scroll when mouse inside text self.bind("<Leave>", lambda event: self.winfo_toplevel().focus_set(), "+") self.bind("<Enter>", lambda event: self.focus_set(), "+") def apply_theme(self, theme='standard'): '''theme=['standard', 'typewriter', 'terminal']''' if theme == 'typewriter': '''takes all inserted text and inserts it one char every 100ms''' options = {"font": ('Times', 10, 'bold')} self.config(options) text = self.get('1.0', 'end') self.delete('1.0', 'end') self.char_index = 0 self._typewriter([char for char in text]) elif theme == 'terminal': '''blocky insert cursor''' options = {'bg': 'black', 'fg': 'white', 'font': ('Courier', 10)} self.config(options) self.cursor = '1.0' self.fg = self.cget('fg') self.bg = self.cget('bg') self.switch = self.fg self.config(insertwidth=0) self._blink_cursor() self._place_cursor() elif theme == 'matrix': '''blocky insert cursor''' options = {'bg': 'black', 'fg': 'green', 'font': ('Courier', 10)} self.config(options) self.cursor = '1.0' self.fg = self.cget('fg') self.bg = self.cget('bg') self.switch = self.fg self.config(insertwidth=0) self._blink_cursor() self._place_cursor() def highlight_pattern(self, pattern, tag, start="1.0", end="end", regexp=False, nocase=True, exact=True, **kwargs): '''Apply the given tag to all text that matches the given pattern If 'regexp' is set to True, pattern will be treated as a regular expression. From: http://stackoverflow.com/questions/3781670/how-to-highlight-text-in-a-tkinter-text-widget ''' if not pattern: return 0 start = self.index(start) end = self.index(end) self.mark_set("matchStart", start) self.mark_set("matchEnd", start) self.mark_set("searchLimit", end) count = tk.IntVar() while True: index = self.search(pattern, "matchEnd", "searchLimit", count=count, regexp=regexp, nocase=nocase, exact=exact, **kwargs) if index: self.mark_set("matchStart", index) self.mark_set("matchEnd", "%s+%sc" % (index, count.get())) self.tag_add(tag, "matchStart", "matchEnd") else: break return count.get() def clear_highlights(self, *tagnames): names = tagnames or self.tag_names() self.tag_delete(*names) def next_pattern(self, pattern, current, regexp=False, nocase=True, exact=True, **kwargs): if not pattern: return None start = self.index(current) end = self.index("end") count = tk.IntVar() index = self.search(pattern, start, end, count=count, regexp=regexp, nocase=nocase, exact=exact, **kwargs) if index: return index, index + "+%sc" % count.get() else: return None def prev_pattern(self, pattern, current, regexp=False, nocase=True, exact=True, **kwargs): if not pattern: return None start = self.index(current) end = self.index("1.0") count = tk.IntVar() index = self.search(pattern, start, end, count=count, regexp=regexp, nocase=nocase, exact=exact, backwards=True, **kwargs) if index: return index, index + "-%sc" % count.get() else: return None # MISC INTERNALS def _show_popup(self, event): '''right-click popup menu''' if self.parent.focus_get() != self: self.focus_set() try: self.popup.post(event.x_root, event.y_root) finally: self.popup.grab_release() def _cut(self): try: selection = self.get(*self.tag_ranges('sel')) self.clipboard_clear() self.clipboard_append(selection) self.delete(*self.tag_ranges('sel')) except TypeError: pass def _copy(self): try: selection = self.get(*self.tag_ranges('sel')) self.clipboard_clear() self.clipboard_append(selection) except TypeError: pass def _paste(self): self.insert('insert', self.selection_get(selection='CLIPBOARD')) def _select_all(self): '''selects all text''' self.tag_add('sel', '1.0', 'end-1c') def _clear_all(self): '''erases all text''' isok = askokcancel('Clear All', 'Erase all text?', parent=self, default='ok') if isok: self.delete('1.0', 'end') def _typewriter(self, text): # theme: typewriter '''after the theme is applied, this method takes all the inserted text and types it out one character every 100ms''' self.insert('insert', text[self.char_index]) self.char_index += 1 if hasattr(self, "typer") and self.char_index == len(text): self.after_cancel(self.typer) else: self.typer = self.after(100, self._typewriter, text) def _place_cursor(self): # theme: terminal '''check the position of the cursor against the last known position every 15ms and update the cursorblock tag as needed''' current_index = self.index('insert') if self.cursor != current_index: self.cursor = current_index self.tag_delete('cursorblock') start = self.index('insert') end = self.index('insert+1c') if start[0] != end[0]: self.insert(start, ' ') end = self.index('insert') self.tag_add('cursorblock', start, end) self.mark_set('insert', self.cursor) self.after(15, self._place_cursor) def _blink_cursor(self): # theme: terminal '''alternate the background color of the cursorblock tagged text every 600 milliseconds''' if self.switch == self.fg: self.switch = self.bg else: self.switch = self.fg self.tag_config('cursorblock', background=self.switch) self.after(600, self._blink_cursor)
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 = tkFont.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 = tkFont.Font(family="helvetica", weight="bold", size=self._size.get()) self._font = tkFont.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.node()["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 1 elif self.shift(): return 1 else: if len(self._parser.parses()) > 0: 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 1 return 0 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__).strip(), width=75, font="fixed") except: ShowText(self._top, "Help: Shift-Reduce Parser Application", (__doc__).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 tkMessageBox 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 = string.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].node().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.node), 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.node()["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.node()["color"] = "black" else: stackwidget["color"] = "black"
class IntegerView(Label, object): """This is the frame for displaying Integer with ability to factorize it. """ def __init__(self, parent, integer=Integer(), **kw): #kw['state'] = 'disabled' kw.setdefault('anchor', 'nw') kw.setdefault('relief', 'sunken') kw.setdefault('width', 10) kw.setdefault('justify', 'left') self._var = StringVar() Label.__init__(self, parent, textvariable=self._var, **kw) self._factorization_enabled = False self.integer = integer self.bind("<Configure>", self._update_width) self._init_menu() def _init_menu(self): self._menu = Menu(self, tearoff=0) self._menu.add_command(label="Copy", command=self._copy) self._menu.add_command(label="Copy LaTeX", command=self._copy_latex) # right button on Mac and other systems button = '2' if tools.IS_MAC else '3' self.bind("<Button-{}>".format(button), self._right_click) def _right_click(self, event): self._menu.post(event.x_root, event.y_root) def _copy(self): pyperclip.setcb(str(self._integer)) def _copy_latex(self): if self._factorization_enabled: cb = self._integer.str_latex() else: cb = self._integer.str_normal() pyperclip.setcb(cb) def _update_width(self, event): self['wraplength'] = self.winfo_width() - 10 def _update_integer(self): self._var.set(str(self._integer)) @property def integer(self): return self._integer @integer.setter def integer(self, value): self._integer = MultiModeStringFormatter.mixin_to(value) if self._factorization_enabled: self._integer.str_mode = 'verbose' self._update_integer() def toggle_factorization(self, value): self._factorization_enabled = value self._integer.str_mode = 'verbose' if value else 'normal' self._update_integer()
class PopupMenuCreator: def __init__(self, atom3i ): self.master = atom3i.parent self.atom3i = atom3i self.cb = atom3i.cb self.optionsDatabase = atom3i.optionsDatabase self.popupLogoPhotoimage = Embedded_Images().getPopupLogo() self.popupMenu = None self.event = None # --------------------------- Popup Utilities ------------------------------- def initilizePopupMenu( self, event ): """ Create a new popup menu """ if( self.popupMenu ): self.popupMenu.unpost() self.popupMenu = Menu(self.master , tearoff=0, bg = "white") self.event = event def showPopupMenu( self ): """ Display the popup menu """ if( self.popupMenu ): self.popupMenu.post(self.event.x_root, self.event.y_root) def swapMenu(self, menu ): """ This is a fix for a problem that no longer exists :p It essentially takes one menu and slaps another one in its place. """ raise Exception, "No one uses this method! But if you see this, maybe not so..." self.popupMenu.unpost() self.popupMenu = menu self.showPopupMenu() def popupRemover(self): """ Goodbye popup! """ if( self.popupMenu ): self.popupMenu.unpost() self.popupMenu = None # ---------------------- Context Sensitive Menus -------------------------- def NoCursorNoSelectPopup( self,event ): """ Popup menu to show when no items under the mouse, and no items selected """ self.initilizePopupMenu( event ) addLogo( self ) #......................... addSeperator( self ) #......................... addModelAction( self ) addSelectAll( self ) addPaste( self ) addUndo( self ) addRedo( self ) #......................... addSeperator( self ) #......................... addFileMenu( self ) addModelMenu( self ) addTransformationMenu( self ) addLayoutMenu( self ) addExportMenu( self ) #......................... addSeperator( self ) #......................... addOpenLastModel( self ) addOpenLastMetaModel(self) addSourcePath( self ) #......................... addSeperator( self ) #......................... addToggleSmoothMode( self ) #......................... addSeperator( self ) #......................... addExit( self ) self.showPopupMenu() def NoCursorMultiSelectPopup(self,event): """ Popup menu to show when no items under the mouse, and multiple items selected """ self.initilizePopupMenu( event ) addLogo( self ) #......................... addSeperator( self ) #......................... addLayoutMenu( self ) addResizeEntity( self ) addNodeLabelDragToggle( self ) #......................... addSeperator( self ) #......................... addSelectAll( self ) addDeselectAll( self ) #......................... addSeperator( self ) #......................... addCut( self ) addCopy( self ) addPaste( self ) #......................... addSeperator( self ) #......................... addUndo( self ) addRedo( self ) #......................... addSeperator( self ) #......................... addClear( self ) self.showPopupMenu() def EntityAtCursorMultiSelectPopup(self,event): """ A graphical entity is under the mouse cursor, along with multiple selected items """ self.initilizePopupMenu( event ) addLogo( self ) #......................... addSeperator( self ) #......................... addLayoutMenu( self ) addEditEntity( self ) addDragOverlap( self ) addDrawArrow( self ) addResizeEntity( self ) addNodeLabelDragToggle( self ) #......................... addSeperator( self ) #......................... addSelectAll( self ) addDeselectAll( self ) #......................... addSeperator( self ) #......................... addCut( self ) addCopy( self ) addPaste( self ) #......................... addSeperator( self ) #......................... addCopyAttributes( self ) addPasteAttributes( self ) #......................... addSeperator( self ) #......................... addUndo( self ) addRedo( self ) #......................... addSeperator( self ) #......................... addClear( self ) self.showPopupMenu() def EntityAtCursorNoSelectPopup(self,event): """ A graphical entity is under the mouse cursor, but no selected items """ self.initilizePopupMenu( event ) addLogo( self ) #......................... addSeperator( self ) #......................... addEditEntity( self ) addDragOverlap( self ) addDrawArrow( self ) addResizeEntity( self ) #......................... addSeperator( self ) #......................... addSelectAll( self ) addPaste( self ) #......................... addSeperator( self ) #......................... addCopyAttributes( self ) addPasteAttributes( self ) #......................... addSeperator( self ) #......................... addUndo( self ) addRedo( self ) self.showPopupMenu() def LinkAtCursorMultiSelectPopup(self,event): """ A graphical link/connection is under the mouse cursor, along with multiple selected items """ self.initilizePopupMenu( event ) addLogo( self ) #......................... addSeperator( self ) #......................... addLayoutMenu( self ) addEditEntity( self ) addDragOverlap( self ) addArrowEditor( self ) addResizeEntity( self ) addNodeLabelDragToggle( self ) #......................... addSeperator( self ) #......................... addSmoothSelected( self ) addToggleSmoothMode( self ) #......................... addSeperator( self ) #......................... addSelectAll( self ) addDeselectAll( self ) #......................... addSeperator( self ) #......................... addCut( self ) addCopy( self ) addPaste( self ) #......................... addSeperator( self ) #......................... addCopyAttributes( self ) addPasteAttributes( self ) #......................... addSeperator( self ) #......................... addUndo( self ) addRedo( self ) #......................... addSeperator( self ) #......................... addClear( self ) self.showPopupMenu() def LinkAtCursorNoSelectPopup(self,event): """ A graphical link/connection is under the mouse cursor, but there are no selected items """ self.initilizePopupMenu( event ) addLogo( self ) #......................... addSeperator( self ) #......................... addEditEntity( self ) addDragOverlap( self ) addArrowEditor( self ) #......................... addSeperator( self ) #......................... addSelectAll( self ) addToggleSmoothMode( self ) addPaste( self ) #......................... addSeperator( self ) #......................... addCopyAttributes( self ) addPasteAttributes( self ) #......................... addSeperator( self ) #......................... addUndo( self ) addRedo( self ) self.showPopupMenu() def ArrowEditorPopup(self,event): """ Menu for the arrow editor """ self.initilizePopupMenu( event ) addLogo( self ) #......................... addSeperator( self ) #......................... addEditEntity( self ) addInsertPoint( self ) addDeletePoint( self ) addSmoothSelected( self ) addNodeLabelMoveToggle( self ) #......................... addSeperator( self ) #......................... addArrowEditorExit( self ) self.showPopupMenu() # ----------------------- Popup a specific submenu ------------------------- def LayoutPopup(self,event): self.initilizePopupMenu( event ) self.popupMenu = self.atom3i.layoutMenu self.showPopupMenu() def ExportPopup(self,event): self.initilizePopupMenu( event ) self.popupMenu = self.atom3i.exportMenu self.showPopupMenu() def ModelPopup(self,event): self.initilizePopupMenu( event ) self.popupMenu = self.atom3i.modelMenu self.showPopupMenu() def TransformationPopup(self,event): self.initilizePopupMenu( event ) self.popupMenu = self.atom3i.transMenu self.showPopupMenu() def FilePopup(self,event): self.initilizePopupMenu( event ) self.popupMenu = self.atom3i.filemenu self.showPopupMenu() def LastModelPopup(self,event): self.initilizePopupMenu( event ) addOpenLastModelSubroutine( self, self.popupMenu ) self.showPopupMenu() def LastMetaModelPopup(self,event): self.initilizePopupMenu( event ) addOpenLastMetaModelSubroutine( self, self.popupMenu ) self.showPopupMenu() def SourcePathPopup(self,event): self.initilizePopupMenu( event ) addSourcePathSubroutine( self, self.popupMenu ) self.showPopupMenu() # ------------------------ String List to PopupMenu --------------------------------- def listChoicePopup(self, title, stringList, unused = None ): """ Creates a popup menu with radiobuttons labeled from the stringList. Returns the index of the label that was chosen. NOTE: choosing outside the popup implicitly chooses index 0 """ # Remove any existing popups first self.popupRemover() self.popupMenu = Menu(self.master , tearoff=0) integerVar = IntVar() self.popupMenu.add_command( label=title, command=self.popupRemover ) self.popupMenu.add_separator() i = 1 for label in stringList: self.popupMenu.add_radiobutton( label=label, variable=integerVar, value=i,indicatoron=False ) i += 1 # This gets the last known co-ordinates of the mouse :D # NOTE: We get co-ordinates in terms of canvas space, convert back into # screenspace first before using them... x,y = self.atom3i.cb.getLastClickCoord() dc = self.atom3i.cb.getCanvas() x,y = [x-dc.canvasx(0),y-dc.canvasy(0)] # These offsets place the menu just where I like it... x = int(x) +40 #+ 100 y = int(y) +40 #+ 20 # Posts the menu, and blocks program execution here on win32 only self.popupMenu.post( x,y ) # Blocks program execution (all platforms) & waits for integerVar to be updated # Not ideal: If we close the popup without selecting anything this will # wait forever and execution will never get anywhere beyond this point!!! # Moreover: AToM3 will not shutdown properly! #self.master.wait_variable( integerVar ) # THEORY: This will work whether or not the post() blocks or not # Practice: Works great on WinXP with Python 2.3 # Linux? while( 1 ): self.master.update() value = integerVar.get() # Hapiness, we got the value we wanted if( value > 0 ): return value # The user killed the popup! O_O elif( self.popupMenu == None ): return 0 # Unhapiness, the user avoided selecting anything elif( value == 0 ): self.popupMenu.unpost() self.popupMenu.post( x,y ) self.master.update() time.sleep( 0.4 ) return 0 # We won't get here, but just in case... def listChoicePopupAlternative(self, title, stringList, actionLabel ): """ OBSOLETE --- Delete this """ raise Exception, "No one uses this method! But if you see this, maybe not so..." """ optionList = [OptionDialog.BOOL_BUTTON_ENTRY,actionLabel] options = dict() optionOrder = list() for i in range(0,len(stringList)): options[i] = [False,optionList,stringList[i],''] optionOrder.append(i) i+=1 dialog = OptionDialog(self.master, title, options,optionOrder, grab = False, position = self.atom3i.cb.getLastClickCoordInRootCoords() ) if( dialog.isCanceled() ): return 0 options = dialog.getOptionsDatabase() i = 1 for option in optionOrder: if( options[option][0] ): return i i += 1 return 0 """
class ApexList(Listbox): def __init__(self, parent, apex=None, **kw): if apex is None: apex = [] kw.setdefault('selectmode', 'extended') Listbox.__init__(self, parent, **kw) self.set_apex(apex) self.bind("<Return>", lambda event: self.expand_selected()) self._init_menu() self._right_click = None def _init_menu(self): self._menu = Menu(self, tearoff=0) self._menu.add_command(label="Copy", command=self._copy_selected) self._menu.add_command(label="Copy LaTeX", command=self._copy_selected_latex) self._menu.add_command(label="Copy all", command=self._copy_all) self._menu.add_command(label="Copy all LaTeX", command=self._copy_all_latex) self._menu.add_command(label="Expand", command=self.expand_selected) # right button on Mac and other systems button = '2' if tools.IS_MAC else '3' self.bind("<Button-{}>".format(button), self._right_click) def _right_click(self, event): self._right_click = event.x, event.y clicked_index = self.nearest(event.y) if not self.selection_includes(clicked_index): self.selection_clear(0, 'end') self.selection_set(clicked_index) self._menu.post(event.x_root, event.y_root) def _copy_selected(self): pyperclip.setcb(", ".join(map(self.get, self.curselection()))) def _copy_selected_latex(self): def elem_latex(index): e = self._apex[index] if e.mode == StringViewFormatter.NORMAL: return e.str_normal() return e.str_latex() pyperclip.setcb(", ".join(map(elem_latex, self.curselection()))) def _copy_all(self): pyperclip.setcb(", ".join(map(str, self._apex))) def _copy_all_latex(self): def elem_latex(e): if e.mode == StringViewFormatter.NORMAL: return e.str_normal() return e.str_latex() pyperclip.setcb(", ".join(map(elem_latex, self._apex))) def curselection(self): """Return list of indices of currently selected items.""" return [int(index) for index in Listbox.curselection(self)] def _update_text(self, index): self.delete(index) self.insert(index, self._apex[index]) def reprint(self): """Updates list elements' text """ for i in xrange(len(self._apex)): self._update_text(i) def expand(self, indices=None): """Sets elements with specified indices to show verbose view """ prev_selection = self.curselection() for index in indices: self._apex[index].mode = StringViewFormatter.MIXED self._update_text(index) for index in prev_selection: self.selection_set(index) def expand_all(self): self.expand(range(len(self._apex))) def expand_selected(self): self.expand(self.curselection()) def reset(self): """Resets every element back to plain integer view """ for index in range(len(self._apex)): self._apex[index].mode = StringViewFormatter.NORMAL self._update_text(index) def set_apex(self, apex): """Sets apex shown in this list """ def transform_number(number): if type(number) in (SpectraElement, Integer): return StringViewFormatter(number) return StringViewFormatter(Integer(number)) self._apex = [transform_number(number) for number in apex] self._apex.sort(key=lambda e: e.object, reverse=True) self.delete(0, "END") self.insert(0, *self._apex)
class IntegerView(Label): """This is the frame for displaying Integer with ability to factorize it. """ def __init__(self, parent, integer=Integer(), **kw): #kw['state'] = 'disabled' kw.setdefault('anchor', 'nw') kw.setdefault('relief', 'sunken') kw.setdefault('width', 10) kw.setdefault('justify', 'left') self._var = StringVar() Label.__init__(self, parent, textvariable=self._var, **kw) self._integer_view = StringViewFormatter(integer) self._update_integer() self._factorization_enabled = False self.bind("<Configure>", self._update_width) self._init_menu() def _init_menu(self): self._menu = Menu(self, tearoff=0) self._menu.add_command(label="Copy", command=self._copy) self._menu.add_command(label="Copy LaTeX", command=self._copy_latex) # right button on Mac and other systems button = '2' if tools.IS_MAC else '3' self.bind("<Button-{}>".format(button), self._right_click) def _right_click(self, event): self._menu.post(event.x_root, event.y_root) def _copy(self): pyperclip.setcb(str(self._integer_view)) def _copy_latex(self): if self._factorization_enabled: cb = self._integer_view.str_latex() else: cb = self._integer_view.str_normal() pyperclip.setcb(cb) def _update_width(self, event): self['wraplength'] = self.winfo_width() - 10 def _update_integer(self): self._var.set(self._integer_view) @property def integer(self): return self._integer_view.object @integer.setter def integer(self, value): self._integer_view = StringViewFormatter(value) if self._factorization_enabled: self._integer_view.mode = StringViewFormatter.VERBOSE self._update_integer() def toggle_factorization(self, value): self._factorization_enabled = value if value: self._integer_view.mode = StringViewFormatter.VERBOSE else: self._integer_view.mode = StringViewFormatter.NORMAL self._update_integer()
def _handle_r_click(self, event): p_menu = Menu(self._parent) p_menu.add_command(label="Add Texture", command=lambda: self._add_new_texture()) p_menu.add_command(label="Delete Texture", command=lambda: self._remove_texture()) p_menu.add_command(label="Delete All", command=lambda: self.remove_all()) p_menu.post(event.x_root, event.y_root)
class ApexList(Listbox): def __init__(self, parent, apex=None, **kw): if apex is None: apex = [] kw.setdefault('selectmode', 'extended') Listbox.__init__(self, parent, **kw) self.set_apex(apex) self.bind("<Return>", lambda event: self.expand_selected()) self._init_menu() self._right_click = None def _init_menu(self): self._menu = Menu(self, tearoff=0) self._menu.add_command(label="Copy", command=self._copy_selected) self._menu.add_command(label="Copy LaTeX", command=self._copy_selected_latex) self._menu.add_command(label="Expand", command=self.expand_selected) # right button on Mac and other systems button = '2' if tools.IS_MAC else '3' self.bind("<Button-{}>".format(button), self._right_click) def _right_click(self, event): self._right_click = event.x, event.y clicked_index = self.nearest(event.y) if not self.selection_includes(clicked_index): self.selection_clear(0, 'end') self.selection_set(clicked_index) self._menu.post(event.x_root, event.y_root) def _copy_selected(self): pyperclip.setcb(", ".join(map(self.get, self.curselection()))) def _copy_selected_latex(self): def elem_latex(index): e = self._apex[index] if e.str_mode == 'normal': return e.str_normal() return e.str_latex() pyperclip.setcb(", ".join(map(elem_latex, self.curselection()))) def curselection(self): """Return list of indices of currently selected items.""" return [int(index) for index in Listbox.curselection(self)] def select_by_divisor(self, divisor=None): """Selects and entries divisible by 'divisor'.""" self.selection_clear(0, 'end') if divisor: for index, number in enumerate(self._apex): if number % divisor == 0: self.selection_set(index) def _update_text(self, index): self.delete(index) self.insert(index, self._apex[index]) def reprint(self): """Updates list elements' text """ for i in xrange(len(self._apex)): self._update_text(i) def expand(self, indices=None): """Sets elements with specified indices to show verbose view """ prev_selection = self.curselection() for index in indices: self._apex[index].str_mode = 'mixed' self._update_text(index) for index in prev_selection: self.selection_set(index) def expand_all(self): self.expand(range(len(self._apex))) def expand_selected(self): self.expand(self.curselection()) def reset(self): """Resets every element back to plain integer view """ for index in range(len(self._apex)): self._apex[index].str_mode = 'normal' self._update_text(index) def set_apex(self, apex): """Sets apex shown in this list """ def transform_number(number): if type(number) not in (SpectraElement, Integer): number = Integer(number) return MultiModeStringFormatter.mixin_to(number) self._apex = [transform_number(number) for number in apex] self._apex.sort(reverse=True) self.delete(0, "END") self.insert(0, *self._apex)
class ListFrame(LabelFrame): """ A Frame representing one of the search term lists (e.g. Hashtags, Excluded Users). Displays all the items in the list, and allows the user to add or remove items. Methods should not be called directly; instead they should be bound as event handlers. """ def __init__(self, name, add_handler, remove_handler, master=None): """ Creates a ListFrame with the given name as its title. add_handler and remove_handler are functions to be called when items are added or removed, and should relay the information back to the Searcher (or whatever object actually uses the list). """ LabelFrame.__init__(self, master) self['text'] = name self.add_handler = add_handler self.remove_handler = remove_handler self.list = Listbox(self) self.list.grid(row=0, columnspan=2) # Tkinter does not automatically close the right-click menu for us, # so we must close it when the user clicks away from the menu. self.list.bind("<Button-1>", lambda event: self.context_menu.unpost()) self.list.bind("<Button-3>", self.open_menu) self.context_menu = Menu(self, tearoff=0) self.context_menu.add_command(label="Remove", command=self.remove) self.input = Entry(self) self.input.bind("<Return>", lambda event: self.add()) self.input.grid(row=1, columnspan=2) self.add_button = Button(self) self.add_button['text'] = "Add" self.add_button['command'] = self.add self.add_button.grid(row=2, column=0, sticky=W+E) self.remove_button = Button(self) self.remove_button['text'] = "Remove" self.remove_button['command'] = self.remove self.remove_button.grid(row=2, column=1, sticky=W+E) def add(self): """ Add the item in the input line to the list. """ self.list.insert(END, self.input.get()) self.add_handler(self.input.get()) self.input.delete(0, END) def remove(self): """ Remove the active (highlighted) item from the list. """ deleted = self.list.get(ACTIVE) self.list.delete(ACTIVE) self.remove_handler(deleted) def open_menu(self, event): """ Opens a right-click menu for the selected item. Currently the menu only has an option for removing the item. """ index = self.list.index("@" + str(event.x) + "," + str(event.y)) if index < 0: return self.context_menu.post(event.x_root, event.y_root) self.list.activate(index) self.list.selection_clear(0, END) self.list.selection_set(ACTIVE)