class OptionWindow(Toplevel): def __call__(self, options=[], display=True): self.options = options self.listbox.delete(0, END) for key, value in options: self.listbox.insert(END, key) if display: self.display() def __init__(self): Toplevel.__init__(self, master=root) self.options = None self.title('Matches') self.listbox = Listbox(master=self) self.listbox.pack(expand=True, fill=BOTH, side=TOP) self.listbox.focus_set() self.listbox.activate(0) self.listbox.selection_set(0) self.listbox.config(width=50) self.listbox.bind('<Key-h>', lambda event: self.listbox.event_generate('<Left>')) self.listbox.bind('<Key-l>', lambda event: self.listbox.event_generate('<Right>')) self.listbox.bind('<Key-k>', lambda event: self.listbox.event_generate('<Up>')) self.listbox.bind('<Key-j>', lambda event: self.listbox.event_generate('<Down>')) self.listbox.bind('<Escape>', lambda event: self.close()) self.protocol("WM_DELETE_WINDOW", self.close) self.transient(root) self.withdraw() def display(self): self.grab_set() self.deiconify() self.listbox.focus_set() # self.wait_window(self) def close(self): # When calling destroy or withdraw without # self.deoiconify it doesnt give back # the focus to the parent window. self.deiconify() self.grab_release() self.withdraw()
class SelectMultiple(TwoCells): def __init__(self, parent, text, values, **kwargs): super().__init__(parent, **kwargs) self.label = Label(self.frame, text=text) self.label.grid(column=0, row=0, padx=5, pady=5, sticky=W) self.selected_options = StringVar(self.frame) self.selected_options.set(values) self.listbox = Listbox(self.frame, listvariable=self.selected_options, selectmode=MULTIPLE) self.listbox.grid(column=1, row=0, padx=5, pady=5, sticky=E) def disable(self): self.listbox.configure(state=DISABLED) return self def enable(self): self.listbox.configure(state=NORMAL) return self def set(self, values): if values: self.enable() else: self.disable() self.listbox.selection_clear(0, self.listbox.size()) for value in values: for index in range(self.listbox.size()): if value == self.listbox.get(index): self.listbox.selection_set(index) self.listbox.event_generate("<<ListboxSelect>>") def get(self): return [self.listbox.get(i) for i in self.listbox.curselection()]
class AlexListBox(Frame): def __init__(self, parent, items=[], width=20, height=10, selectioncommand=None): super().__init__(parent) self.selectioncommand = selectioncommand self.listbox = Listbox(self, width=width, height=height) self.listbox.grid(row=0, column=0) scrollbar_y = AlexScrollbar(self, command=self.listbox.yview) scrollbar_y.grid(row=0, column=1, sticky=N + S) self.listbox.configure(yscrollcommand=scrollbar_y.set) scrollbar_x = AlexScrollbar(self, command=self.listbox.xview, orient=HORIZONTAL) scrollbar_x.grid(row=1, column=0, sticky=E + W) self.listbox.configure(xscrollcommand=scrollbar_x.set) if self.selectioncommand is not None: self.listbox.bind('<<ListboxSelect>>', self._select_callback) self.set_items(items) def _select_callback(self, event): selection = self.get() # ignore unselect if selection != None: self.selectioncommand(selection) def set_items(self, items): self.listbox.delete(0, END) self.items = [] for item in items: self.items.append(item) self.listbox.insert(END, "%s" % item) def get_items(self): return self.items def get(self): selections = self.listbox.curselection() if len(selections) > 0: return self.items[selections[0]] else: return None def set(self, selection): self.listbox.selection_clear(0, len(self.items)) for i in range(0, len(self.items)): if self.items[i] == selection: self.listbox.selection_set(i)
class LabeledListBox(Frame): def __init__(self, parent, list_model, label_text): Frame.__init__(self, parent) self._list_model = list_model self._list_objects = [] self._selected_items = [] scrollbar = Scrollbar(self, orient=VERTICAL) Label(self, text=label_text).pack() self.listbox = Listbox(self, selectmode=EXTENDED, exportselection=0, yscrollcommand=scrollbar.set, borderwidth=0, highlightthickness=0) scrollbar.config(command=self.listbox.yview) scrollbar.pack(side=RIGHT, fill=Y) self.listbox.pack(side=LEFT, fill=BOTH, expand=1) self.listbox.bind('<<ListboxSelect>>', self._on_select) self._list_model.list_items_model.add_listener(self._list_items_changed) self._list_model.selected_items_model.add_listener(self._selected_items_changed) self._update_list_items() def _list_items_changed(self, values): self._update_list_items() def _selected_items_changed(self, values): self._update_selected_items() def _update_list_items(self): values, labels = self._list_model.list_items_model.get_list_values() if not values == self._list_objects: self._list_objects = [] self._selected_items = [] self.listbox.delete(0, END) for value, label in zip(values, labels): self._list_objects.append(value) self.listbox.insert(END, label) self._update_selected_items() def _update_selected_items(self): selected_items = self._list_model.selected_items_model.selected_items if not selected_items == self._selected_items: self._selected_items = selected_items for index, list_item in enumerate(self._list_objects): if list_item in selected_items: self.listbox.selection_set(index) def _on_select(self, evt): visible_selected_indices = self.listbox.curselection() for index, list_item in enumerate(self._list_objects): if index in visible_selected_indices: self._list_model.selected_items_model.select(list_item) else: self._list_model.selected_items_model.deselect(list_item)
def create_widgets(self): self.files_list = StringVar() self.refresh_images() self.active_image = Label(self) self.active_image.grid(row=0, column=1, sticky=tkinter.E + tkinter.W + tkinter.S + tkinter.N) files_listbox = Listbox(self, listvariable=self.files_list) files_listbox.grid(row=0, column=0, sticky=tkinter.E + tkinter.W + tkinter.N) files_listbox.bind("<<ListboxSelect>>", self.set_active_image) files_listbox.selection_set(0)
class MyListbox(Dialog): def __init__(self, master, soplst): self.sopListe = soplst super().__init__(master, title='SOP Liste') def body(self, master): self.lbox = Listbox(master, background='white', selectmode=SINGLE) self.lbox.pack(fill=BOTH, expand=5) for item in self.sopListe: self.lbox.insert(END, item) self.lbox.selection_set(0) return self.lbox # initial focus #return # no initial focus def apply(self): self.result = int( self.lbox.curselection()[0]) # Position des ausgewählten Elements
class TextSelect(Frame): def __init__(self, client, anchor, items, destroyAnchor=False): """ Args: client: [SelectionClient] The window that text is returned to. anchor: A window that the text selection popup is created relative to. items: [str], items to display in the listbox. destroyAnchor: [bool] if true, destroy the anchor after positioning the window. """ self.top = Toplevel() self.anchor = anchor self.top.overrideredirect(1) self.top.wm_geometry('+%s+%s' % (anchor.winfo_rootx() + anchor.winfo_x(), anchor.winfo_rooty() + anchor.winfo_y() ) ) super(TextSelect, self).__init__(self.top) self.entry = Entry(self) self.client = client self.items = items self.place(x = 0.5, y = 0.5, height = 100, width = 100) self.entry.bind('<Return>', self.close) self.entry.bind('<KeyPress>', self.filter) self.entry.bind('<Escape>', self.abort) self.entry.bind('<Up>', self.up) self.entry.bind('<Down>', self.down) self.entry.pack() # Create the list of items. self.list = Listbox(self) for item in self.items: self.list.insert('end', item) self.list.pack() self.grid() self.entry.focus() # Reposition the select button against the anchor. We defer this # until after idle so that the anchor has a chance to get rendered. def reposition(*args): self.top.wm_geometry('+%s+%s' % ( anchor.winfo_rootx(), anchor.winfo_rooty()) ) if destroyAnchor: anchor.destroy() self.after_idle(reposition) def close(self, event): sel = self.list.curselection() if sel: item = self.list.get(sel[0]) else: item = self.entry.get() # Note that the order of this appears to be significant: destroying # before selecting leaves the focus in a weird state. self.client.selected(item) self.top.destroy() return 'braek' def abort(self, event): self.top.destroy() self.client.aborted() return 'break' def up(self, event): sel = self.list.curselection() if not sel: self.list.selection_set(0) return 'break' sel = sel[0] print('sel is %s size is %s' % (sel, self.list.size())) if sel > 0: print('setting selection to %s' % sel) self.list.selection_clear(sel) self.list.selection_set(sel - 1) self.list.see(sel) return 'break' def down(self, event): sel = self.list.curselection() if not sel: self.list.selection_set(0) return 'break' sel = sel[0] print('sel is %s size is %s' % (sel, self.list.size())) if sel < self.list.size() - 1: print('setting selection to %s' % (sel + 1)) self.list.selection_clear(sel) self.list.selection_set(sel + 1) self.list.see(sel) return 'break' def filter(self, event): """Filter the listbox based on the contents of the entryfield.""" # first add the character to the entry. currentText = self.entry.get() print(event.keysym) if event.keysym == 'BackSpace': # Handle backspace specially. if currentText: currentText = currentText[:-1] self.entry.delete(0, 'end') self.entry.insert(0, currentText) else: return 'break' else: # Assume normal character. Insert it. self.entry.insert('insert', event.char) currentText += event.char self.list.delete(0, 'end') pattern = currentText.upper() for item in self.items: if pattern in item.upper(): self.list.insert('end', item) return 'break'
class Editor: # Info Defaults LOG_FILE = 'editor.log' devices = [] vidPK = [] vidPATH = [] vidNAME = [] uuid = [] uuidFK = [] def __init__(self, master, soundGenerator, rfidScanner): self.environment = Environment() self.soundProvider = SoundProvider(soundGenerator) self.configureScannerProvider(rfidScanner) self.load() frame = Frame(master) frame.pack() self.activeCardNumber = StringVar() self.usbSpin = StringVar() self.usbSpin.set(self.environment.Usb) Label(frame, text='RFID Card').grid(row=0, column=0) Label(frame, text='Video').grid(row=0, column=2) self.ee = Entry(frame, textvariable=self.activeCardNumber, state=DISABLED, disabledforeground='black') self.ee.grid(row=1, column=0) self.r = Button(frame, text='Read Card', command=self.startCardProcess) self.r.grid(row=1, column=1) self.box = Listbox(frame) for entry in self.vidNAME: self.box.insert(END, entry) self.box.bind("<<ListboxSelect>>", self.newselection) self.box.grid(row=1, rowspan=5, column=2, columnspan=2) self.scroll = Scrollbar(self.box, orient=VERTICAL) self.box.config(yscrollcommand=self.scroll.set) self.scroll.config(command=self.box.yview) Button(frame, text='Assign Kill Code', command=self.createKiller).grid(row=2, column=0) Label(frame, text='Source USB').grid(row=4, column=0) self.spin = Spinbox(frame, values=self.devices) self.spin.delete(0, END) self.spin.insert(0, self.environment.Usb) self.spin.grid(row=5, column=0) self.status = Button(frame, text='Update Device Repository', command=self.updateDevice, disabledforeground='blue') self.status.grid(row=6, column=0) Button(frame, text='Save', command=self.save).grid(row=6, column=2) Button(frame, text='Quit', command=self.closeWithSavePrompt).grid( row=6, column=3) def configureScannerProvider(self, rfidScanner): provider = RFIDScannerProvider(rfidScanner) self.RFIDScannerProvider = provider.PN532( int(self.environment.CHIP_SELECT_PIN), int(self.environment.MASTER_OUTPUT_SLAVE_INPUT_PIN), int(self.environment.MASTER_INPUT_SLAVE_OUTPUT_PIN), int(self.environment.SERIAL_CLOCK_PIN)) def closeWithSavePrompt(self): ans = messagebox.askquestion( 'Save And Quit', 'Would you like to save your changes?') if ans == 'yes': self.save() sys.exit(0) def startCardProcess(self): # Disable button to prevent event stacking self.r.config(state=DISABLED) self.processCard() self.r.config(state=NORMAL) def processCard(self): # Scans RFID cards and sets them to text box try: self.processCardUnchecked() except Exception as e: self.displayScanError(e) def processCardUnchecked(self): cardScan = CardScanWrapper(self.soundS, self.RFIDScannerProvider) cardScan.runScan() self.processResult(cardScan.getFormattedResult()) def processResult(self, scanResult): if scanResult == None: return self.activeCardNumber.set(scanResult) # Populate text box self.deselectActiveListboxItems() self.linkCardWithListbox(scanResult) def deselectActiveListboxItems(self): # De-select any active items in listbox self.box.selection_clear(0, END) def linkCardWithListbox(self, scanResult): index = self.verifyCard(scanResult) if str(self.uuidFK[index]) == self.environment.KillCommand: messagebox.showinfo( 'Kill Card', 'This card is currently assigned to kill the application.') return self.highlightItemInListbox(index) def highlightItemInListbox(self, index): try: i = self.vidPK.index(self.uuidFK[index]) except: messagebox.showinfo('Card Unassigned', 'Card is not currently assigned to a video') else: self.box.see(i) self.box.selection_clear(0, END) self.box.selection_set(i) self.box.activate(i) def verifyCard(self, uidt): try: uuidIndex = self.uuid.index(uidt) except: uuidIndex = self.addNewCard(uidt) return uuidIndex def addNewCard(self, uidt): self.uuid.append(uidt) self.uuidFK.append(-1) newIndex = len(self.uuid) - 1 return newIndex def displayScanError(self, e): messagebox.showerror('Error Occurred', 'Error: ' + str(e)) logging.error('Scan Failed: ' + str(e)) def save(self): toSaveList = self.makePairedList( self.vidPK, self.vidNAME, self.vidPATH) self.safeSaveToFile(self.environment.VideoList, toSaveList) toSaveList = self.makePairedList(self.uuid, self.uuidFK) self.safeSaveToFile(self.environment.UuidTable, toSaveList) def makePairedList(self, *itemLists): stop = len(itemLists) subList = [] listAll = [] for listIndex in range(len(itemLists[0])): del subList[:] for indice in range(stop): subList.append(itemLists[indice][listIndex]) listAll.append(list(subList)) return listAll def safeSaveToFile(self, fileName, pairList): try: self.writePairedListToTempFile(fileName, pairList) except Exception as e: logging.error('Failed to create video list: ' + str(e)) else: self.replaceOriginalFileWithItsTemp(fileName) def replaceOriginalFileWithItsTemp(self, fileName): try: if os.path.isfile(fileName): os.remove(fileName) os.rename(fileName + '.temp', fileName) except Exception as e: logging.error('Failed to replace old video list: ' + str(e)) def writePairedListToTempFile(self, fileName, pairedList): f = open(fileName + '.temp', 'w') self.writePairedListGivenFile(f, pairedList) f.close() def writePairedListGivenFile(self, f, pairedList): i = 0 while(i < len(pairedList) - 1): self.writeSingleLineOfPairedListToOpenFile(f, pairedList, i) f.write('\n') i = i+1 self.writeSingleLineOfPairedListToOpenFile(f, pairedList, i) def writeSingleLineOfPairedListToOpenFile(self, f, pairedList, itemIndex): fLine = "" for item in range(len(pairedList[itemIndex])): fLine = fLine + str(pairedList[itemIndex][item]) + ',' f.write(fLine[:-1]) def updateDevice(self): scan = self.safeScan() if scan != None: self.safeProcessScan(scan) self.status.config(text='Update Device Repository', state=NORMAL) self.status.update_idletasks() def safeScan(self): scan = None try: scan = self.runScannerWithNotification() except Exception as e: self.showScanErrorMessage(e) return scan def runScannerWithNotification(self): self.status.config(text='Scanning...', state=DISABLED) self.status.update_idletasks() scan = ScriptedFileSearch(subprocess) scan.scan("scanner.sh") return scan def showScanErrorMessage(self, e): messagebox.showerror('Scan Failed', 'Scan error: ' + str(e)) logging.error(str(e)) def safeProcessScan(self, scan): try: self.processScan(scan) except Exception as e: self.showErrorMessage(e) def showErrorMessage(self, e): messagebox.showerror('Error', 'Error: ' + str(e)) logging.error(str(e)) def refreshListBox(self): self.box.delete(0, END) for entry in self.vidNAME: self.box.insert(END, entry) def processScan(self, scan): # Check if a scan turned up any results if self.scanIsEmpty(scan): self.showAbortScanMessage() return self.verifyUSB() self.processScanFiles(scan) self.refreshListBox() def showAbortScanMessage(self): messagebox.showwarning( 'No Files Found', 'A scan failed to find any files.') logging.warning('Empty Scan occurred when attempting a merge') def scanIsEmpty(self, scan): return len(scan.NAME) == 0 def verifyUSB(self): if self.environment.Usb != self.spin.get(): self.Usb = self.spin.get() self.environment.update() def processScanFiles(self, scan): i = 0 j = 0 newPK = [] newName = [] newPath = [] self.status.config(text='Reading Files...') self.status.update_idletasks() # Iterate through list while i < len(scan.NAME): # Verifiy File try: if scan.PATH[i].find(self.environment.Usb) >= 0: # File resides on repository - update FK try: # Locate matching entry fkk = self.vidNAME.index(scan.NAME[i]) except Exception as e: # No matching entry logging.info('New file found in repository: ' + str(e)) pass else: # Update FK on uuid table for uu in self.uuidFK: if uu == self.vidPK[fkk]: uu = scan.PK[i] # Store entry in new Tables newPK.append(scan.PK[i]) newName.append(scan.NAME[i]) newPath.append(scan.PATH[i]) else: # Video resides on updating device - check if file already copied found = False while j < len(scan.NAME): if str(scan.NAME[i]) == str(scan.NAME[j]) and scan.PATH[j].find(self.environment.Usb) >= 0: found = True break j = j + 1 if not found: # Copy file and append try: # Get device name device = scan.PATH[i].replace('/media/pi/', '') device = device[0:device.find('/')] # Copy self.status.config( text='Copying ' + scan.NAME[i] + '...') self.status.update_idletasks() shutil.copyfile( scan.PATH[i], scan.PATH[i].replace(device, self.environment.Usb)) except Exception as e: logging.error('Failed to copy' + scan.NAME[i] + ': ' + str(e)) else: # Add to new array newPK.append(scan.PK[i]) newName.append(scan.NAME[i]) newPath.append( scan.PATH[i].replace(device, self.environment.Usb)) except Exception as e: logging.error(str(e)) i = i+1 del self.vidNAME[:] del self.vidPATH[:] del self.vidPK[:] self.vidNAME = newName self.vidPATH = newPath self.vidPK = newPK def newselection(self, event): # Fires when a new item is selected in the listbox selection = event.widget.curselection() try: txt = self.ee.get() if txt == '': return i = self.uuid.index(txt) self.uuidFK[i] = self.vidPK[selection[0]] except Exception as e: messagebox.showerror('Error During Set', 'Error: ' + str(e)) logging.error(str(e)) def createKiller(self): try: self.assignCurrentCardAsKiller() self.box.selection_clear(0, END) except Exception as e: self.handleCardNotScannedError(e) def assignCurrentCardAsKiller(self): i = self.uuid.index(self.ee.get()) self.uuidFK[i] = int(self.environment.KillCommand) def handleCardNotScannedError(self, e): messagebox.showinfo( 'Card Not Scanned', 'Please scan a card to assign it a [Kill Application] code.' + str(e)) logging.error(str(e)) def load(self): # Generate Log logging.basicConfig(filename=self.LOG_FILE, level=logging.INFO) # Load Sound file self.soundProvider.init() self.soundProvider.mixer.pre_init(44100, -16, 12, 512) # pygame.init() IS this only for linux distribution? self.soundS = self.soundProvider.mixer.Sound(self.environment.SCAN_SOUND) self.soundS.set_volume(1) # Create an instance of the PN532 class. self.RFIDScannerProvider.begin()) # Configure PN532 to communicate with MiFare cards. self.RFIDScannerProvider.SAM_configuration() self.loadFiles() self.locateDevices() self.loadDevice() def loadFiles(self): self.readCSV(self.environment.VideoList, (int, self.vidPK), (str, self.vidNAME), (str, self.vidPATH)) self.readCSV(self.environment.UuidTable, (str, self.uuid), (int, self.uuidFK))
class ConfigMgr(Dialog): def __init__(self, x, y, board): self.root = Toplevel() self.root.title("Config Manager") self.board = board f1 = Frame(self.root) f2 = Frame(self.root) f3 = Frame(f2) f1.pack(side=LEFT) f2.pack(side=LEFT) f3.pack(side=TOP) self.listbox = Listbox(f1) self.files = [] self.loadFileList() self.listbox.pack() l1 = Label(f3, text="new File name:") l1.pack(side=LEFT) self.e = Entry(f3) self.e.delete(0, END) self.e.insert(0, "FILENAME") self.e.pack(side=LEFT) buttons = [] buttons.append( Button(f2, text="Save to selected", command=self.saveConf)) buttons.append( Button(f2, text="Save to new File", command=self.newConf)) buttons.append( Button(f2, text="Load selected File", command=self.loadConf)) buttons.append( Button(f2, text="Delete selected File", command=self.delConf)) buttons.append(Button(f2, text="Continue", command=self.root.destroy)) for b in buttons: b.pack(side=TOP) self.align(x, y) self.root.mainloop() def loadFileList(self): if (not os.path.exists("./CONF")): os.mkdir("./CONF") self.files = os.listdir("./CONF/") self.listbox.delete(0, END) for item in self.files: self.listbox.insert(END, item) self.listbox.selection_set(0) self.listbox.update() def saveConf(self): f = open(self.listbox.selection_get(), "wb") data = BoardConfig(copy(self.board)) data_b = pickle.dumps(data) f.write(data_b) f.close() self.loadConf() def newConf(self): f = open("./CONF/" + self.e.get(), "wb") data = BoardConfig(copy(self.board)) data_b = pickle.dumps(data) f.write(data_b) f.close() self.loadFileList() print(self.files) f = open("./CONF/" + self.e.get(), "rb") data_b = f.read() data = pickle.loads(data_b) data.load(self.board) f.close() self.redraw(0) def loadConf(self): f = open("./CONF/" + self.listbox.selection_get(), "rb") data_b = f.read() data = pickle.loads(data_b) data.load(self.board) f.close() self.redraw(0) def delConf(self): MsgBox = messagebox.askquestion( 'WARNING', 'Are you sure you want to delete this file?', icon='warning') if (MsgBox == "yes"): os.remove("./CONF/" + self.listbox.selection_get()) self.loadFileList() self.root.lift() def redraw(self, p): self.board.setHighlight(p) self.board.drawBoard(320, 320) self.board.drawPieces()
class ListboxVidget(Vidget, Eventor): """ ListboxVidget contains a Listbox widget. It adds the following abilities: - Store items of any type, unlike Listbox widget that only stores texts. - Remember selected item even if the listbox widget lost focus. - Notify pre-change and post-change events. """ # Error raised when trying to change the listbox while a change is going on class CircularCallError(ValueError): pass # Error raised when trying to change the listbox while it is disabled class DisabledError(ValueError): pass # Event notified when the listbox's items are to be changed ITEMS_CHANGE_SOON = 'ITEMS_CHANGE_SOON' # Event notified when the listbox's items are changed ITEMS_CHANGE_DONE = 'ITEMS_CHANGE_DONE' # Event notified when the listbox's active item is to be changed ITEMCUR_CHANGE_SOON = 'ITEMCUR_CHANGE_SOON' # Event notified when the listbox's active item is changed ITEMCUR_CHANGE_DONE = 'ITEMCUR_CHANGE_DONE' # Events list EVENTS = ( ITEMS_CHANGE_SOON, ITEMS_CHANGE_DONE, ITEMCUR_CHANGE_SOON, ITEMCUR_CHANGE_DONE, ) def __init__( self, items=None, item_to_text=None, normal_bg='', normal_fg='', active_bg='sky blue', active_fg='white', selected_bg='steel blue', selected_fg='white', master=None, ): """ Initialize object. @param items: Items list. @param item_to_text: Item-to-text function. Default is `str`. @param normal_bg: Unselected item background color. @param normal_fg: Unselected item foreground color. @param active_bg: Active item background color. `Active` means the item is selected (in general meaning) but the listbox has no focus. @param active_fg: Active item foreground color. `Active` means the item is selected (in general meaning) but the listbox has no focus. @param selected_bg: Selected item background color. `Selected` means the item is selected (in general meaning) and the listbox has focus. @param selected_fg: Selected item foreground color. `Selected` means the item is selected (in general meaning) and the listbox has focus. @param master: Master widget. @return: None. """ # Initialize Vidget. # Create main frame widget. Vidget.__init__( self, master=master, ) # Initialize Eventor Eventor.__init__(self) # If items list is given if items is not None: # If items list is not list if not isinstance(items, list): # Raise error raise TypeError(items) # If items list is list. # If items list is not given, or items list is given and is list # Items list self._items = items if items is not None else [] # Item-to-text function. Default is `str`. self._item_to_text = item_to_text if item_to_text is not None else str # Unselected item background color self._normal_fg = normal_fg # Unselected item foreground color self._normal_bg = normal_bg # Active item background color self._active_fg = active_fg # Active item foreground color self._active_bg = active_bg # Selected item background color self._selected_fg = selected_fg # Selected item foreground color self._selected_bg = selected_bg # Whether the listbox is changing self._is_changing = False # Active index. `-1` means void, i.e. no item is active. self._indexcur = -1 # Whether active index is being reset to same value self._is_resetting = False # Create listbox widget self._listbox = Listbox( master=self.widget(), relief='groove', activestyle='none', highlightthickness=0, # Active index cache only supports single-selection mode for now. # See 2N6OR. selectmode='single', ) # Set the listbox widget as config target self.config_target_set(self._listbox) # Create x-axis scrollbar self._scrollbar_xview = _HiddenScrollbar( self.widget(), orient=HORIZONTAL, ) # Create y-axis scrollbar self._scrollbar_yview = _HiddenScrollbar( self.widget(), orient=VERTICAL, ) # Mount scrollbars self._listbox.config(xscrollcommand=self._scrollbar_xview.set) self._listbox.config(yscrollcommand=self._scrollbar_yview.set) self._scrollbar_xview.config(command=self._listbox.xview) self._scrollbar_yview.config(command=self._listbox.yview) # Bind single-click event handler self._listbox.bind('<Button-1>', self._on_single_click) # Bind double-click event handler self._listbox.bind('<Double-Button-1>', self._on_double_click) # Update listbox widget self._listbox_widget_update(keep_active=False) # Update widget self._widget_update() def _widget_update(self): """ Update widget. @return: None. """ # Row 0 for listbox and y-axis scrollbar self.widget().rowconfigure(0, weight=1) # Row 1 for x-axis scrollbar self.widget().rowconfigure(1, weight=0) # Column 0 for listbox and x-axis scrollbar self.widget().columnconfigure(0, weight=1) # Column 1 for y-axis scrollbar self.widget().columnconfigure(1, weight=0) # Lay out listbox self._listbox.grid(row=0, column=0, sticky='NSEW') # Lay out x-axis scrollbar self._scrollbar_xview.grid(row=1, column=0, sticky='EW') # Lay out y-axis scrollbar self._scrollbar_yview.grid(row=0, column=1, sticky='NS') def is_enabled(self): """ Test whether the listbox is enabled. @return: Boolean. """ # Return whether the listbox is enabled return self._listbox.config('state')[4] != DISABLED def is_changing(self): """ Test whether the listbox is changing. @return: Boolean. """ # Return whether the listbox is changing return self._is_changing def is_resetting(self): """ Test whether the listbox is setting active index to the same value. @return: Boolean. """ # Return whether the listbox is setting active index to the same value return self._is_resetting def size(self): """ Get number of items. @return: Number of items. """ # Return number of items return len(self._items) def items(self): """ Get items list. Notice do not change the list outside. @return: Items list. """ # Return items list return self._items def items_set( self, items, notify=True, keep_active=False, ): """ Set items list. Notice do not change the list outside. @param items: Items list. @param notify: Whether notify pre-change and post-change events. @param keep_active: Whether keep or clear active index. @return: None. """ # If the items is not list if not isinstance(items, list): # Raise error raise TypeError(items) # If the items is list. # If the listbox is disabled if not self.is_enabled(): # Raise error raise ListboxVidget.DisabledError() # If the listbox is not disabled. # If the listbox is changing if self._is_changing: # Raise error raise ListboxVidget.CircularCallError() # If the listbox is not changing. # Set changing flag on self._is_changing = True # If notify events if notify: # Notify pre-change event self.handler_notify(self.ITEMS_CHANGE_SOON) # Store the new items self._items = items # Update listbox widget self._listbox_widget_update( keep_active=keep_active ) # If notify events if notify: # Notify post-change event self.handler_notify(self.ITEMS_CHANGE_DONE) # Set changing flag off self._is_changing = False def index_is_valid(self, index): """ Test whether given index is valid. Notice -1 is not valid. @param index: Index to test. @return: Boolean. """ # Test whether given index is valid return 0 <= index and index < self.size() def index_is_valid_or_void(self, index): """ Test whether given index is valid or is -1. @param index: Index to test. @return: Boolean. """ # Test whether given index is valid or is -1 return index == -1 or self.index_is_valid(index) def index_first(self): """ Get the first item's index. @return: First item's index, or -1 if the listbox is empty. """ # Return the first item's index return 0 if self.size() > 0 else -1 def index_last(self): """ Get the last item's index. @return: Last item's index, or -1 if the listbox is empty. """ # Return the last item's index return self.size() - 1 def indexcur(self, internal=False, raise_error=False): """ Get the active index. @param internal: See 2N6OR. @return: The active index. If no active active, either return -1, or raise IndexError if `raise_error` is True. """ # Get active indexes indexcurs = self._indexcurs(internal=internal) # If have active indexes if indexcurs: # Return the first active index return indexcurs[0] # If no active indexes else: # If raise error if raise_error: # Raise error raise IndexError(-1) # If not raise error else: # Return -1 return -1 def _indexcurs(self, internal=False): """ Get active indexes list. 2N6OR @param internal: Whether use listbox widget's selected indexes, instead of cached active index. Notice listbox widget has no selected indexes if it has no focus. Notice using cached active index only supports single-selection mode, which means the result list has at most one index. @return: Active indexes list. """ # If use listbox widget's selected indexes if internal: # Return listbox widget's selected indexes list return [int(x) for x in self._listbox.curselection()] # If not use listbox widget's selected indexes else: # If cached active index is valid if self.index_is_valid(self._indexcur): # Return a list with the cached active index return [self._indexcur] # If cached active index is not valid else: # Return empty list return [] def indexcur_set( self, index, focus=False, notify=True, notify_arg=None, ): """ Set active index. @param index: The index to set. @param focus: Whether set focus on the listbox widget. @param notify: Whether notify pre-change and post-change events. @param notify_arg: Event argument. @return: None. """ # If the index is not valid or -1 if not self.index_is_valid_or_void(index): # Raise error raise IndexError(index) # If the index is valid or is -1. # If the listbox is not enabled if not self.is_enabled(): # Raise error raise ListboxVidget.DisabledError() # If the listbox is enabled. # If the listbox is changing if self._is_changing: # Raise error raise ListboxVidget.CircularCallError() # If the listbox is not changing. # Set changing flag on self._is_changing = True # Get old active index old_indexcur = self._indexcur # Set resetting flag on if new and old indexes are equal self._is_resetting = (index == old_indexcur) # If notify events if notify: # Notify pre-change event self.handler_notify(self.ITEMCUR_CHANGE_SOON, notify_arg) # If old active index is valid if self.index_is_valid(old_indexcur): # Set old active item's background color to normal color self._listbox.itemconfig(old_indexcur, background=self._normal_bg) # Set old active item's foreground color to normal color self._listbox.itemconfig(old_indexcur, foreground=self._normal_fg) # Cache new active index self._indexcur = index # Clear listbox widget's selection self._listbox.selection_clear(0, END) # Set listbox widget's selection self._listbox.selection_set(index) # Set listbox widget's activated index self._listbox.activate(index) # If new active index is valid if index != -1: # Set new active item's background color to active color self._listbox.itemconfig(index, background=self._active_bg) # Set new active item's foreground color to active color self._listbox.itemconfig(index, foreground=self._active_fg) # If set focus if focus: # Set focus on the listbox widget self._listbox.focus_set() # If new active index is valid if index != -1: # Make the active item visible self._listbox.see(index) # If notify events if notify: # Notify post-change event self.handler_notify(self.ITEMCUR_CHANGE_DONE, notify_arg) # Set resetting flag off self._is_resetting = False # Set changing flag off self._is_changing = False def indexcur_set_by_event( self, event, focus=False, notify=True, notify_arg=None, ): """ Set active index using a Tkinter event object that contains coordinates of the active item. @param event: Tkinter event object. @param focus: Whether set focus on the listbox widget. @param notify: Whether notify pre-change and post-change events. @param notify_arg: Event argument. @return: None. """ # Get the event's y co-ordinate's nearest listbox item index index = self._listbox.nearest(event.y) # If the index is not valid if not self.index_is_valid_or_void(index): # Ignore the event return # If the index is valid else: # Set the index as active index self.indexcur_set( index=index, focus=focus, notify=notify, notify_arg=notify_arg, ) def item(self, index): """ Get item at given index. @return: Item at given index, or IndexError if the index is not valid. """ return self.items()[index] def itemcur(self, internal=False, raise_error=False): """ Get the active item. @param internal: See 2N6OR. @param raise_error: Whether raise error if no active item. @return: The active item. If no active item, if `raise_error` is True, raise IndexError, otherwise return None. """ # Get active index. # May raise IndexError if `raise_error` is True. indexcur = self.indexcur( internal=internal, raise_error=raise_error, ) # If no active index if indexcur == -1: # Return None return None # If have active index else: # Return the active item return self.items()[indexcur] def item_insert( self, item, index=None, notify=True, keep_active=True, ): """ Insert item at given index. @param item: Item to insert. @param index: Index to insert. `None` means active index, and if no active index, insert at the end. @param notify: Whether notify pre-change and post-change events. @param keep_active: Whether keep or clear active index. @return: None. """ # If notify events if notify: # Notify pre-change events self.handler_notify(self.ITEMCUR_CHANGE_SOON) self.handler_notify(self.ITEMS_CHANGE_SOON) # Get old active index active_index = self.indexcur() # If the index is None, # it means use active index. if index is None: # Use active index. # `-1` works and means appending. index = active_index # Insert the item to the items list self._items.insert(index, item) # If old active index is valid if active_index != -1: # If old active index is GE the inserted index if active_index >= index: # Shift active index by one active_index += 1 # If old active index is not GE the inserted index, use it as-is. # Set new active index self.indexcur_set(index=active_index, notify=False) # Update listbox widget self._listbox_widget_update( keep_active=keep_active ) # If notify events if notify: # Notify post-change events self.handler_notify(self.ITEMS_CHANGE_DONE) self.handler_notify(self.ITEMCUR_CHANGE_DONE) def item_remove( self, index, notify=True, keep_active=True, ): """ Remove item at given index. @param index: Index to remove. @param notify: Whether notify pre-change and post-change events. @param keep_active: Whether keep or clear active index. @return: None. """ # If the index is not valid if not self.index_is_valid(index): # Raise error raise ValueError(index) # If the index is valid. # If notify events if notify: # Notify pre-change events self.handler_notify(self.ITEMCUR_CHANGE_SOON) self.handler_notify(self.ITEMS_CHANGE_SOON) # Get old active index active_index = self.indexcur() # Remove item at the index del self._items[index] # If old active index is valid if active_index != -1: # Get the last index index_last = self.index_last() # If old active index is GT the last index if active_index > index_last: # Use the last index as new active index active_index = index_last # If old active index is not GT the last index, use it as-is. # Set new active index self.indexcur_set(index=active_index, notify=False) # Update listbox widget self._listbox_widget_update( keep_active=keep_active ) # If notify events if notify: # Notify post-change events self.handler_notify(self.ITEMS_CHANGE_DONE) self.handler_notify(self.ITEMCUR_CHANGE_DONE) def handler_add( self, event, handler, need_arg=False, ): """ Add event handler for an event. If the event is ListboxVidget event, add the event handler to Eventor. If the event is not ListboxVidget event, add the event handler to listbox widget. Notice this method overrides `Eventor.handler_add` in order to add non-ListboxVidget event handler to listbox widget. @param event: Event name. @param handler: Event handler. @param need_arg: Whether the event handler needs event argument. @return: None. """ # If the event is ListboxVidget event if event in self.EVENTS: # Add the event handler to Eventor return Eventor.handler_add( self, event=event, handler=handler, need_arg=need_arg, ) # If the event is not ListboxVidget event, # it is assumed to be Tkinter widget event. else: # Add the event handler to listbox widget return self.bind( event=event, handler=handler, ) def bind( self, event, handler, ): """ Add event handler to listbox widget. ListboxVidget internally uses `<Button-1>` and `<Double-Button-1>` to capture active index changes. So if the given event is `<Button-1>` or `<Double-Button-1>`, the given handler will be wrapped. @param event: Event name. @param handler: Event handler. @return: None. """ # If the event is not `<Button-1>` or `<Double-Button-1>` if event not in ['<Button-1>', '<Double-Button-1>']: # Add the event handler to listbox widget self._listbox.bind(event, handler) # If the event is `<Button-1>` or `<Double-Button-1>` else: # Create event handler wrapper def handler_wrapper(e): """ Event handler wrapper that sets new active index and then calls the wrapped event handler. Setting new active index is needed because when this handler is called by Tkinter, the active index of the listbox is still old. @param e: Tkinter event object. @return: None. """ # Set new active index self.indexcur_set_by_event(e, notify=True) # Call the wrapped event handler handler(e) # Add the event handler wrapper to the listbox widget self._listbox.bind(event, handler_wrapper) def _on_single_click(self, event): """ `<Button-1>` event handler that updates active index. @param event: Tkinter event object. @return: None. """ # Updates active index self.indexcur_set_by_event(event, notify=True) def _on_double_click(self, event): """ `<Double-Button-1>` event handler that updates active index. @param event: Tkinter event object. @return: None. """ # Updates active index self.indexcur_set_by_event(event, notify=True) def _listbox_widget_update( self, keep_active, ): """ Update listbox widget's items and selection. @param keep_active: Whether keep or clear active index. @return: None. """ # Remove old items from listbox widget self._listbox.delete(0, END) # Insert new items into listbox widget. # For each ListboxVidget items. for index, item in enumerate(self.items()): # Get item text item_text = self._item_to_text(item) # Insert the item text into listbox widget self._listbox.insert(index, item_text) # Set the item's normal background color self._listbox.itemconfig(index, background=self._normal_bg) # Set the item's normal foreground color self._listbox.itemconfig(index, foreground=self._normal_fg) # Set the item's selected background color self._listbox.itemconfig(index, selectbackground=self._selected_bg) # Set the item's selected foreground color self._listbox.itemconfig(index, selectforeground=self._selected_fg) # If keep active index if keep_active: # Use old active index indexcur = self._indexcur # If not keep active index else: # Set active index to -1 indexcur = self._indexcur = -1 # Clear old selection self._listbox.selection_clear(0, END) # Set new selection. # `-1` works. self._listbox.selection_set(indexcur) # Set new active index. # `-1` works. self._listbox.activate(indexcur) # If new active index is valid if indexcur != -1: # Set active background color self._listbox.itemconfig(indexcur, background=self._active_bg) # Set active foreground color self._listbox.itemconfig(indexcur, foreground=self._active_fg) # Make the active item visible self._listbox.see(indexcur)
class SerialConfigFrame(Frame): # pylint: disable=too-many-ancestors, too-many-instance-attributes """ Serial port configuration frame class. """ def __init__(self, container, *args, **kwargs): """ Constructor. :param tkinter.Frame container: reference to container frame :param args: optional args to pass to Frame parent class :param kwargs: optional kwargs for value ranges, or to pass to Frame parent class """ self._bpsrate_rng = kwargs.pop("bpsrates", BPSRATE_RNG) self._databits_rng = kwargs.pop("databits", DATABITS_RNG) self._stopbits_rng = kwargs.pop("stopbits", STOPBITS_RNG) self._parity_rng = kwargs.pop("parities", PARITY_RNG) self._timeout_rng = kwargs.pop("timeouts", TIMEOUT_RNG) self._preselect = kwargs.pop("preselect", ()) self._readonlybg = kwargs.pop("readonlybackground", BGCOL) Frame.__init__(self, container, *args, **kwargs) self._show_advanced = False self._status = DISCONNECTED self._ports = () self._port = StringVar() self._port_desc = StringVar() self._bpsrate = IntVar() self._databits = IntVar() self._stopbits = DoubleVar() self._parity = StringVar() self._rtscts = IntVar() self._xonxoff = IntVar() self._timeout = StringVar() self._img_refresh = ImageTk.PhotoImage(Image.open(ICON_REFRESH)) self._img_expand = ImageTk.PhotoImage(Image.open(ICON_EXPAND)) self._img_contract = ImageTk.PhotoImage(Image.open(ICON_CONTRACT)) self._body() self._do_layout() self._get_ports() self._attach_events() self.reset() def _body(self): """ Set up widgets. """ self._frm_basic = Frame(self) self._lbl_port = Label(self._frm_basic, text="Port") self._lbx_port = Listbox( self._frm_basic, border=2, relief="sunken", bg=self._readonlybg, width=32, height=5, justify=LEFT, exportselection=False, ) self._scr_portv = Scrollbar(self._frm_basic, orient=VERTICAL) self._scr_porth = Scrollbar(self._frm_basic, orient=HORIZONTAL) self._lbx_port.config(yscrollcommand=self._scr_portv.set) self._lbx_port.config(xscrollcommand=self._scr_porth.set) self._scr_portv.config(command=self._lbx_port.yview) self._scr_porth.config(command=self._lbx_port.xview) self._lbl_bpsrate = Label(self._frm_basic, text="Rate bps") self._spn_bpsrate = Spinbox( self._frm_basic, values=self._bpsrate_rng, width=8, state=READONLY, readonlybackground=self._readonlybg, wrap=True, textvariable=self._bpsrate, ) self._btn_refresh = Button( self._frm_basic, command=self._on_refresh, image=self._img_refresh, width=28, height=22, ) self._btn_toggle = Button( self._frm_basic, command=self._on_toggle_advanced, image=self._img_expand, width=28, height=22, ) self._frm_advanced = Frame(self) self._lbl_databits = Label(self._frm_advanced, text="Data Bits") self._spn_databits = Spinbox( self._frm_advanced, values=self._databits_rng, width=3, state=READONLY, readonlybackground=self._readonlybg, wrap=True, textvariable=self._databits, ) self._lbl_stopbits = Label(self._frm_advanced, text="Stop Bits") self._spn_stopbits = Spinbox( self._frm_advanced, values=self._stopbits_rng, width=3, state=READONLY, readonlybackground=self._readonlybg, wrap=True, textvariable=self._stopbits, ) self._lbl_parity = Label(self._frm_advanced, text="Parity") self._spn_parity = Spinbox( self._frm_advanced, values=self._parity_rng, width=6, state=READONLY, readonlybackground=self._readonlybg, wrap=True, textvariable=self._parity, ) self._chk_rts = Checkbutton(self._frm_advanced, text="RTS/CTS", variable=self._rtscts) self._chk_xon = Checkbutton(self._frm_advanced, text="Xon/Xoff", variable=self._xonxoff) self._lbl_timeout = Label(self._frm_advanced, text="Timeout (s)") self._spn_timeout = Spinbox( self._frm_advanced, values=self._timeout_rng, width=4, state=READONLY, readonlybackground=self._readonlybg, wrap=True, textvariable=self._timeout, ) def _do_layout(self): """ Layout widgets. """ self._frm_basic.grid(column=0, row=0, columnspan=4, sticky=(W, E)) self._lbl_port.grid(column=0, row=0, sticky=(W)) self._lbx_port.grid(column=1, row=0, columnspan=2, sticky=(W, E), padx=3, pady=3) self._scr_portv.grid(column=3, row=0, sticky=(N, S)) self._scr_porth.grid(column=1, row=1, columnspan=2, sticky=(E, W)) self._lbl_bpsrate.grid(column=0, row=2, sticky=(W)) self._spn_bpsrate.grid(column=1, row=2, sticky=(W), padx=3, pady=3) self._btn_refresh.grid(column=2, row=2, sticky=(E)) self._btn_toggle.grid(column=3, row=2, sticky=(E)) self._frm_advanced.grid_forget() self._lbl_databits.grid(column=0, row=0, sticky=(W)) self._spn_databits.grid(column=1, row=0, sticky=(W), padx=3, pady=3) self._lbl_stopbits.grid(column=2, row=0, sticky=(W)) self._spn_stopbits.grid(column=3, row=0, sticky=(W), padx=3, pady=3) self._lbl_parity.grid(column=0, row=1, sticky=(W)) self._spn_parity.grid(column=1, row=1, sticky=(W), padx=3, pady=3) self._chk_rts.grid(column=2, row=1, sticky=(W)) self._chk_xon.grid(column=3, row=1, sticky=(W), padx=3, pady=3) self._lbl_timeout.grid(column=0, row=2, sticky=(W)) self._spn_timeout.grid(column=1, row=2, sticky=(W), padx=3, pady=3) def _attach_events(self): """ Bind events to widgets. """ self._lbx_port.bind("<<ListboxSelect>>", self._on_select_port) def _get_ports(self): """ Populate list of available serial ports using pyserial comports tool. Attempt to automatically select a serial device matching a list of 'preselect' devices (only works on platforms which parse UART device desc or HWID e.g. Posix). """ self._ports = sorted(comports()) init_idx = 0 port = "" desc = "" if len(self._ports) > 0: # default selection to first port (port, desc, _) = self._ports[0] self._port.set(port) self._port_desc.set(desc) for idx, (port, desc, _) in enumerate(self._ports): self._lbx_port.insert(idx, port + ": " + desc) for dev in self._preselect: if dev in desc: # update selection to recognised GNSS device init_idx = idx self._port.set(port) self._port_desc.set(desc) break self.set_status(DISCONNECTED) else: self.set_status(NOPORTS) self._lbx_port.activate(init_idx) self._lbx_port.selection_set(first=init_idx) def _on_select_port(self, *args, **kwargs): # pylint: disable=unused-argument """ Get selected port from listbox and set global variable. """ idx = self._lbx_port.curselection() if idx == "": idx = 0 port_orig = self._lbx_port.get(idx) port = port_orig[0:port_orig.find(":")] desc = port_orig[port_orig.find(":") + 1:] if desc == "": desc = "device" self._port.set(port) self._port_desc.set(desc) def _on_refresh(self): """ Refresh list of ports. """ if self._status in (CONNECTED, CONNECTED_FILE): return self.set_status(DISCONNECTED) self._lbx_port.delete(0, "end") self._get_ports() def _on_toggle_advanced(self): """ Toggle advanced serial port settings panel on or off. """ self._show_advanced = not self._show_advanced if self._show_advanced: self._frm_advanced.grid(column=0, row=1, columnspan=3, sticky=(W, E)) self._btn_toggle.config(image=self._img_contract) else: self._frm_advanced.grid_forget() self._btn_toggle.config(image=self._img_expand) def set_status(self, status: int = DISCONNECTED): """ Set connection status, which determines whether controls are enabled or not: 0=DISCONNECTED, 1=CONNECTED, 2=CONNECTED_FILE, 3=NOPORTS. :param int status: status (0,1,2,3) """ self._status = status for widget in ( self._lbl_port, self._lbl_bpsrate, self._lbl_databits, self._lbl_stopbits, self._lbl_parity, self._lbl_timeout, self._chk_rts, self._chk_xon, self._lbx_port, ): widget.configure( state=(NORMAL if status == DISCONNECTED else DISABLED)) for widget in ( self._spn_bpsrate, self._spn_databits, self._spn_stopbits, self._spn_parity, self._spn_timeout, ): widget.configure( state=(READONLY if status == DISCONNECTED else DISABLED)) def reset(self): """ Reset settings to defaults (first value in range). """ self._bpsrate.set(self._bpsrate_rng[0]) self._databits.set(self._databits_rng[0]) self._stopbits.set(self._stopbits_rng[0]) self._parity.set(self._parity_rng[0]) self._rtscts.set(False) self._xonxoff.set(False) self._timeout.set(self._timeout_rng[0]) @property def status(self) -> int: """ Getter for status flag: 0=DISCONNECTED, 1=CONNECTED, 2=CONNECTED_FILE, 3=NOPORTS. :return: status flag (0,1,2,3) :rtype: int """ return self._status @property def port(self) -> str: """ Getter for port. :return: selected port :rtype: str """ return self._port.get() @property def port_desc(self) -> str: """ Getter for port description. :return: selected port description :rtype: str """ return self._port_desc.get() @property def bpsrate(self) -> int: """ Getter for bps rate (commonly but incorrectly referred to as baud rate). :return: selected baudrate :rtype: int """ return self._bpsrate.get() @property def databits(self) -> int: """ Getter for databits. :return: selected databits :rtype: int """ return self._databits.get() @property def stopbits(self) -> float: """ Getter for stopbits. :return: selected stopbits :rtype: float """ return self._stopbits.get() @property def parity(self) -> str: """ Getter for parity. :return: selected parity :rtype: str """ return PARITIES[self._parity.get()] @property def rtscts(self) -> bool: """ Getter for rts/cts. :return: selected rts/cts :rtype: bool """ return self._rtscts.get() @property def xonxoff(self) -> bool: """ Getter for xon/xoff. :return: selected xon/xoff :rtype: bool """ return self._xonxoff.get() @property def timeout(self) -> float: """ Getter for timeout. :return: selected timeout :rtype: float (or None) """ if self._timeout.get() == "None": return None return float(self._timeout.get())
class ContactsPage(Frame): """ Contacts Page: Contains the list of currently added contacts === Public Attributes === master: The frame containing all information on this page controller: Reference to the Controller Class page_name: String containing a name for this page; used for setting the tabs in the containing notebook contacts_list: Dictionary of contacts, each contact is a Contact class instance, each key is the name of the contact current_contact: Contains the contact that was selected the last time we clicked on show info. scroll_bar: Scroll bar that controls what is viewable in the contacts list; won't scroll if nothing is in the list, or everything is already shown. contacts_field: Area where contacts are shown; 10 at a time letters_field: Listbox that shows each letter of the alphabet to help the user find contact they're looking for show_info: Button that updates the info_field with the information of the currently selected contact wheel_spin: WheelSpinner object for the Show Contact Button. delete: Button that deletes the selected contact info_field: Listbox that contains the information of the currently selected contact info_scroll: Scrollbar that controls what is viewable in the info_field; won't scroll if nothing is in the list, or everything is already shown self.load: Button to load contacts self.save: Button to save contacts === Methods === create: Initializes objects & places them on the page insert_contact: Adds a contact's name to the end of the contacts field show_contact_info: Shows the information of the selected contact in the info listbox delete_contact: Deletes the selected contact & reloads the contacts Listbox clear_fields: Clears both fields on the contacts page load_contacts: Loads contacts in from a file save_contacts: Saves contacts as a file yview: Adjusts the view of contacts_field & letters_field at the same time on_mouse_wheel: Adjusts the view of contacts_field and letters_field at the same time, for the mouse wheel """ def __init__(self, master, controller, **kw): super().__init__(master, **kw) self.master = master self.controller = controller self.page_name = "View Contacts" # Initialize object names self.contacts_list = {} self.current_contact = None self.alphabetical_order = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] self.scroll_bar = None self.contacts_field = None self.letters_field = None self.show_info = None self.wheel_spin = None self.delete = None self.info_field = None self.info_scroll = None self.bind("<<Finish Spinning Wheel>>", self.show_winning_info) self.bind("<Visibility>", self.__on_visibility) self.load = None self.save = None self.create() def create(self) -> None: self.info_scroll = Scrollbar(self, orient=VERTICAL) self.info_field = Listbox( self, yscrollcommand=self.info_scroll.set ) self.delete = Button(self, text="Delete", command=lambda: self.delete_contact()) self.delete.grid(row=2, column=3, columnspan=3, sticky=N + S + E + W) self.show_info = Button(self, text="Show Info", command=lambda: self.show_contact_info()) self.show_info.grid(row=2, column=0, columnspan=3, sticky=N + S + E + W) wheel_spin_options = ['Name', 'Home Phone Numbers', 'Work Phone Numbers', 'Personal Phone Numbers', 'Emails', 'Home Addresses', 'Notes'] self.wheel_spin = WheelSpinner(self, wheel_spin_options, width=50, height=200, radius=60) self.wheel_spin.grid(row=3, column=0, columnspan=5) self.scroll_bar = Scrollbar(self) self.contacts_field = Listbox( self, yscrollcommand=self.scroll_bar.set, selectmode=SINGLE, exportselection=0 ) self.letters_field = Listbox( self, width=2, selectmode=NONE, exportselection=0 ) self.letters_field.bind('<<ListboxSelect>>', self.scroll_to_letter) self.contacts_field.grid(row=1, column=0, columnspan=5, sticky=N + S + E + W) self.letters_field.grid(row=1, column=4, sticky=N + S + E + W) self.scroll_bar.grid(row=1, column=5, sticky=N + S + E + W) self.scroll_bar.config(command=self.yview) self.contacts_field.bind("<MouseWheel>", self.on_mouse_wheel) self.letters_field.bind("<MouseWheel>", self.on_letter_mouse_wheel) self.save = Button( self, text="Save Contacts", command=lambda: self.save_contacts() ) self.save.grid(row=0, column=3, columnspan=4, sticky=N + S + E + W) self.load = Button( self, text="Load Contacts", command=lambda: self.load_contacts() ) self.load.grid(row=0, column=0, columnspan=3, sticky=N + S + E + W) for i in range(3): self.grid_rowconfigure(i, weight=1) for i in range(4): self.grid_columnconfigure(i, weight=1) def scroll_to_letter(self, event): id = 0 for contact in self.order_contact(): if contact[0] == self.letters_field.get(self.letters_field.curselection()[0]): self.contacts_field.see(id) self.contacts_field.selection_clear(0, END) self.contacts_field.selection_set(id) self.letters_field.selection_clear(0, END) return id += 1 self.letters_field.selection_clear(0, END) def on_mouse_wheel(self, event) -> str: self.contacts_field.yview("scroll", int(-event.delta / 80), "units") return "break" def on_letter_mouse_wheel(self, event) -> str: self.letters_field.yview("scroll", int(-event.delta / 80), "units") return "break" def yview(self, *args) -> None: self.contacts_field.yview(*args) self.letters_field.yview(*args) def delete_contact(self) -> None: name = self.contacts_field.get(self.contacts_field.curselection()[0]) del self.contacts_list[name] self.clear_fields() for contact in sorted(self.contacts_list): self.insert_contact(contact) def clear_fields(self) -> None: for field in [self.contacts_field, self.info_field, self.letters_field]: field.delete(0, END) def refresh_fields(self) -> None: self.clear_fields() for contact in self.order_contact(): self.contacts_field.insert(END, contact) for letter in self.alphabetical_order: self.letters_field.insert(END, letter.upper()) def load_contacts(self) -> None: self.randomize_alphabetical_order() with open("project/contacts_pickle", 'rb') as infile: self.contacts_list = pickle.load(infile) self.refresh_fields() def save_contacts(self) -> None: with open("project/contacts_pickle", 'wb') as outfile: pickle.dump(self.contacts_list, outfile) def insert_contact(self, contact) -> None: self.contacts_field.insert(END, contact) def show_contact_info(self) -> None: """ This method shows the spinning wheel if a contact is selected and if the wheel isn't already rotating It is called on the Show Contact Info button. :return: None """ if len(self.contacts_field.curselection()) == 0 or self.wheel_spin.is_rotating: return name = self.contacts_field.get(self.contacts_field.curselection()[0]) self.current_contact = self.contacts_list[name] self.wheel_spin.draw() def show_winning_info(self, event) -> None: """ This method is called when the event <<Finish Spinning Wheel>> is invoked. It displays the current contact information that was selected by the spinning wheel. :return: None """ self.randomize_alphabetical_order() self.refresh_fields() winner = self.wheel_spin.winner label = self.wheel_spin.display_label text0 = self.current_contact.name + "'s " + winner.lower() + ':\n' text1 = '' if winner == 'Name': text1 = self.current_contact.name elif winner == 'Home Phone Numbers': for elem in self.current_contact.phone_numbers["Home"]: text1 = text1 + elem + ", " elif winner == 'Work Phone Numbers': for elem in self.current_contact.phone_numbers["Work"]: text1 = text1 + elem + ', ' elif winner == 'Personal Phone Numbers': for elem in self.current_contact.phone_numbers["Personal"]: text1 = text1 + elem + ', ' elif winner == 'Emails': for elem in self.current_contact.email_addresses: text1 = text1 + elem + ', ' elif winner == 'Home Addresses': for elem in self.current_contact.addresses: text1 = text1 + elem + ', ' elif winner == 'Notes': for elem in self.current_contact.notes: text1 = text1 + elem + ', ' label['text'] = text0 + text1 def randomize_alphabetical_order(self): random.shuffle(self.alphabetical_order) def order_contact(self) -> list: """ This function takes all the contacts and order them in the order stored in self.alphabetical order :return: The ordered list """ i = 0 order = self.alphabetical_order contacts = list(self.contacts_list) ordered_list = [] # We loop until we have all the contact ordered. while i < len(self.contacts_list): current_next_contact = None for contact in contacts: if current_next_contact is None: current_next_contact = contact continue # If the first letter is higher in the order than the current next contact, we # change the contact. if order.index(contact[0].lower()) < order.index(current_next_contact[0].lower()): current_next_contact = contact continue # If the first character is the same, we loop through the other character to find # which on should be # added first. if order.index(contact[0].lower()) == order.index(current_next_contact[0].lower()): for current_character in range(1, min(len(contact), len(current_next_contact))): if order.index(contact[current_character].lower()) < \ order.index(current_next_contact[current_character].lower()): current_next_contact = contact break if order.index(contact[current_character].lower()) > \ order.index(current_next_contact[current_character].lower()): break # we append the contact to the list and remove it from the remaining contact to order. contacts.remove(current_next_contact) ordered_list.append(current_next_contact) i += 1 return ordered_list def __on_visibility(self, event) -> None: """ This function is called when the user click on the contact tab. It randomizes the alphabetical order :param event: :return: None """ self.randomize_alphabetical_order() self.refresh_fields()
class LucteriosMainForm(Tk): def __init__(self): Tk.__init__(self) try: img = Image("photo", file=join( dirname(import_module('lucterios.install').__file__), "lucterios.png")) self.tk.call('wm', 'iconphoto', self._w, img) except: pass self.has_checked = False self.title(ugettext("Lucterios installer")) self.minsize(475, 260) self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self.running_instance = {} self.resizable(True, True) self.protocol("WM_DELETE_WINDOW", self.on_closing) self.ntbk = ttk.Notebook(self) self.ntbk.grid(row=0, column=0, columnspan=1, sticky=(N, S, E, W)) self.create_instance_panel() self.create_module_panel() stl = ttk.Style() stl.theme_use("default") stl.configure("TProgressbar", thickness=5) self.progress = ttk.Progressbar( self, style="TProgressbar", orient='horizontal', mode='indeterminate') self.progress.grid(row=1, column=0, sticky=(E, W)) self.btnframe = Frame(self, bd=1) self.btnframe.grid(row=2, column=0, columnspan=1) Button(self.btnframe, text=ugettext("Refresh"), width=20, command=self.refresh).grid( row=0, column=0, padx=3, pady=3, sticky=(N, S)) self.btnupgrade = Button( self.btnframe, text=ugettext("Search upgrade"), width=20, command=self.upgrade) self.btnupgrade.config(state=DISABLED) self.btnupgrade.grid(row=0, column=1, padx=3, pady=3, sticky=(N, S)) Button(self.btnframe, text=ugettext("Close"), width=20, command=self.on_closing).grid( row=0, column=2, padx=3, pady=3, sticky=(N, S)) def on_closing(self): all_stop = True instance_names = list(self.running_instance.keys()) for old_item in instance_names: if (self.running_instance[old_item] is not None) and self.running_instance[old_item].is_running(): all_stop = False if all_stop or askokcancel(None, ugettext("An instance is always running.\nDo you want to close?")): self.destroy() else: self.refresh() def destroy(self): instance_names = list(self.running_instance.keys()) for old_item in instance_names: if self.running_instance[old_item] is not None: self.running_instance[old_item].stop() del self.running_instance[old_item] Tk.destroy(self) def create_instance_panel(self): frm_inst = Frame(self.ntbk) frm_inst.grid_columnconfigure(0, weight=1) frm_inst.grid_rowconfigure(0, weight=1) frm_inst.grid_columnconfigure(1, weight=3) frm_inst.grid_rowconfigure(1, weight=0) self.instance_list = Listbox(frm_inst, width=20) self.instance_list.bind('<<ListboxSelect>>', self.select_instance) self.instance_list.pack() self.instance_list.grid(row=0, column=0, sticky=(N, S, W, E)) self.instance_txt = Text(frm_inst, width=75) self.instance_txt.grid(row=0, column=1, rowspan=2, sticky=(N, S, W, E)) self.instance_txt.config(state=DISABLED) self.btninstframe = Frame(frm_inst, bd=1) self.btninstframe.grid(row=1, column=0, columnspan=1) self.btninstframe.grid_columnconfigure(0, weight=1) Button(self.btninstframe, text=ugettext("Launch"), width=25, command=self.open_inst).grid( row=0, column=0, columnspan=2, sticky=(N, S)) Button(self.btninstframe, text=ugettext("Modify"), width=10, command=self.modify_inst).grid(row=1, column=0, sticky=(N, S)) Button(self.btninstframe, text=ugettext("Delete"), width=10, command=self.delete_inst).grid(row=1, column=1, sticky=(N, S)) Button(self.btninstframe, text=ugettext("Save"), width=10, command=self.save_inst).grid(row=2, column=0, sticky=(N, S)) Button(self.btninstframe, text=ugettext("Restore"), width=10, command=self.restore_inst).grid(row=2, column=1, sticky=(N, S)) Button(self.btninstframe, text=ugettext("Add"), width=25, command=self.add_inst).grid( row=3, column=0, columnspan=2, sticky=(N, S)) self.ntbk.add(frm_inst, text=ugettext('Instances')) def create_module_panel(self): frm_mod = Frame(self.ntbk) frm_mod.grid_columnconfigure(0, weight=1) frm_mod.grid_rowconfigure(0, weight=1) self.module_txt = Text(frm_mod) self.module_txt.grid(row=0, column=0, sticky=(N, S, W, E)) self.module_txt.config(state=DISABLED) self.ntbk.add(frm_mod, text=ugettext('Modules')) def do_progress(self, progressing): if not progressing: self.progress.stop() self.progress.grid_remove() else: self.progress.start(25) self.progress.grid(row=1, column=0, sticky=(E, W)) def enabled(self, is_enabled, widget=None): if widget is None: widget = self self.do_progress(not is_enabled) if is_enabled: widget.config(cursor="") else: widget.config(cursor="watch") if isinstance(widget, Button) and (widget != self.btnupgrade): if is_enabled and (not hasattr(widget, 'disabled') or not widget.disabled): widget.config(state=NORMAL) else: widget.config(state=DISABLED) else: for child_cmp in widget.winfo_children(): self.enabled(is_enabled, child_cmp) @ThreadRun def refresh(self, instance_name=None): if instance_name is None: instance_name = self.get_selected_instance_name() self.instance_txt.delete("1.0", END) self._refresh_instance_list() self.set_select_instance_name(instance_name) if not self.has_checked: self._refresh_modules() if self.instance_list.size() == 0: sleep(.3) self._refresh_modules() sleep(.3) self.after_idle(self.add_inst) def _refresh_modules(self): self.btnupgrade.config(state=DISABLED) self.module_txt.config(state=NORMAL) self.module_txt.delete("1.0", END) lct_glob = LucteriosGlobal() mod_lucterios, mod_applis, mod_modules = lct_glob.installed() self.module_txt.insert( END, ugettext("Lucterios core\t\t%s\n") % mod_lucterios[1]) self.module_txt.insert(END, '\n') self.module_txt.insert(END, ugettext("Application\n")) for appli_item in mod_applis: self.module_txt.insert( END, "\t%s\t%s\n" % (appli_item[0].ljust(30), appli_item[1])) self.module_txt.insert(END, ugettext("Modules\n")) for module_item in mod_modules: self.module_txt.insert( END, "\t%s\t%s\n" % (module_item[0].ljust(30), module_item[1])) extra_urls = lct_glob.get_extra_urls() if len(extra_urls) > 0: self.module_txt.insert(END, "\n") self.module_txt.insert(END, ugettext("Pypi servers\n")) for extra_url in extra_urls: self.module_txt.insert(END, "\t%s\n" % extra_url) self.module_txt.config(state=DISABLED) self.has_checked = True self.after(1000, lambda: Thread(target=self.check).start()) def _refresh_instance_list(self): self.instance_list.delete(0, END) luct_glo = LucteriosGlobal() instance_list = luct_glo.listing() for item in instance_list: self.instance_list.insert(END, item) if item not in self.running_instance.keys(): self.running_instance[item] = None instance_names = list(self.running_instance.keys()) for old_item in instance_names: if old_item not in instance_list: if self.running_instance[old_item] is not None: self.running_instance[old_item].stop() del self.running_instance[old_item] def set_select_instance_name(self, instance_name): cur_sel = 0 for sel_iter in range(self.instance_list.size()): if self.instance_list.get(sel_iter) == instance_name: cur_sel = sel_iter break self.instance_list.selection_set(cur_sel) self.select_instance(None) def get_selected_instance_name(self): if len(self.instance_list.curselection()) > 0: return self.instance_list.get(int(self.instance_list.curselection()[0])) else: return "" def set_ugrade_state(self, must_upgrade): if must_upgrade: self.btnupgrade.config(state=NORMAL) self.btnupgrade["text"] = ugettext("Upgrade needs") else: self.btnupgrade["text"] = ugettext("No upgrade") self.btnupgrade.config(state=DISABLED) def check(self): must_upgrade = False try: lct_glob = LucteriosGlobal() _, must_upgrade = lct_glob.check() finally: self.after(300, self.set_ugrade_state, must_upgrade) @ThreadRun def upgrade(self): self.btnupgrade.config(state=DISABLED) self.instance_list.config(state=DISABLED) try: from logging import getLogger admin_path = import_module( "lucterios.install.lucterios_admin").__file__ proc = Popen( [sys.executable, admin_path, "update"], stderr=STDOUT, stdout=PIPE) value = proc.communicate()[0] try: value = value.decode('ascii') except: pass six.print_(value) if proc.returncode != 0: getLogger("lucterios.admin").error(value) else: getLogger("lucterios.admin").info(value) showinfo(ugettext("Lucterios installer"), ugettext( "The application must restart")) python = sys.executable os.execl(python, python, *sys.argv) finally: self._refresh_modules() self.btnupgrade.config(state=NORMAL) self.instance_list.config(state=NORMAL) @ThreadRun def select_instance(self, evt): if self.instance_list['state'] == NORMAL: self.instance_list.config(state=DISABLED) try: instance_name = self.get_selected_instance_name() self.instance_txt.configure(state=NORMAL) self.instance_txt.delete("1.0", END) if instance_name != '': if instance_name not in self.running_instance.keys(): self.running_instance[instance_name] = None inst = LucteriosInstance(instance_name) inst.read() self.instance_txt.insert(END, "\t\t\t%s\n\n" % inst.name) self.instance_txt.insert( END, ugettext("Database\t\t%s\n") % inst.get_database_txt()) self.instance_txt.insert( END, ugettext("Appli\t\t%s\n") % inst.get_appli_txt()) self.instance_txt.insert( END, ugettext("Modules\t\t%s\n") % inst.get_module_txt()) self.instance_txt.insert( END, ugettext("Extra\t\t%s\n") % inst.get_extra_txt()) self.instance_txt.insert(END, '\n') if self.running_instance[instance_name] is not None and self.running_instance[instance_name].is_running(): self.instance_txt.insert(END, ugettext( "=> Running in http://%(ip)s:%(port)d\n") % {'ip': self.running_instance[instance_name].lan_ip, 'port': self.running_instance[instance_name].port}) self.btninstframe.winfo_children()[0]["text"] = ugettext( "Stop") else: self.running_instance[instance_name] = None self.instance_txt.insert(END, ugettext("=> Stopped\n")) self.btninstframe.winfo_children()[0]["text"] = ugettext( "Launch") else: self.btninstframe.winfo_children()[0]["text"] = ugettext( "Launch") self.btninstframe.winfo_children()[0].disabled = ( instance_name == '') self.btninstframe.winfo_children()[1].disabled = ( instance_name == '') self.btninstframe.winfo_children()[2].disabled = ( instance_name == '') self.btninstframe.winfo_children()[3].disabled = ( instance_name == '') self.btninstframe.winfo_children()[4].disabled = ( instance_name == '') self.instance_txt.configure(state=DISABLED) finally: setup_from_none() self.instance_list.config(state=NORMAL) @ThreadRun def add_modif_inst_result(self, result, to_create): inst = LucteriosInstance(result[0]) inst.set_extra("LANGUAGE_CODE='%s'" % result[5]) inst.set_appli(result[1]) inst.set_module(result[2]) inst.set_database(result[4]) if to_create: inst.add() else: inst.modif() inst = LucteriosInstance(result[0]) inst.set_extra(result[3]) inst.security() self.refresh(result[0]) def add_inst(self): self.enabled(False) try: self.do_progress(False) ist_edt = InstanceEditor() ist_edt.execute() ist_edt.transient(self) self.wait_window(ist_edt) finally: self.enabled(True) if ist_edt.result is not None: self.add_modif_inst_result(ist_edt.result, True) def modify_inst(self): self.enabled(False) try: self.do_progress(False) ist_edt = InstanceEditor() ist_edt.execute(self.get_selected_instance_name()) ist_edt.transient(self) self.wait_window(ist_edt) finally: self.enabled(True) if ist_edt.result is not None: self.add_modif_inst_result(ist_edt.result, False) @ThreadRun def delete_inst_name(self, instance_name): inst = LucteriosInstance(instance_name) inst.delete() self.refresh() def delete_inst(self): setup_from_none() instance_name = self.get_selected_instance_name() if askokcancel(None, ugettext("Do you want to delete '%s'?") % instance_name): self.delete_inst_name(instance_name) else: self.refresh() @ThreadRun def open_inst(self): instance_name = self.get_selected_instance_name() if instance_name != '': try: if instance_name not in self.running_instance.keys(): self.running_instance[instance_name] = None if self.running_instance[instance_name] is None: port = FIRST_HTTP_PORT for inst_obj in self.running_instance.values(): if (inst_obj is not None) and (inst_obj.port >= port): port = inst_obj.port + 1 self.running_instance[instance_name] = RunServer( instance_name, port) self.running_instance[instance_name].start() else: self.running_instance[instance_name].stop() self.running_instance[instance_name] = None finally: self.set_select_instance_name(instance_name) @ThreadRun def save_instance(self, instance_name, file_name): inst = LucteriosInstance(instance_name) inst.filename = file_name if inst.archive(): showinfo(ugettext("Lucterios installer"), ugettext( "Instance saved to %s") % file_name) else: showerror( ugettext("Lucterios installer"), ugettext("Instance not saved!")) self.refresh(instance_name) def save_inst(self): instance_name = self.get_selected_instance_name() if instance_name != '': file_name = asksaveasfilename( parent=self, filetypes=[('lbk', '.lbk'), ('*', '.*')]) if file_name != '': self.save_instance(instance_name, file_name) @ThreadRun def restore_instance(self, instance_name, file_name): if file_name[-4:] == '.bkf': rest_inst = MigrateFromV1(instance_name, withlog=True) else: rest_inst = LucteriosInstance(instance_name) rest_inst.filename = file_name if rest_inst.restore(): showinfo(ugettext("Lucterios installer"), ugettext( "Instance restore from %s") % file_name) else: showerror( ugettext("Lucterios installer"), ugettext("Instance not restored!")) self.refresh(instance_name) def restore_inst(self): instance_name = self.get_selected_instance_name() if instance_name != '': file_name = askopenfilename( parent=self, filetypes=[('lbk', '.lbk'), ('bkf', '.bkf'), ('*', '.*')]) if file_name != '': self.restore_instance(instance_name, file_name) def execute(self): self.refresh() center(self, (700, 300)) self.mainloop()
class InstanceEditor(Toplevel, EditorInstance): def __init__(self): Toplevel.__init__(self) EditorInstance.__init__(self) self.focus_set() self.grab_set() self.result = None self.module_data = None self.mod_applis = None self.title(ugettext("Instance editor")) self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self.ntbk = ttk.Notebook(self) self.ntbk.grid(row=0, column=0, columnspan=1, sticky=(N, S, E, W)) self.frm_general = Frame(self.ntbk, width=350, height=150) self.frm_general.grid_columnconfigure(0, weight=0) self.frm_general.grid_columnconfigure(1, weight=1) self._general_tabs() self.ntbk.add(self.frm_general, text=ugettext('General')) self.frm_database = Frame(self.ntbk, width=350, height=150) self.frm_database.grid_columnconfigure(0, weight=0) self.frm_database.grid_columnconfigure(1, weight=1) self._database_tabs() self.ntbk.add(self.frm_database, text=ugettext('Database')) btnframe = Frame(self, bd=1) btnframe.grid(row=1, column=0, columnspan=1) Button(btnframe, text=ugettext("OK"), width=10, command=self.apply).grid(row=0, column=0, sticky=(N, S, E)) Button(btnframe, text=ugettext("Cancel"), width=10, command=self.destroy).grid(row=0, column=1, sticky=(N, S, W)) def _database_tabs(self): Label(self.frm_database, text=ugettext("Type")).grid(row=0, column=0, sticky=(N, W), padx=5, pady=3) self.typedb = ttk.Combobox(self.frm_database, textvariable=StringVar(), state=READLONY) self.typedb.bind("<<ComboboxSelected>>", self.typedb_selection) self.typedb.grid(row=0, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_database, text=ugettext("Name")).grid(row=1, column=0, sticky=(N, W), padx=5, pady=3) self.namedb = Entry(self.frm_database) self.namedb.grid(row=1, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_database, text=ugettext("User")).grid(row=2, column=0, sticky=(N, W), padx=5, pady=3) self.userdb = Entry(self.frm_database) self.userdb.grid(row=2, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_database, text=ugettext("Password")).grid(row=3, column=0, sticky=(N, W), padx=5, pady=3) self.pwddb = Entry(self.frm_database) self.pwddb.grid(row=3, column=1, sticky=(N, S, E, W), padx=5, pady=3) def _general_tabs(self): Label(self.frm_general, text=ugettext("Name")).grid(row=0, column=0, sticky=(N, W), padx=5, pady=3) self.name = Entry(self.frm_general) self.name.grid(row=0, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_general, text=ugettext("Appli")).grid(row=1, column=0, sticky=(N, W), padx=5, pady=3) self.applis = ttk.Combobox(self.frm_general, textvariable=StringVar(), state=READLONY) self.applis.bind("<<ComboboxSelected>>", self.appli_selection) self.applis.grid(row=1, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_general, text=ugettext("Modules")).grid(row=2, column=0, sticky=(N, W), padx=5, pady=3) self.modules = Listbox(self.frm_general, selectmode=EXTENDED) self.modules.configure(exportselection=False) self.modules.grid(row=2, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_general, text=ugettext("Language")).grid(row=3, column=0, sticky=(N, W), padx=5, pady=3) self.language = ttk.Combobox(self.frm_general, textvariable=StringVar(), state=READLONY) self.language.grid(row=3, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_general, text=ugettext("CORE-connectmode")).grid(row=4, column=0, sticky=(N, W), padx=5, pady=3) self.mode = ttk.Combobox(self.frm_general, textvariable=StringVar(), state=READLONY) self.mode.bind("<<ComboboxSelected>>", self.mode_selection) self.mode.grid(row=4, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_general, text=ugettext("Password")).grid(row=5, column=0, sticky=(N, W), padx=5, pady=3) self.password = Entry(self.frm_general, show="*") self.password.grid(row=5, column=1, sticky=(N, S, E, W), padx=5, pady=3) def typedb_selection(self, event): visible = list(self.typedb[VALUES]).index(self.typedb.get()) != 0 for child_cmp in self.frm_database.winfo_children()[2:]: if visible: child_cmp.config(state=NORMAL) else: child_cmp.config(state=DISABLED) def appli_selection(self, event): if self.applis.get() != '': appli_id = list(self.applis[VALUES]).index(self.applis.get()) luct_glo = LucteriosGlobal() current_inst_names = luct_glo.listing() appli_root_name = self.mod_applis[appli_id][0].split('.')[-1] default_name_idx = 1 while appli_root_name + six.text_type( default_name_idx) in current_inst_names: default_name_idx += 1 self.name.delete(0, END) self.name.insert(0, appli_root_name + six.text_type(default_name_idx)) mod_depended = self.mod_applis[appli_id][2] self.modules.select_clear(0, self.modules.size()) for mod_idx in range(len(self.module_data)): current_mod = self.module_data[mod_idx] if current_mod in mod_depended: self.modules.selection_set(mod_idx) def mode_selection(self, event): visible = list(self.mode[VALUES]).index(self.mode.get()) != 2 for child_cmp in self.frm_general.winfo_children()[-2:]: if visible: child_cmp.config(state=NORMAL) else: child_cmp.config(state=DISABLED) def apply(self): from lucterios.framework.settings import DEFAULT_LANGUAGES, get_locale_lang if self.is_new_instance and ( (self.name.get() == '') or (self.name_rull.match(self.name.get()) is None)): showerror(ugettext("Instance editor"), ugettext("Name invalid!")) return if self.applis.get() == '': showerror(ugettext("Instance editor"), ugettext("No application!")) return db_param = "%s:name=%s,user=%s,password=%s" % ( self.typedb.get(), self.namedb.get(), self.userdb.get(), self.pwddb.get()) security = "MODE=%s" % list(self.mode[VALUES]).index(self.mode.get()) if self.password.get() != '': security += ",PASSWORD=%s" % self.password.get() module_list = [ self.module_data[int(item)] for item in self.modules.curselection() ] appli_id = list(self.applis[VALUES]).index(self.applis.get()) current_lang = get_locale_lang() for lang in DEFAULT_LANGUAGES: if lang[1] == self.language.get(): current_lang = lang[0] self.result = (self.name.get(), self.mod_applis[appli_id][0], ",".join(module_list), security, db_param, current_lang) self.destroy() def _load_current_data(self, instance_name): from lucterios.framework.settings import DEFAULT_LANGUAGES lct_inst, applis_id, mode_id, typedb_index, current_lang = self._get_instance_elements( instance_name) self.is_new_instance = False self.name.delete(0, END) self.name.insert(0, lct_inst.name) self.name.config(state=DISABLED) self.applis.current(applis_id) self.mode.current(mode_id) self.mode_selection(None) self.typedb.current(typedb_index) self.typedb.config(state=DISABLED) self.typedb_selection(None) self.namedb.delete(0, END) if 'name' in lct_inst.database[1].keys(): self.namedb.insert(0, lct_inst.database[1]['name']) self.userdb.delete(0, END) if 'user' in lct_inst.database[1].keys(): self.userdb.insert(0, lct_inst.database[1]['user']) self.pwddb.delete(0, END) if 'password' in lct_inst.database[1].keys(): self.pwddb.insert(0, lct_inst.database[1]['password']) self.modules.select_clear(0, self.modules.size()) for mod_idx in range(len(self.module_data)): current_mod = self.module_data[mod_idx] if current_mod in lct_inst.modules: self.modules.select_set(mod_idx) for lang in DEFAULT_LANGUAGES: if lang[0] == current_lang: self.language.current(self.language[VALUES].index(lang[1])) def execute(self, instance_name=None): from lucterios.framework.settings import DEFAULT_LANGUAGES, get_locale_lang self._define_values() self.module_data = [] self.modules.delete(0, END) for module_title, module_name in self.module_list: self.modules.insert(END, module_title) self.module_data.append(module_name) self.mode[VALUES] = self.mode_values self.language[VALUES] = self.lang_values self.typedb[VALUES] = self.dbtype_values self.applis[VALUES] = self.appli_list if instance_name is not None: self._load_current_data(instance_name) else: self.typedb.current(0) self.mode.current(2) if len(self.appli_list) > 0: self.applis.current(0) self.appli_selection(None) self.mode_selection(None) self.typedb_selection(None) for lang in DEFAULT_LANGUAGES: if lang[0] == get_locale_lang(): self.language.current(self.language[VALUES].index(lang[1])) center(self)
class LucteriosMainForm(Tk, LucteriosMain): def __init__(self): Tk.__init__(self) LucteriosMain.__init__(self) try: try: self.tk.call('tk_getOpenFile', '-foobarbaz') except TclError: pass # now set the magic variables accordingly self.tk.call('set', '::tk::dialog::file::showHiddenBtn', '1') self.tk.call('set', '::tk::dialog::file::showHiddenVar', '0') except Exception: pass try: self.img = Image( "photo", file=join(dirname(import_module('lucterios.install').__file__), "lucterios.png")) self.tk.call('wm', 'iconphoto', self._w, self.img) except Exception: self.img = None self.start_up_app() self.has_checked = False self.title(ugettext("Lucterios launcher")) self.minsize(475, 260) self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self.resizable(True, True) self.protocol("WM_DELETE_WINDOW", self.on_closing) self.ntbk = ttk.Notebook(self) self.ntbk.grid(row=0, column=0, columnspan=1, sticky=(N, S, E, W)) self.create_instance_panel() self.create_module_panel() stl = ttk.Style() stl.theme_use("default") stl.configure("TProgressbar", thickness=5) self.progress = ttk.Progressbar(self, style="TProgressbar", orient='horizontal', mode='indeterminate') self.progress.grid(row=1, column=0, sticky=(E, W)) self.btnframe = Frame(self, bd=1) self.btnframe.grid(row=2, column=0, columnspan=1) Button(self.btnframe, text=ugettext("Refresh"), width=20, command=self.refresh).grid(row=0, column=0, padx=3, pady=3, sticky=(N, S)) self.btnupgrade = Button(self.btnframe, text=ugettext("Search upgrade"), width=20, command=self.upgrade) self.btnupgrade.config(state=DISABLED) self.btnupgrade.grid(row=0, column=1, padx=3, pady=3, sticky=(N, S)) Button(self.btnframe, text=ugettext("Close"), width=20, command=self.on_closing).grid(row=0, column=2, padx=3, pady=3, sticky=(N, S)) def show_splash_screen(self): if sys.platform != 'darwin': self.withdraw() self.splash = SplashScreen(self, self.img) def remove_splash_screen(self): self.splash.destroy_splash_screen() del self.splash self.deiconify() def show_info(self, text, message): showinfo(text, message) def show_error(self, text, message): showerror(text, message) def on_closing(self): if self.is_all_stop() or askokcancel( None, ugettext( "An instance is always running.\nDo you want to close?")): self.destroy() else: self.refresh() def destroy(self): self.stop_all() Tk.destroy(self) def create_instance_panel(self): frm_inst = Frame(self.ntbk) frm_inst.grid_columnconfigure(0, weight=1) frm_inst.grid_rowconfigure(0, weight=1) frm_inst.grid_columnconfigure(1, weight=3) frm_inst.grid_rowconfigure(1, weight=0) self.instance_list = Listbox(frm_inst, width=20) self.instance_list.bind('<<ListboxSelect>>', self.select_instance) self.instance_list.pack() self.instance_list.grid(row=0, column=0, sticky=(N, S, W, E)) self.instance_txt = Text(frm_inst, width=75) self.instance_txt.grid(row=0, column=1, rowspan=2, sticky=(N, S, W, E)) self.instance_txt.config(state=DISABLED) self.btninstframe = Frame(frm_inst, bd=1) self.btninstframe.grid(row=1, column=0, columnspan=1) self.btninstframe.grid_columnconfigure(0, weight=1) Button(self.btninstframe, text=ugettext("Launch"), width=25, command=self.open_inst).grid(row=0, column=0, columnspan=2, sticky=(N, S)) Button(self.btninstframe, text=ugettext("Modify"), width=10, command=self.modify_inst).grid(row=1, column=0, sticky=(N, S)) Button(self.btninstframe, text=ugettext("Delete"), width=10, command=self.delete_inst).grid(row=1, column=1, sticky=(N, S)) Button(self.btninstframe, text=ugettext("Save"), width=10, command=self.save_inst).grid(row=2, column=0, sticky=(N, S)) Button(self.btninstframe, text=ugettext("Restore"), width=10, command=self.restore_inst).grid(row=2, column=1, sticky=(N, S)) Button(self.btninstframe, text=ugettext("Add"), width=25, command=self.add_inst).grid(row=3, column=0, columnspan=2, sticky=(N, S)) self.ntbk.add(frm_inst, text=ugettext('Instances')) def create_module_panel(self): frm_mod = Frame(self.ntbk) frm_mod.grid_columnconfigure(0, weight=1) frm_mod.grid_rowconfigure(0, weight=1) self.module_txt = Text(frm_mod) self.module_txt.grid(row=0, column=0, sticky=(N, S, W, E)) self.module_txt.config(state=DISABLED) self.ntbk.add(frm_mod, text=ugettext('Modules')) def do_progress(self, progressing): if not progressing: self.progress.stop() self.progress.grid_remove() else: self.progress.start(25) self.progress.grid(row=1, column=0, sticky=(E, W)) def enabled(self, is_enabled, widget=None): if widget is None: widget = self self.do_progress(not is_enabled) if is_enabled: widget.config(cursor="") else: widget.config(cursor="watch") if isinstance(widget, Button) and (widget != self.btnupgrade): if is_enabled and (not hasattr(widget, 'disabled') or not widget.disabled): widget.config(state=NORMAL) else: widget.config(state=DISABLED) else: for child_cmp in widget.winfo_children(): self.enabled(is_enabled, child_cmp) @ThreadRun def refresh(self, instance_name=None): if instance_name is None: instance_name = self.get_selected_instance_name() self.instance_txt.delete("1.0", END) self._refresh_instance_list() self.set_select_instance_name(instance_name) if not self.has_checked: self._refresh_modules() if self.instance_list.size() == 0: sleep(.3) self._refresh_modules() sleep(.3) self.after_idle(self.add_inst) def run_after(self, ms, func=None, *args): self.after(ms, func, *args) def _refresh_modules(self): self.btnupgrade.config(state=DISABLED) self.module_txt.config(state=NORMAL) self.module_txt.delete("1.0", END) lct_glob = LucteriosGlobal() mod_lucterios, mod_applis, mod_modules = lct_glob.installed() self.module_txt.insert( END, ugettext("Lucterios core\t\t%s\n") % mod_lucterios[1]) self.module_txt.insert(END, '\n') self.module_txt.insert(END, ugettext("Application\n")) for appli_item in mod_applis: self.module_txt.insert( END, "\t%s\t%s\n" % (appli_item[0].ljust(30), appli_item[1])) self.module_txt.insert(END, ugettext("Modules\n")) for module_item in mod_modules: self.module_txt.insert( END, "\t%s\t%s\n" % (module_item[0].ljust(30), module_item[1])) extra_urls = lct_glob.get_extra_urls() if len(extra_urls) > 0: self.module_txt.insert(END, "\n") self.module_txt.insert(END, ugettext("Pypi servers\n")) for extra_url in extra_urls: self.module_txt.insert(END, "\t%s\n" % extra_url) self.module_txt.config(state=DISABLED) self.has_checked = True self.run_after(1000, lambda: Thread(target=self.check).start()) def _refresh_instance_list(self): self.instance_list.delete(0, END) luct_glo = LucteriosGlobal() instance_list = luct_glo.listing() for item in instance_list: self.instance_list.insert(END, item) if item not in self.running_instance.keys(): self.running_instance[item] = None instance_names = list(self.running_instance.keys()) for old_item in instance_names: if old_item not in instance_list: if self.running_instance[old_item] is not None: self.running_instance[old_item].stop() del self.running_instance[old_item] def set_select_instance_name(self, instance_name): cur_sel = 0 for sel_iter in range(self.instance_list.size()): if self.instance_list.get(sel_iter) == instance_name: cur_sel = sel_iter break self.instance_list.selection_set(cur_sel) self.select_instance(None) def get_selected_instance_name(self): if len(self.instance_list.curselection()) > 0: return self.instance_list.get( int(self.instance_list.curselection()[0])) else: return "" def set_ugrade_state(self, upgrade_mode): if upgrade_mode != 2: msg = ugettext("The application must restart") self.show_info(ugettext("Lucterios launcher"), msg) self.btnupgrade["text"] = msg self.btnupgrade.config(state=DISABLED) self._refresh_modules() else: self.btnupgrade["text"] = self.ugrade_message(upgrade_mode == 1) self.btnupgrade.config( state=NORMAL if upgrade_mode == 1 else DISABLED) def upgrade(self): self.btnupgrade.config(state=DISABLED) self.instance_list.config(state=DISABLED) try: LucteriosMain.upgrade(self) finally: self.btnupgrade.config(state=DISABLED) self.instance_list.config(state=NORMAL) @ThreadRun def select_instance(self, evt): if self.instance_list['state'] == NORMAL: self.instance_list.config(state=DISABLED) try: instance_name = self.get_selected_instance_name() self.instance_txt.configure(state=NORMAL) self.instance_txt.delete("1.0", END) if instance_name != '': if instance_name not in self.running_instance.keys(): self.running_instance[instance_name] = None inst = LucteriosInstance(instance_name) inst.read() self.instance_txt.insert(END, "\t\t\t%s\n\n" % inst.name) self.instance_txt.insert( END, ugettext("Database\t\t%s\n") % inst.get_database_txt()) self.instance_txt.insert( END, ugettext("Appli\t\t%s\n") % inst.get_appli_txt()) self.instance_txt.insert( END, ugettext("Modules\t\t%s\n") % inst.get_module_txt()) self.instance_txt.insert( END, ugettext("Extra\t\t%s\n") % inst.get_extra_txt()) self.instance_txt.insert(END, '\n') if self.running_instance[ instance_name] is not None and self.running_instance[ instance_name].is_running(): self.instance_txt.insert( END, ugettext("=> Running in http://%(ip)s:%(port)d\n") % { 'ip': self.running_instance[instance_name].lan_ip, 'port': self.running_instance[instance_name].port }) self.btninstframe.winfo_children( )[0]["text"] = ugettext("Stop") else: self.running_instance[instance_name] = None self.instance_txt.insert(END, ugettext("=> Stopped\n")) self.btninstframe.winfo_children( )[0]["text"] = ugettext("Launch") else: self.btninstframe.winfo_children()[0]["text"] = ugettext( "Launch") self.btninstframe.winfo_children()[0].disabled = ( instance_name == '') self.btninstframe.winfo_children()[1].disabled = ( instance_name == '') self.btninstframe.winfo_children()[2].disabled = ( instance_name == '') self.btninstframe.winfo_children()[3].disabled = ( instance_name == '') self.btninstframe.winfo_children()[4].disabled = ( instance_name == '') self.instance_txt.configure(state=DISABLED) finally: setup_from_none() self.instance_list.config(state=NORMAL) def add_inst(self): self.enabled(False) try: self.do_progress(False) ist_edt = InstanceEditor() ist_edt.execute() ist_edt.transient(self) self.wait_window(ist_edt) finally: self.enabled(True) if ist_edt.result is not None: self.add_modif_inst_result(ist_edt.result, True) def modify_inst(self): self.enabled(False) try: self.do_progress(False) ist_edt = InstanceEditor() ist_edt.execute(self.get_selected_instance_name()) ist_edt.transient(self) self.wait_window(ist_edt) finally: self.enabled(True) if ist_edt.result is not None: self.add_modif_inst_result(ist_edt.result, False) def delete_inst(self): setup_from_none() instance_name = self.get_selected_instance_name() if askokcancel(None, ugettext("Do you want to delete '%s'?") % instance_name): self.delete_inst_name(instance_name) else: self.refresh() def save_inst(self): instance_name = self.get_selected_instance_name() if instance_name != '': file_name = asksaveasfilename(parent=self, filetypes=[('lbk', '.lbk'), ('*', '.*')], initialdir=expanduser('~')) if file_name != '': self.save_instance(instance_name, file_name) def restore_inst(self): instance_name = self.get_selected_instance_name() if instance_name != '': file_name = askopenfilename(parent=self, filetypes=[('lbk', '.lbk'), ('*', '.*')], initialdir=expanduser('~')) if isinstance(file_name, str) and (file_name != ''): self.restore_instance(instance_name, file_name) def execute(self): self.refresh() center(self, (700, 300)) self.mainloop()
class AutocompleteEntry(Entry): def __init__(self, *args, **kwargs): Entry.__init__(self, width=100, *args, **kwargs) self.focus_set() self.pack() self.var = self["textvariable"] if self.var == '': self.var = self["textvariable"] = StringVar() self.var.trace('w', self.changed) self.bind("<Right>", self.selection) self.bind("<Up>", self.up) self.bind("<Down>", self.down) self.bind("<Return>", self.enter) self.lb_up = False self.lb = None def enter(self, event): print(event) def changed(self, name, index, mode): if self.var.get() == '': if self.lb: self.lb.destroy() self.lb_up = False else: words = self.comparison() if words: if not self.lb_up: self.lb = Listbox(master=root, width=100) self.lb.bind("<Double-Button-1>", self.selection) self.lb.bind("<Right>", self.selection) self.lb.place(x=self.winfo_x(), y=self.winfo_y()+self.winfo_height()) self.lb_up = True self.lb.delete(0, END) for w in words: self.lb.insert(END,w) else: if self.lb_up: self.lb.destroy() self.lb_up = False def selection(self, _): if self.lb_up: self.var.set(self.lb.get(ACTIVE)) self.lb.destroy() self.lb_up = False self.icursor(END) def up(self, _): if self.lb_up: if self.lb.curselection() == (): index = '0' else: index = self.lb.curselection()[0] if index != '0': self.lb.selection_clear(first=index) index = str(int(index)-1) self.lb.selection_set(first=index) self.lb.activate(index) def down(self, _): if self.lb_up: if self.lb.curselection() == (): index = '0' else: index = self.lb.curselection()[0] if index != END: self.lb.selection_clear(first=index) index = str(int(index)+1) self.lb.selection_set(first=index) self.lb.activate(index) def comparison(self): q = self.var.get() q = str(q.decode('utf8')) for hit in searcher.search(qp.parse(q), limit=50): if hit['author']: yield '%s. "%s"' % (hit['author'], hit['title']) else: yield hit['title']
class AutomateCenter(Toplevel): def __init__(self, parent, dir=""): super().__init__(parent, height=100) self.parent = parent self.attributes('-topmost', 'true') self.resizable(width=False, height=False) self.title('Automate Center') self.dir = dir self.files = [] self.filesToInsert = [] print('Current dir received by Automate Center: ' + self.dir) for file in os.listdir(self.dir): self.files.append( os.path.join(self.dir, file).replace('\\', '/').replace('//', '/')[2:]) self.lbl = Label(self, text = ' Select the files for which you \nwish' \ + ' to automate PPT Insert' ) self.lbl.pack() selectFrame = Frame(self) selectFrame.pack(pady=5) self.selectAllButton = Button(selectFrame, text="ALL") self.selectNoneButton = Button(selectFrame, text="NONE") self.selectAllButton.grid(row=0, column=1, padx=2) self.selectNoneButton.grid(row=0, column=0, padx=2) self.selectAllButton.bind('<ButtonRelease-1>', self.onAllClick) self.selectNoneButton.bind('<ButtonRelease-1>', self.onNoneClick) self.lb = Listbox(self, selectmode=MULTIPLE) for i in self.files: self.lb.insert(END, i) self.lb.bind("<<ListboxSelect>>", self.onSelect) self.lb.pack(pady=5) self.cancelBtn = Button(self, text='Cancel', width=12, height=1) self.cancelBtn.pack() self.okBtn = Button(self, text='OK', width=12, height=1) self.okBtn.pack(pady=2) self.okBtn.bind('<ButtonRelease-1>', self.onOkClick) self.cancelBtn.bind('<ButtonRelease-1>', self.onCancelClick) self.NABtn = Button(self, text='No Automation', width=12, height=1) self.NABtn.pack(pady=2) self.NABtn.bind('<ButtonRelease-1>', self.onNAClick) def onCancelClick(self, event): self.destroy() def onNAClick(self, event): log.writeEvent('Cancel Automate') self.parent.automate = False self.parent.slides.append(self.parent.blank) self.destroy() def onOkClick(self, event): if self.filesToInsert == []: return for idx in range(len(self.files)): if idx in self.filesToInsert: self.parent.slides.append(PPTImage('OS/' + self.files[idx])) self.parent.show.configure(image=self.parent.slides[-1].original) self.parent.show.image = self.parent.slides[-1].original self.parent.populate() self.destroy() def onSelect(self, event): self.filesToInsert = list(self.lb.curselection()) def onAllClick(self, event): self.filesToInsert = [] self.lb.selection_set(0, END) for idx in range(len(self.files)): self.filesToInsert.append(idx) def onNoneClick(self, event): self.lb.selection_clear(0, END) self.filesToInsert = []
class Player: def __init__(self, master): self.master = master pygame.init() pygame.mixer.init() #===Empty thread list=====# self.threads = [] #=====show an icon for the player===# def get_icon(): self.winicon = PhotoImage(file="best (2).png") master.iconphoto(False, self.winicon) #=====run the get_icon on a different thread from the gui=====# def icon(): mythreads = threading.Thread(target=get_icon) self.threads.append(mythreads) mythreads.start() icon() #=======all Button symbols and variables======# PLAY = "►" PAUSE = "║║" RWD = "⏮" FWD = "⏭" STOP = "■" UNPAUSE = "||" mute = "🔇" unmute = u"\U0001F50A" vol_mute = 0.0 vol_unmute = 1 #==========music playlist listbox=========# self.scroll = Scrollbar(master) self.play_list = Listbox(master, font="Sansarif 12 bold", bd=5, bg="white", width=37, height=19, selectbackground="black") self.play_list.place(x=600, y=77) self.scroll.place(x=946, y=80, height=389, width=15) self.scroll.config(command=self.play_list.yview) self.play_list.config(yscrollcommand=self.scroll.set) files = 'best (2).png' self.img1 = Image.open(files) self.img1 = self.img1.resize((600, 470), Image.ANTIALIAS) self.img = ImageTk.PhotoImage(self.img1) self.lab = Label(master) self.lab.grid(row=0, column=0) self.lab["compound"] = LEFT self.lab["image"] = self.img #=====show the song playing==========# self.var = StringVar() self.var.set( ".............................................................................." ) self.song_title = Label(master, font="Helvetica 12 bold", bg="black", fg="white", width=60, textvariable=self.var) self.song_title.place(x=3, y=0) # =====add a music list to the listbox======" def append_listbox(): global song_list try: directory = askdirectory() os.chdir(directory) # it permits to change the current dir song_list = os.listdir() song_list.reverse() for item in song_list: # it returns the list of files song pos = 0 self.play_list.insert(pos, item) pos += 1 global size index = 0 size = len(song_list) self.play_list.selection_set(index) self.play_list.see(index) self.play_list.activate(index) self.play_list.selection_anchor(index) except: showerror("File selected error", "Please choose a file correctly") # =====run the append_listbox function on separate thread====== # def add_songs_playlist(): mythreads = threading.Thread(target=append_listbox) self.threads.append(mythreads) mythreads.start() #=====show music time=========# def get_time(): current_time = pygame.mixer.music.get_pos() / 1000 formated_time = time.strftime("%H:%M:%S", time.gmtime(current_time)) next_one = self.play_list.curselection() song = self.play_list.get(next_one) song_timer = MP3(song) song_length = int(song_timer.info.length) format_for_length = time.strftime("%H:%M:%S", time.gmtime(song_length)) self.label_time.config( text=f"{ format_for_length} / {formated_time}") self.progress["maximum"] = song_length self.progress["value"] = int(current_time) master.after(100, get_time) #=====play the music====# def Play_music(): try: track = self.play_list.get(ACTIVE) pygame.mixer.music.load(track) self.var.set(track) pygame.mixer.music.play() get_time() # iterate through all the songs in the playlist # there is a bug when closing the window except: showerror("No Music", "Please load the music you want to play") def playAll(): try: index = 0 for i in range(size): self.play_list.select_clear(0, END) self.play_list.selection_set(index, last=None) self.play_list.see(index) self.play_list.activate(index) self.play_list.selection_anchor(index) track = self.play_list.get(index) pygame.mixer.music.load(track) self.var.set(track) pygame.mixer.music.play() current_song = self.play_list.curselection() song = self.play_list.get(current_song) song_timer = MP3(song) song_length = int(song_timer.info.length) * 1000 get_time() index += 1 except: showerror("No songs in playlist", "Please add music") def play_all(): mythreads = threading.Thread(target=playAll) self.threads.append(mythreads) mythreads.start() # ===pause and unpause == # def pause_unpause(): if self.button_pause['text'] == PAUSE: pygame.mixer.music.pause() self.button_pause['text'] = UNPAUSE elif self.button_pause['text'] == UNPAUSE: pygame.mixer.music.unpause() self.button_pause['text'] = PAUSE # ==play the music on a diffent thread from the gui == # def play_thread(): mythreads = threading.Thread(target=Play_music) self.threads.append(mythreads) mythreads.start() master.bind("<space>", lambda x: play_thread()) # ===stop=== def stop(): pygame.mixer.music.stop() #====increase and decrease volume when slider is moved()==# def volume(x): pygame.mixer.music.set_volume(self.volume_slider.get()) # ====mute and unmute the song while the song plays== # def muted(): if self.button_mute['text'] == unmute: pygame.mixer.music.set_volume(vol_mute) self.volume_slider.set(vol_mute) self.button_mute['fg'] = "red" self.button_mute['text'] = mute elif self.button_mute['text'] == mute: pygame.mixer.music.set_volume(vol_unmute) self.volume_slider.set(vol_unmute) self.button_mute['fg'] = "white" self.button_mute['text'] = unmute #===move to the next song===# def nextSong(): try: next_one = self.play_list.curselection() next_one = next_one[0] + 1 song = self.play_list.get(next_one) pygame.mixer.music.load(song) pygame.mixer.music.play() self.play_list.select_clear(0, END) self.play_list.activate(next_one) self.play_list.selection_set(next_one, last=None) self.var.set(song) get_time() self.play_list.see(next_one) except: showerror("No Next Song", "Please press the previous button") def next(): mythreads = threading.Thread(target=nextSong) self.threads.append(mythreads) mythreads.start() #===move to the previous song===# def prevSong(): try: next_one = self.play_list.curselection() next_one = next_one[0] - 1 song = self.play_list.get(next_one) pygame.mixer.music.load(song) pygame.mixer.music.play() self.play_list.select_clear(0, END) self.play_list.activate(next_one) self.play_list.selection_set(next_one, last=None) self.var.set(song) get_time() self.play_list.see(next_one) except: showerror("No previous Song", "Please press the Next button") def prev(): mythreads = threading.Thread(target=prevSong) self.threads.append(mythreads) mythreads.start() self.master.bind('<Left>', lambda x: prev()) self.master.bind('<Right>', lambda x: next()) #=====exit the application=====# def exit(): MsgBox = askquestion( 'Exit Application', 'Are you sure you want to exit the music player.', icon='warning') if MsgBox == 'yes': master.quit() master.after(100, exit) else: showinfo('Return', 'Continue playing your awesome music') return #=====Help window=====# def help(): top = Toplevel() top.title("Help") top.geometry("350x554+500+80") top.resizable(width=0, height=0) user_manual = [ " MUSIC PLAYER USER MANUAL: \n", "1. play button = ( ► )", "2. pause button = ║║ ", "3. unpause symbol = ||", "4. next button = ⏭ ", "5. previous button = ⏮", "6. mute button = '\U0001F50A' ", "7. unmute symbol = 🔇", "8. stop button = ■ ", "\n\n| Made by manucho | Copyright @ 2021 |\n" ] for i in user_manual: manual = Label(top, text=i, width=50, height=3, font="Helvetica, 11", bg="black", fg="white") manual.pack(side=TOP, fill=BOTH) #==============================================================================================# # THis part contains the menu, volume slider , music playlist label and the volume slider # #===============================================================================================# self.menu = Menu( self.lab, font="helvetica, 3", ) master.config(menu=self.menu) self.menu.add_command(label="HELP", command=help) self.menu.add_command(label="EXIT", command=exit) self.separator = ttk.Separator(self.lab, orient='horizontal') self.separator.place(relx=0, rely=0.87, relwidth=1, relheight=1) self.button_play = Button(master, text=PLAY, width=5, bd=5, bg="black", fg="white", font="Helvetica, 15", command=play_thread) self.button_play.place(x=150, y=415) self.button_stop = Button(master, text=STOP, width=5, bd=5, font="Helvetica, 15", bg="black", fg="white", command=stop) self.button_stop.place(x=225, y=415) self.button_prev = Button(master, text=FWD, width=5, bd=5, font="Helvetica, 15", bg="black", fg="white", command=next) self.button_prev.place(x=300, y=415) self.buttonPlayall = Button(self.master, text='\U0001F500', bg='black', fg='white', font='Helvetica, 15', bd=5, width=3, command=play_all) self.buttonPlayall.place(x=375, y=415) self.button_next = Button(master, text=RWD, width=5, bd=5, bg="black", fg="white", font="Helvetica, 15", command=prev) self.button_next.place(x=10, y=415) self.button_pause = Button(master, text=PAUSE, width=4, bd=5, font="Helvetica, 15", bg="black", fg="white", command=pause_unpause) self.button_pause.place(x=85, y=415) self.button_mute = Button(master, text=unmute, width=2, bd=5, font="Helvetica, 15", bg="black", fg="white", command=muted) self.button_mute.place(x=430, y=415) self.label_playlist = Label(master, text=u"♫ Music Playlist ♫ ", width=31, font="Helvetica, 15") self.label_playlist.place(x=610, y=5) self.button_load_music = Button( master, text="♫ Click Here To Load The Music ♫", width=43, bd=5, font="Helvetica, 10", bg="black", fg="white", command=add_songs_playlist) self.button_load_music.place(x=605, y=45) self.style = ttk.Style() self.style.configure("myStyle.Horizontal.TScale", background="#505050") self.volume_slider = ttk.Scale(self.lab, from_=0, to=1, orient=HORIZONTAL, value=1, length=120, command=volume, style="myStyle.Horizontal.TScale") self.volume_slider.place(x=475, y=424) self.progress = ttk.Progressbar(self.lab, orient=HORIZONTAL, value=0, length=453, mode='determinate') self.progress.place(x=0, y=385) self.label_time = Label(master, text="00:00:00 / 00:00:00", width=17, font="Helvetica, 10", bg="black", fg="white") self.label_time.place(x=460, y=387)
class index_select(Frame): def __init__(self,controller,current_model,master,*args,**kwargs): self.controller = controller self.current_model = current_model self.filtered_list = None self.saved_selection = None from tkinter import EXTENDED,Scrollbar,Y #dibujar widget super().__init__(master, *args, **kwargs) f_st=Frame(self) Label(f_st,text="Current_index: ").pack() Label(f_st, text="Current_filter: ").pack() f_st.pack() frame_index_listbox = Frame(self) self.listbox = Listbox(frame_index_listbox, exportselection=False,selectmode=EXTENDED) self.listbox.pack(side=LEFT) scrollbar = Scrollbar(frame_index_listbox) scrollbar.pack(side=LEFT, fill=Y) frame_index_listbox.pack() # attach listbox to scrollbar self.listbox.config(yscrollcommand=scrollbar.set) scrollbar.config(command=self.listbox.yview) f=Frame(self) Label(f,text="Filtro: ").pack(side=LEFT) self.entry_w = Entry(f) self.entry_w.pack(side=LEFT) f.pack() f2=Frame(self) Button(f2, text='Filter',command=self.filter_indexs).pack(side=LEFT) Button(f2,text='Clean Filter',command=self.clean_filter).pack(side=LEFT) Button(f2, text='Export sel for gen',command=self.export_selection).pack(side=LEFT) f2.pack() f3=Frame(self) Button(self, text='<<',command=self.next_prev(-1)).pack(side=LEFT) Button(self, text='>>',command=self.next_prev(1)).pack(side=LEFT) f3.pack() self.update_model(current_model) self.listbox.select_set(0) self.listbox.event_generate("<<ListboxSelect>>") self.listbox.bind('<<ListboxSelect>>', self.selection) def filter_indexs(self): import random path_filter = self.entry_w.get() with open(path_filter,'r') as f: indexs_list_str = f.read().strip() random.seed(3) indexs_list = list(set(indexs_list_str.split('\n'))) print(len(indexs_list)) random.shuffle(indexs_list) self.filtered_list = indexs_list self.listbox.delete(0, END) for item in indexs_list: self.listbox.insert(END, item) def clean_filter(self): self.filtered_list = None self.update_model(self.current_model) self.listbox.delete(0, END) for item in sorted(self.index_list): self.listbox.insert(END, item) self.saved_selection = None def export_selection(self): sel_files_folder = os.path.join('config_files','select_files') os.makedirs(sel_files_folder,exist_ok=True) sel_list = self.listbox.curselection() index_list = [self.listbox.get(ind) for ind in sel_list] print("Exporting list for image generator. List: {0}".format(index_list)) now_s = now_string() out_selection_file = {'index_list' : index_list, 'train_result_path': self.current_model.current_config_file, 'details' : '', 'mask_file' : self.current_model.current_mask_file} sel_file_name = "{0}_{1}_{2}_selection.json".format(self.current_model.classifier_key,self.current_model.dataset_key,now_s) sel_path = os.path.join(sel_files_folder,sel_file_name) with open(sel_path,'w') as f: json.dump(out_selection_file,f) print("Select in {0}".format(sel_path)) def update_model(self, current_model): self.model = current_model self.index_list = self.model.get_index_list() self.current_index = self.model.get_current_index() self.mask_list = self.model.get_current_mask_index_list() indexs_list = self.filtered_list if self.filtered_list else sorted(self.index_list) self.listbox.delete(0, END) for item in indexs_list: self.listbox.insert(END, item) if self.saved_selection: ind,ypos = self.saved_selection self.listbox.selection_set(ind) self.listbox.yview_moveto(ypos[0]) # go back to that position def next_prev(self,x): def selection(): ind_l = self.index_list.index(self.current_index) n = len(self.index_list) n_ind_l = (ind_l+x) % n next = self.index_list[n_ind_l] self.current_index = next self.listbox.selection_clear(0, END) self.listbox.select_set(n_ind_l) self.controller.event_change_index(next) return selection def selection(self,event): w = event.widget index = int(w.curselection()[0]) value = w.get(index) print("v: {0}".format(value)) selected_index = value self.current_index = selected_index self.controller.event_change_index(selected_index) self.saved_selection = (index,self.listbox.yview()) pass
class AutocompleteEntry(Entry): def __init__(self, autocompleteList, *args, **kwargs): # Listbox length if 'listboxLength' in kwargs: self.listboxLength = kwargs['listboxLength'] del kwargs['listboxLength'] else: self.listboxLength = 8 # Custom matches function if 'matchesFunction' in kwargs: self.matchesFunction = kwargs['matchesFunction'] del kwargs['matchesFunction'] else: def matches(fieldValue, acListEntry): pattern = re.compile('.*' + re.escape(fieldValue) + '.*', re.IGNORECASE) return re.match(pattern, acListEntry) self.matchesFunction = matches Entry.__init__(self, *args, **kwargs) self.focus() self.autocompleteList = autocompleteList self.var = self["textvariable"] if self.var == '': self.var = self["textvariable"] = StringVar() self.var.trace('w', self.changed) self.bind("<Right>", self.selection) self.bind("<Up>", self.moveUp) self.bind("<Down>", self.moveDown) self.listboxUp = False def changed(self, name, index, mode): if self.var.get() == '': if self.listboxUp: self.listbox.destroy() self.listboxUp = False else: words = self.comparison() if words: if not self.listboxUp: self.listbox = Listbox(width=self["width"], height=self.listboxLength) self.listbox.bind("<Button-1>", self.selection) self.listbox.bind("<Right>", self.selection) self.listbox.place(x=self.winfo_x(), y=self.winfo_y() + self.winfo_height()) self.listboxUp = True self.listbox.delete(0, END) for w in words: self.listbox.insert(END, w) else: if self.listboxUp: self.listbox.destroy() self.listboxUp = False def selection(self, event): if self.listboxUp: self.var.set(self.listbox.get(ACTIVE)) self.listbox.destroy() self.listboxUp = False self.icursor(END) def moveUp(self, event): if self.listboxUp: if self.listbox.curselection() == (): index = '0' else: index = self.listbox.curselection()[0] if index != '0': self.listbox.selection_clear(first=index) index = str(int(index) - 1) self.listbox.see(index) # Scroll! self.listbox.selection_set(first=index) self.listbox.activate(index) def moveDown(self, event): if self.listboxUp: if self.listbox.curselection() == (): index = '0' else: index = self.listbox.curselection()[0] if index != END: self.listbox.selection_clear(first=index) index = str(int(index) + 1) self.listbox.see(index) # Scroll! self.listbox.selection_set(first=index) self.listbox.activate(index) def comparison(self): return [ w for w in self.autocompleteList if self.matchesFunction(self.var.get(), w) ]
class TShapesWindow(Frame): """ Class represents auxiliary window containg shapes information. :param master: master window object. :type master: tkinter.Tk :param app: main app object. :type app: TApp """ def __init__(self, master, TApp): """ Call the parent class constructor and initialise object variables. """ super().__init__() self.TApp = TApp self.round_digits = TTicksSettings.ROUND_DIGITS self.init_widgets() def init_widgets(self): """ Init frame widgets. """ # Listbox self.shapes_list = Listbox(self, exportselection=False, selectmode=EXTENDED) self.grid_columnconfigure(0, weight=1, minsize=300) self.grid_rowconfigure(0, weight=1) self.shapes_list.grid(row=0, column=0, sticky=NSEW, padx=(5, 25), pady=5) self.shapes_list.bind("<<ListboxSelect>>", self.shapes_list_selected_item) # Listbox's yscrollbar self.shapes_list_scrollbar = Scrollbar(self, orient = VERTICAL, \ command = self.shapes_list.yview) self.shapes_list_scrollbar.grid(row=0, column=0, sticky=NS + E, padx=(0, 5), pady=5) self.shapes_list.config(yscrollcommand=self.shapes_list_scrollbar.set) # Drop down menu with materials self.material_box = Combobox(self, values=["pec", "free_space"]) self.material_box.grid(row=1, column=0, sticky=EW, padx=5, pady=5) self.material_box.bind("<<ComboboxSelected>>", self.assign_material_to_shape) # Right click popup menu self.init_popup_menu() self.shapes_list.bind("<Button-3>", self.show_popoup_menu) # Delete key removes selected shape(s) self.shapes_list.bind("<Delete>", self.remove_shape) def update_list(self, shapes, *, swap=False): """ Update shapes list. :param swap: shapes list selection swap toggle. :type swap: boolean """ selection = self.shapes_list.curselection() try: shape_num = selection[0] except: shape_num = 0 self.shapes_list.delete(0, END) coord_string = "" for i, single_shape in enumerate(shapes): if (single_shape.type == "Rectangle"): coord_string = "(" + str(round(single_shape.point1_mod.x, self.round_digits)) + \ ", " + str(round(single_shape.point1_mod.y, self.round_digits)) + \ "), (" + str(round (single_shape.point2_mod.x, self.round_digits)) + \ ", " + str(round(single_shape.point2_mod.y, self.round_digits)) + ")" elif (single_shape.type == "Cylinder"): coord_string = "(" + str(round(single_shape.centre_mod.x, self.round_digits)) + \ ", " + str(round(single_shape.centre_mod.y, self.round_digits)) + \ "), " + str(round(single_shape.radius_mod, self.round_digits)) elif (single_shape.type == "CylinSector"): coord_string = "(" + str(round(single_shape.centre_mod.x, self.round_digits)) + \ ", " + str(round(single_shape.centre_mod.y, self.round_digits)) + \ "), " + str(round(single_shape.radius_mod, self.round_digits)) + \ ", " + str(round(single_shape.start, self.round_digits)) + \ ", " + str(round(single_shape.extent, self.round_digits)) elif (single_shape.type == "Polygon"): coord_string = str(len(single_shape.points)) self.shapes_list.insert( i, str(i + 1) + ". " + single_shape.type + ": " + coord_string) coord_string = "" if (not swap): self.shapes_list.select_clear(0, END) if (shape_num >= self.shapes_list.size()): self.shapes_list.selection_set(shape_num - 1) self.shapes_list.activate(shape_num) else: for item in selection: self.shapes_list.selection_set(item) self.shapes_list.activate(shape_num) def assign_material_to_shape(self, event): """ Assign material to a shape. :param event: event evoking this method (listbox select). :type event: tkinter.Event """ material = self.material_box.get() try: shape_num = (self.shapes_list.curselection())[0] except: return else: if (shape_num < 0 or material == ""): return else: try: self.TApp.shapes[shape_num].material = material except Exception as message: messagebox.showerror("Material assignment error!", message) return def shapes_list_selected_item(self, event): """ Handle listbox selection event. :param event: event evoking this method (listbox select). :type event: tkinter.Event """ # Add multiple selection self.shapes_list.focus_force() try: shape_num = (self.shapes_list.curselection())[0] selection = self.shapes_list.curselection() except IndexError: return except Exception as message: messagebox.showerror("Error while picking shape!", message) if (shape_num < 0): return else: try: shape = self.TApp.shapes[shape_num] except Exception as message: messagebox.showerror("Materials list error", message) return self.material_box.set(str(shape.material)) for single_shape in self.TApp.shapes: single_shape.width = 1 for item in selection: self.TApp.shapes[item].width = 2 self.TApp.main_canvas.delete("all") for item in selection: self.shapes_list.selection_set(item) self.TApp.canvas_refresh() def init_popup_menu(self): """ Init shapes pane pup-up menu. """ self.popup_menu = Menu(self, tearoff=0) self.popup_menu.add_command(label="Edit shape", command=self.edit_shape) self.popup_menu.add_command(label="Change shape colour", command=self.change_shape_colour) self.popup_menu.add_command(label="Remove shape(s)", command=self.remove_shape) self.popup_menu.add_separator() self.popup_menu.add_command(label="Add vertex to polygon", command=self.add_vertex_to_polygon) self.popup_menu.add_separator() self.popup_menu.add_command(label="Copy shape", command=self.copy_shape) self.popup_menu.add_command(label="Paste shape", command=self.paste_shape) self.popup_menu.add_separator() self.popup_menu.add_command(label="Move up", command=self.move_shape_up) self.popup_menu.add_command(label="Move down", command=self.move_shape_down) self.popup_menu.add_command(label="Move to top", command=self.move_shape_top) self.popup_menu.add_command(label="Move to bottom", command=self.move_shape_bottom) def show_popoup_menu(self, event): """ Show shapes list pop-up menu. :param event: event evoking this method (RMB click). :type event: tkinter.Event """ try: self.popup_menu.post(event.x_root, event.y_root) finally: self.popup_menu.grab_release() def move_shape_up(self): """ Move a shape one place up the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: try: self.TApp.shapes.insert(shape_num - 1, self.TApp.shapes.pop(shape_num)) self.update_list(self.TApp.shapes) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh(swap=True) self.shapes_list.selection_set(shape_num - 1) self.shapes_list.activate(shape_num - 1) except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) return def move_shape_down(self): """ Move a shape one place down the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: try: self.TApp.shapes.insert(shape_num + 1, self.TApp.shapes.pop(shape_num)) self.update_list(self.TApp.shapes) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh(swap=True) self.shapes_list.selection_set(shape_num + 1) self.shapes_list.activate(shape_num + 1) except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) return def move_shape_top(self): """ Move a shape to the top of the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: try: self.TApp.shapes.insert(0, self.TApp.shapes.pop(shape_num)) self.update_list(self.TApp.shapes) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh(swap=True) self.shapes_list.selection_set(0) self.shapes_list.activate(0) # self.shapes_list.focus_set () except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) return def move_shape_bottom(self): """ Move a shape to the bottom of the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: try: self.TApp.shapes.append(self.TApp.shapes.pop(shape_num)) self.update_list(self.TApp.shapes) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh(swap=True) self.shapes_list.selection_set(END) self.shapes_list.activate(END) except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) return def edit_shape(self): """ Edit selected shape on the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: self.TApp.operations.append(TOperation("edit", shape = \ deepcopy(self.TApp.shapes[shape_num]), \ num = shape_num)) if (self.TApp.shapes[shape_num].type == "Rectangle"): self.TApp.edit_rectangle(shape_num) elif (self.TApp.shapes[shape_num].type == "Cylinder"): self.TApp.edit_cylin(shape_num) elif (self.TApp.shapes[shape_num].type == "CylinSector"): self.TApp.edit_cylin_sector(shape_num) elif (self.TApp.shapes[shape_num].type == "Polygon"): self.TApp.edit_polygon(shape_num) def change_shape_colour(self): """ Change selected shape on the shapes list colour. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return if (shape_num < 0): return else: self.TApp.change_shape_colour(shape_num=shape_num) def remove_shape(self, event=None): """ Remove selected shape on the shapes list. """ try: selection = self.shapes_list.curselection() except IndexError: return if (len(selection) == 0): return else: try: for item in reversed(selection): del self.TApp.shapes[item] self.update_list(self.TApp.shapes) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh() except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) return def add_vertex_to_polygon(self): """ Add a vertex to selected polygon on the shapes list. """ try: shape_num = (self.shapes_list.curselection())[0] except IndexError: return input_str = simpledialog.askstring("Input coordinates", "Give mew vertex's coordinates") point_mod_x, point_mod_y = [float(val) for val in input_str.split()] if (self.TApp.shapes[shape_num].type == "Polygon" and shape_num > -1): self.TApp.shapes[shape_num].add_vertex(x_mod=point_mod_x, y_mod=point_mod_y) self.TApp.main_canvas.delete("all") self.TApp.canvas_refresh() def copy_shape(self): """ Copy selected shape on the shapes list. """ try: shape_num = self.shapes_list.curselection()[0] except: shape_num = -1 if (shape_num > -1): try: self.TApp.copy_shape(shape_num=shape_num) except Exception as message: messagebox.showerror("Error while manipulating shapes list!", message) def paste_shape(self, *, deltax=15, deltay=15): """ Paste selected shape from buffer. :param deltax: pasted shape offset in x direction in pixels. :type deltax: integer :param deltay: pasted shape offset in y direction in pixels. :type deltay: integer """ self.TApp.paste_ctrl_v(Event())
class Application(Frame): """Container class, encapsulates app""" # this class inherits from Tkinter.parent def __init__(self, path, master=None): """Constructor""" # call parent class constructor Frame.__init__(self, master) self.path = path self.image_filenames = find_image_files(path) # necessary to make the application actually appear on the screen self.grid(sticky=N + S + E + W) # PIL image, for loading from file and for resizing self.image_pil = None # Tk photoimage, for display self.image_tk = None # list of coords (2-element lists) of current selection's pts self.points_orig = [] # points_canvas is 'points_orig', in canvas coordinates self.points_canvas = [] # x- and y-coords of displayed image in canvas coordinate frame self.x_offset = -1 self.y_offset = -1 # font in listbox text self.font = Font(family='Helvetica', size=10, weight='normal') # crosshair line size , as fraction of # min(displayed-imagewidth, displayed-image-height) self.crosshair_fraction = 0.05 # color for drawing first crosshair - the origin self.crosshair1_color = 'red' # color for drawing second crosshair - (together with the first # crosshair, these two points define a coordinate frame) - self.crosshair2_color = 'green' # color for drawing third and on crosshairs - all points other # than the first and second, have this color self.crosshair3_color = 'cyan' # the width, in pixels, of crosshairs self.crosshair_thickness = 2 # length of crosshair (updated upon display) self.crosshair_radius = -1 # the scale of currently displayed image (updated upon display) self.image_scaling = 1.0 # create all widges and set their initial conditions self.create_widgets() def create_widgets(self): """Set up all application graphics""" # get the top level winddow top = self.winfo_toplevel() # set the title of the top level window top.title('Image Point Tagging Tool') # make row 0 of the top level window's grid stretchable top.rowconfigure(0, weight=1) # make column 0 of the top level window's grid stretchable top.columnconfigure(0, weight=1) # bind keys for entire app top.bind_all('<Up>', self.select_prev) top.bind_all('<Down>', self.select_next) # make row 0 of Application's widget's grid stretchable self.rowconfigure(0, weight=1) # make column 0 of Application's widget's grid stretchable self.columnconfigure(0, weight=1) self.canvas = Canvas(self, bg='gray') self.canvas.grid(row=0, column=0, rowspan=2, sticky=N + S + E + W) self.canvas.rowconfigure(0, weight=1) self.canvas.columnconfigure(0, weight=1) # bind resize events (need -4 here bec. event gives 4+(real_size)) self.canvas.bind( '<Configure>', lambda e, s=self: s.on_resize_canvas(e.width - 2, e.height - 2)) # bind canvas mouse clicks self.canvas.bind('<Button-1>', self.on_click_button1) self.canvas.bind('<Button-3>', self.on_click_button3) # create scrollbars self.scrollbar_x = Scrollbar(self, orient=HORIZONTAL, width=10) self.scrollbar_y = Scrollbar(self, orient=VERTICAL, width=10) self.scrollbar_x.grid(row=1, column=1, columnspan=2, sticky=E + W) self.scrollbar_y.grid(row=0, column=3, sticky=N + S) # create lb for showing labeled/not-labeled images self.listbox_marks = Listbox(self, width=1, takefocus=0, exportselection=0, font=self.font) self.listbox_marks.grid(row=0, column=1, sticky=N + S + E + W) # create lb for showing image filenames self.lisbox_filenames = Listbox(self, width=30, selectmode=SINGLE, xscrollcommand=self.scrollbar_x.set, yscrollcommand=self.scrollbar_y.set, exportselection=0, font=self.font) self.lisbox_filenames.grid(row=0, column=2, sticky=N + S + E + W) # bind scrollbar movement self.scrollbar_x['command'] = self.lisbox_filenames.xview self.scrollbar_y['command'] = self.on_scrollbar_y # bind left mouse click selection self.lisbox_filenames.bind( '<Button-1>', lambda e, s=self: s.select(self.lisbox_filenames.nearest(e.y))) self.listbox_marks.bind( '<Button-1>', lambda e, s=self: s.select(self.lisbox_filenames.nearest(e.y))) # bind wheel scroll self.lisbox_filenames.bind( '<Button-4>', lambda e, s=self: on_mousewheel(self.listbox_marks, 4)) self.lisbox_filenames.bind( '<Button-5>', lambda e, s=self: on_mousewheel(self.listbox_marks, 5)) self.listbox_marks.bind( '<Button-4>', lambda e, s=self: on_mousewheel(self.lisbox_filenames, 4)) self.listbox_marks.bind( '<Button-5>', lambda e, s=self: on_mousewheel(self.lisbox_filenames, 5)) # skip is # of chars to skip in path string so that only the # part of the path that was not supplied is displayed skip = len(self.path) if self.path[skip - 1] != '/': skip += 1 # insert image filenames plus marks into lists and # select first image that does not have pts file i = 0 index_of_image_with_no_pts_file = -1 for image_filename in self.image_filenames: self.lisbox_filenames.insert(END, image_filename[skip:]) if self.has_pts_file(i): self.listbox_marks.insert(END, '+') else: self.listbox_marks.insert(END, '') if index_of_image_with_no_pts_file < 0: index_of_image_with_no_pts_file = i i += 1 if index_of_image_with_no_pts_file < 0: self.select(0) else: self.select(index_of_image_with_no_pts_file) def on_scrollbar_y(self, *args): """Vertical scrollbar motion callback""" apply(self.lisbox_filenames.yview, args) apply(self.listbox_marks.yview, args) def on_click_button1(self, event): """Button 1 click callback: adds a crosshair at click location""" if self.coord_in_img(event.x, event.y): point = [(event.x - self.x_offset) / self.image_scaling, (event.y - self.y_offset) / self.image_scaling] point_scaled = [float(event.x), float(event.y)] self.points_orig.append(point) self.points_canvas.append(point_scaled) if len(self.points_orig) == 1: self.mark_labeled() self.on_resize_canvas(int(self.canvas['width']), int(self.canvas['height'])) self.save_points() def on_click_button3(self, event): """Button 3 click callback: deletes landmark near click location""" if not self.coord_in_img(event.x, event.y): return i = self.find_point_near_crosshair(event.x, event.y) if i >= 0: del self.points_orig[i] del self.points_canvas[i] if len(self.points_orig) == 0: self.mark_unlabeled() self.on_resize_canvas(int(self.canvas['width']), int(self.canvas['height'])) self.save_points() def select(self, i): """Select the i'th image to work with - make current selection = i""" # uncomment the following line if you are only dealing with # faces that have three points labeled on them and you want to # automatically reorder a previously tagged database so that # the person's right eye is the first point, left eye is # second point and mouth is third point self.sort_points() self.lisbox_filenames.selection_clear(0, END) self.listbox_marks.selection_clear(0, END) self.lisbox_filenames.selection_set(i) self.listbox_marks.selection_set(i) self.lisbox_filenames.see(i) self.listbox_marks.see(i) self.image_pil = PIL.Image.open(self.get_image_filename()) self.points_orig = self.read_pts_file() self.on_resize_canvas(int(self.canvas['width']), int(self.canvas['height'])) def select_prev(self, *args): #pylint: disable=unused-argument """Select entry that comes before current selection""" i = self.get_selected_index() if i > 0: self.select(i - 1) def select_next(self, *args): #pylint: disable=unused-argument """Select entry that comes after current selection""" i = self.get_selected_index() if i < len(self.image_filenames) - 1: self.select(i + 1) def on_resize_canvas(self, width, height): """Called when canvas is resized""" if width <= 0 or height <= 0: return # maximize image width or height depending on aspect ratios image_width = self.image_pil.size[0] image_height = self.image_pil.size[1] image_aspect_ratio = float(image_width) / float(image_height) self.canvas['width'] = width self.canvas['height'] = height canvas_width = int(self.canvas['width']) canvas_height = int(self.canvas['height']) canvas_aspect_ratio = float(canvas_width) / float(canvas_height) if image_aspect_ratio < canvas_aspect_ratio: new_image_width = int(image_aspect_ratio * float(canvas_height)) new_image_height = canvas_height else: new_image_width = canvas_width new_image_height = int(float(canvas_width) / image_aspect_ratio) self.image_tk = PhotoImage( self.image_pil.resize((new_image_width, new_image_height), PIL.Image.BILINEAR)) self.x_offset = 0.5 * (float(canvas_width) - float(new_image_width)) self.y_offset = 0.5 * (float(canvas_height) - float(new_image_height)) self.crosshair_radius = 0.5 * self.crosshair_fraction * float( min(new_image_width, new_image_height)) self.canvas.delete('image') self.canvas.create_image(self.x_offset, self.y_offset, anchor=NW, image=self.image_tk, tags='image') width_scale = float(new_image_width) / float(image_width) height_scale = float(new_image_height) / float(image_height) self.image_scaling = 0.5 * (width_scale + height_scale) self.points_canvas = [[ x[0] * self.image_scaling + self.x_offset, x[1] * self.image_scaling + self.y_offset ] for x in self.points_orig] self.redraw_points() def redraw_points(self): """redraw points in current entry's .pts file""" self.canvas.delete('line') # draw first crosshair in color1 if len(self.points_canvas) > 0: point1 = self.points_canvas[0] self.draw_crosshair(point1[0], point1[1], self.crosshair1_color) # draw second crosshair in color2 if len(self.points_canvas) > 1: point2 = self.points_canvas[1] self.draw_crosshair(point2[0], point2[1], self.crosshair2_color) # draw third or higher crosshair in color3 if len(self.points_canvas) > 2: for point in self.points_canvas[2:]: self.draw_crosshair(point[0], point[1], self.crosshair3_color) def draw_crosshair(self, x_coord, y_coord, fill_color): """Draw a cross at (x_coord, y_coord) in the currently selected image""" start_x = x_coord - self.crosshair_radius start_y = y_coord - self.crosshair_radius end_x = x_coord + self.crosshair_radius end_y = y_coord + self.crosshair_radius min_x = self.x_offset min_y = self.y_offset max_x = self.x_offset + self.image_tk.width() - 1 max_y = self.y_offset + self.image_tk.height() - 1 if start_x < min_x: start_x = min_x if start_y < min_y: start_y = min_y if end_x > max_x: end_x = max_x if end_y > max_y: end_y = max_y self.canvas.create_line(x_coord, start_y, x_coord, end_y, width=self.crosshair_thickness, tags='line', fill=fill_color) self.canvas.create_line(start_x, y_coord, end_x, y_coord, width=self.crosshair_thickness, tags='line', fill=fill_color) def get_selected_index(self): """Returns index of current selection""" return int(self.lisbox_filenames.curselection()[0]) def coord_in_img(self, x_coord, y_coord): """Returns whether (x_coord, y_coord) is inside the shown image""" return (x_coord >= self.x_offset and y_coord >= self.y_offset and x_coord < self.x_offset + self.image_tk.width() and y_coord < self.y_offset + self.image_tk.height()) def find_point_near_crosshair(self, x_coord, y_coord): """Returns index of landmark point near (x_coord, y_coord), or -1""" i = 0 i_min = -1 min_dist = self.image_tk.width() + self.image_tk.height() for pair in self.points_canvas: x_dist = x_coord - pair[0] y_dist = y_coord - pair[1] dist = sqrt(x_dist * x_dist + y_dist * y_dist) if dist <= self.crosshair_radius and dist < min_dist: i_min = i i += 1 return i_min def save_points(self): """Save current points to pts file""" # remove whatever was there before if self.has_pts_file(): os.remove(self.get_pts_filename()) # save current result if len(self.points_orig) > 0: filehandle = open(self.get_pts_filename(), 'w') for pair in self.points_orig: message = str(pair[0]) + ', ' + str(pair[1]) + '\n' filehandle.write(message) filehandle.close() def sort_points(self): """ Reorder points, assuming face labeling, so that the first point is always the person's right eye, the second point is the person's left eye and the third point is the mouth. NB: this function only (destructively) works on self.points_orig """ if len(self.points_orig) != 3: return # step 1 sort the points according to y-value self.points_orig.sort(key=lambda pt: pt[1]) # step 2: from the top-most two points, call the leftmost one # the person's right eye and call the other the person's left eye if self.points_orig[0][0] > self.points_orig[1][0]: # swap first and second points' x-coordinate tmp = self.points_orig[0][0] self.points_orig[0][0] = self.points_orig[1][0] self.points_orig[1][0] = tmp # swap first and second points' y-coordinate tmp = self.points_orig[0][1] self.points_orig[0][1] = self.points_orig[1][1] self.points_orig[1][1] = tmp # order changed, so re-save self.save_points() def has_pts_file(self, i=None): """Returns whether (i'th) selection has a pts file with landmarks""" if i is None: i = self.get_selected_index() return os.path.exists(self.get_pts_filename(i)) def get_pts_filename(self, i=None): """Returns filename of selected (or i'th) .pts file""" if i is None: i = self.get_selected_index() image_filename = self.image_filenames[i] return os.path.splitext(image_filename)[0] + '.pts' def get_image_filename(self, i=None): """Returns filename of (i'th) selection's image""" if i is None: i = self.get_selected_index() return self.image_filenames[i] def read_pts_file(self, i=None): """Returns list of points (lists) in (i'th) selection's .pts file""" if i is None: i = self.get_selected_index() if self.has_pts_file(i): filehandle = open(self.get_pts_filename(i), 'r') lines = filehandle.readlines() filehandle.close() return [[float(pair[0]), float(pair[1])] for pair in [line.split(',') for line in lines]] else: return [] def mark_labeled(self, i=None): """Mark (i'th) selection as having a .pts file""" if i is None: i = self.get_selected_index() self.listbox_marks.insert(i, '+') self.listbox_marks.delete(i + 1) self.listbox_marks.selection_set(i) def mark_unlabeled(self, i=None): """Unmark (i'th) selection as having a .pts file""" if i is None: i = self.get_selected_index() self.listbox_marks.insert(i, '') self.listbox_marks.delete(i + 1) self.listbox_marks.selection_set(i)
class LogUI(Frame): def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.filemodels = [] filemodel1 = FileModel("C:/Users/chen_xi/test1.csv", searchconds=[], relation="and", joincondtuples=[]) filemodel2 = FileModel("C:/Users/chen_xi/test2.csv", searchconds=[], relation="and", joincondtuples=[]) self.filemodels = [filemodel1, filemodel2] self._initUI() self.selectedfileindex = -1 def _initUI(self): self.parent.title("Log Processor") self.pack() self._initfilepanel() self._initsearchcondpanel() self._initjoincondpanel() self._initconsole() self._inflatefilelist() def _initfilepanel(self): frame = Frame(self) frame.grid(row=0, column=0, sticky=E + W + S + N) label = Label(frame, text="File List: ") label.grid(sticky=N + W) self.filelist = Listbox(frame, width=40) self.filelist.grid(row=1, column=0, rowspan=2, columnspan=3) vsl = Scrollbar(frame, orient=VERTICAL) vsl.grid(row=1, column=3, rowspan=2, sticky=N + S + W) hsl = Scrollbar(frame, orient=HORIZONTAL) hsl.grid(row=3, column=0, columnspan=3, sticky=W + E + N) self.filelist.config(yscrollcommand=vsl.set, xscrollcommand=hsl.set) self.filelist.bind('<<ListboxSelect>>', self._onfilelistselection) hsl.config(command=self.filelist.xview) vsl.config(command=self.filelist.yview) upbtn = Button(frame, text="Up", width=7, command=self._upfile) upbtn.grid(row=1, column=4, padx=5, pady=5) downbtn = Button(frame, text="Down", width=7, command=self._downfile) downbtn.grid(row=2, column=4, padx=5, pady=5) newbtn = Button(frame, text="New", width=7, command=self._addfile) newbtn.grid(row=4, column=1, pady=5, sticky=E + S) delbtn = Button(frame, text="Delete", width=7, command=self._deletefile) delbtn.grid(row=4, column=2, padx=5, pady=5, sticky=W + S) def _inflatefilelist(self): self.filelist.delete(0, END) for filemodel in self.filemodels: self.filelist.insert(END, filemodel.filename) def _initsearchcondpanel(self): frame = Frame(self) frame.grid(row=0, column=1, sticky=E + W + S + N, padx=5) label = Label(frame, text="Search Condition: ") label.grid(row=0, column=0, columnspan=1, sticky=W) relationlable = Label(frame, text="Relation") relationlable.grid(row=0, column=1, columnspan=1, sticky=E) self.condrelationvar = StringVar(frame) relationinput = Combobox(frame, textvariable=self.condrelationvar, values=["and", "or"]) relationinput.grid(row=0, column=2, padx=5, sticky=E) relationinput.bind('<<ComboboxSelected>>', self._onrelationchange) self.searchcondlist = Listbox(frame) self.searchcondlist.grid(row=1, rowspan=1, columnspan=3, sticky=E + W + S + N) vsl = Scrollbar(frame, orient=VERTICAL) vsl.grid(row=1, column=3, rowspan=1, sticky=N + S + W) hsl = Scrollbar(frame, orient=HORIZONTAL) hsl.grid(row=2, column=0, columnspan=3, sticky=W + E + N) self.searchcondlist.config(yscrollcommand=vsl.set, xscrollcommand=hsl.set) hsl.config(command=self.searchcondlist.xview) vsl.config(command=self.searchcondlist.yview) newbtn = Button(frame, text="New", width=7, command=self._addsearchcondition) newbtn.grid(row=3, column=0, padx=5, pady=5, sticky=E) delbtn = Button(frame, text="Delete", width=7, command=self._deletesearchcondition) delbtn.grid(row=3, column=1, sticky=E) modbtn = Button(frame, text="Update", width=7, command=self._modifysearchcondition) modbtn.grid(row=3, column=2, padx=5, pady=5, sticky=W) def _onrelationchange(self, evt): selectedmodel = self._getselectedfile() selectedmodel.relation = self.condrelationvar.get() def _inflatesearchcondlist(self, filemodel): self.condrelationvar.set(filemodel.relation) conds = filemodel.searchconds self.searchcondlist.delete(0, END) for cond in conds: self.searchcondlist.insert(END, cond.tostring()) def _initjoincondpanel(self): frame = Frame(self) frame.grid(row=0, column=2, sticky=E + W + S + N, padx=5) label = Label(frame, text="Join Condition: ") label.grid(sticky=N + W) self.joincondlist = Listbox(frame) self.joincondlist.grid(row=1, rowspan=1, columnspan=3, sticky=E + W + S + N) vsl = Scrollbar(frame, orient=VERTICAL) vsl.grid(row=1, column=3, rowspan=1, sticky=N + S + W) hsl = Scrollbar(frame, orient=HORIZONTAL) hsl.grid(row=2, column=0, columnspan=3, sticky=W + E + N) self.joincondlist.config(yscrollcommand=vsl.set, xscrollcommand=hsl.set) hsl.config(command=self.joincondlist.xview) vsl.config(command=self.joincondlist.yview) newbtn = Button(frame, text="New", width=7, command=self._addjoincondition) newbtn.grid(row=3, column=0, padx=5, pady=5, sticky=E) delbtn = Button(frame, text="Delete", width=7, command=self._deletejoincondition) delbtn.grid(row=3, column=1, sticky=E) modbtn = Button(frame, text="Update", width=7, command=self._modifyjoincondition) modbtn.grid(row=3, column=2, padx=5, pady=5, sticky=W) def _inflatejoincondlist(self, condtuples): self.joincondlist.delete(0, END) for condtuple in condtuples: cond = condtuple[0] tofilename = condtuple[1] self.joincondlist.insert(END, cond.tostring() + " in " + tofilename) def _initconsole(self): separator = Separator(self, orient=HORIZONTAL) separator.grid(row=1, columnspan=3, sticky=W + E, padx=5, pady=5) self.console = Text(self) self.console.grid(row=2, columnspan=3, sticky=W + E, padx=5, pady=5) vsl = Scrollbar(self, orient=VERTICAL) vsl.grid(row=2, column=3, sticky=N + S + W) hsl = Scrollbar(self, orient=HORIZONTAL) hsl.grid(row=3, column=0, columnspan=3, sticky=W + E + N) hsl.config(command=self.console.xview) vsl.config(command=self.console.yview) resbtn = Button(self, text="Search", width=7, comman=self._showsearchresult) resbtn.grid(row=4, column=2, padx=5, pady=5, sticky=E) def _showsearchresult(self): try: res = self._searchresult() formatres = self._formatsearchresult(res) except Exception: formatres = "Error!\r\n" + traceback.format_exc() self.console.delete("0.0", END) self.console.insert("0.0", formatres) def _searchresult(self): filesearchs = [] joinsearchs = [] for filemodel in self.filemodels: filename = filemodel.filename singlesearch = SingleFileSearch(filename, DictReader(open(filename)), filemodel.searchconds, filemodel.relation) filesearchs.append(singlesearch) joindict = {} for joincondtuple in filemodel.joincondtuples: tofilename = joincondtuple[1] joincond = joincondtuple[0] if tofilename not in joindict: joindict[tofilename] = [] joindict[tofilename].append(joincond) for tofilename in joindict: joinsearch = JoinFileSearch(filename, DictReader(open(filename)), tofilename, DictReader(open(tofilename)), joindict[tofilename]) joinsearchs.append(joinsearch) search = Search(filesearchs, joinsearchs) return search.process() def _formatsearchresult(self, searchresult): formatres = self._formatsummary(searchresult) + "\r\n" fileresults = searchresult.results for filemodel in self.filemodels: filename = filemodel.filename fileresult = fileresults[filename] formatres += self._formatfileresult(fileresult) formatres += "\r\n" return formatres def _formatsummary(self, searchresult): res = "Summary\r\n" res += "Time Cost: " + str(searchresult.timecost) + " Seconds\r\n" fileresults = searchresult.results for filemodel in self.filemodels: filename = filemodel.filename fileresult = fileresults[filename] res += filename + " Size: " + str(len(fileresult.result)) + "\r\n" return res def _formatfileresult(self, fileresult): res = "" filename = fileresult.filename res += filename + " Size: " + str(len(fileresult.result)) + "\r\n" fields = csvhandler.getfields(filename) for (i, field) in enumerate(fields): res += field if i < (len(fields) - 1): res += "," else: res += "\r\n" for rowdict in fileresult.result: for (i, field) in enumerate(fields): res += rowdict[field] if i < (len(fields) - 1): res += "," else: res += "\r\n" return res def _addfile(self): filetypes = [('csv files', '*.csv'), ('All files', '*')] selectedfile = askopenfilename(filetypes=filetypes) if selectedfile is not None: newmodel = FileModel(selectedfile, searchconds=[], relation="and", joincondtuples=[]) self.filemodels.append(newmodel) self.filelist.insert(END, newmodel.filename) self._setselectedfileindex(len(self.filemodels) - 1) def _deletefile(self): index = self._getselectedfileindex() if index >= 0: self.filelist.delete(index) del self.filemodels[index] self._setselectedfileindex(-1) def _upfile(self): if self._getselectedfileindex() <= 0: return index = self._getselectedfileindex() selectedfilename = self._getselectedfile().filename if index > 0: self.filelist.insert((index - 1), selectedfilename) self.filelist.delete(index + 1) self.filemodels[index - 1], self.filemodels[index] = self.filemodels[index], self.filemodels[index - 1] self._setselectedfileindex(index - 1) def _downfile(self): if self._getselectedfileindex() < 0: return index = self._getselectedfileindex() selectedfilename = self._getselectedfile().filename if index < (len(self.filemodels) - 1): self.filelist.insert((index + 2), selectedfilename) self.filelist.delete(index) self.filemodels[index], self.filemodels[index + 1] = self.filemodels[index + 1], self.filemodels[index] self._setselectedfileindex(index + 1) def _onfilelistselection(self, evt): if len(self.filelist.curselection()) == 0: return self._setselectedfileindex(self.filelist.curselection()[0]) selectedfile = self._getselectedfile() self._inflatesearchcondlist(selectedfile) joincondtuples = selectedfile.joincondtuples self._inflatejoincondlist(joincondtuples) def _addsearchcondition(self): if self._getselectedfileindex() < 0: return self._popupsearchcondwindow() def _deletesearchcondition(self): index = self._getselectedsearchcondindex() if index < 0: return selectedfile = self._getselectedfile() del selectedfile.searchconds[index] self.searchcondlist.delete(index) def _modifysearchcondition(self): if self._getselectedsearchcondindex() < 0: return self._popupsearchcondwindow(self._getselectedsearchcondindex()) def _addjoincondition(self): if self._getselectedfileindex() < 0: return self._popupjoincondwindow() def _deletejoincondition(self): index = self._getselectedjoincondindex() if index < 0: return selectedfile = self._getselectedfile() del selectedfile.joincondtuples[index] self.joincondlist.delete(index) def _modifyjoincondition(self): if self._getselectedjoincondindex() < 0: return self._popupjoincondwindow(self._getselectedjoincondindex()) def _popupsearchcondwindow(self, index=-1): if index < 0: cond = ValueSearchCondition("", "") else: cond = self._getselectedfile().searchconds[index] window = Toplevel(self) title = Label(window, text="New Search Condition") title.grid(row=0, column=0, padx=5, pady=5, sticky=W + N) fieldlabel = Label(window, text="Field Name: ") fieldlabel.grid(row=1, column=0, padx=5, pady=5, sticky=W) fields = csvhandler.getfields(self._getselectedfile().filename) fieldvar = StringVar(window) fieldinput = Combobox(window, textvariable=fieldvar, values=fields, width=20) fieldinput.grid(row=1, column=1, columnspan=2, padx=5, pady=5, sticky=W) valuelabel = Label(window, text="Value: ") valuelabel.grid(row=3, column=0, padx=5, pady=5, sticky=W) valueinput = Entry(window) valueinput.grid(row=3, column=1, columnspan=2, padx=5, pady=5, sticky=W) minlabel = Label(window, text="Min Value: ") minlabel.grid(row=4, column=0, padx=5, pady=5, sticky=W) mininput = Entry(window) mininput.grid(row=4, column=1, columnspan=2, padx=5, pady=5, sticky=W) maxlabel = Label(window, text="Max Value: ") maxlabel.grid(row=5, column=0, padx=5, pady=5, sticky=W) maxinput = Entry(window) maxinput.grid(row=5, column=1, columnspan=2, padx=5, pady=5, sticky=W) sarchkind = IntVar() def _enablesingle(): valueinput.config(state=NORMAL) mininput.config(state=DISABLED) maxinput.config(state=DISABLED) singlebutton.select() def _enablejoin(): valueinput.config(state=DISABLED) mininput.config(state=NORMAL) maxinput.config(state=NORMAL) joinbutton.select() typelabel = Label(window, text="Search Type: ") typelabel.grid(row=2, column=0, padx=5, pady=5, sticky=W) singlebutton = Radiobutton(window, text="Single", variable=sarchkind, value=1, command=_enablesingle) singlebutton.grid(row=2, column=1, columnspan=1, padx=5, pady=5, sticky=W) joinbutton = Radiobutton(window, text="Range", variable=sarchkind, value=2, command=_enablejoin) joinbutton.grid(row=2, column=2, columnspan=1, padx=5, pady=5, sticky=W) # init value fieldvar.set(cond.field) if isinstance(cond, ValueSearchCondition): valueinput.insert(0, cond.val) _enablesingle() elif isinstance(cond, RangeSearchCondition): mininput.insert(0, cond.valmin) maxinput.insert(0, cond.valmax) _enablejoin() def _newcond(): '''create new condition ''' if sarchkind.get() == 1: cond = ValueSearchCondition(fieldvar.get(), valueinput.get()) else: cond = RangeSearchCondition(fieldvar.get(), mininput.get(), maxinput.get()) selectedfile = self._getselectedfile() if index < 0: selectedfile.searchconds.append(cond) else: del selectedfile.searchconds[index] selectedfile.searchconds[index:index] = [cond] self._inflatesearchcondlist(selectedfile) window.destroy() okbtn = Button(window, text="Confirm", width=7, command=_newcond) okbtn.grid(row=6, column=1, rowspan=1, columnspan=1, sticky=E, padx=5, pady=5) clsbtn = Button(window, text="Close", width=7, command=lambda: window.destroy()) clsbtn.grid(row=6, column=2, rowspan=1, columnspan=1, sticky=E, padx=5, pady=5) def _popupjoincondwindow(self, index=-1): if index < 0: cond = JoinSearchCondition(("", "")) tofilename = "" else: condtuple = self._getselectedfile().joincondtuples[index] cond = condtuple[0] tofilename = condtuple[1] window = Toplevel(self) title = Label(window, text="New Search Condition") title.grid(row=0, column=0, padx=5, pady=5, sticky=W + N) filenamelabel = Label(window, text="Target Field Name: ") filenamelabel.grid(row=1, column=0, padx=5, pady=5, sticky=W) filevar = StringVar(window) filenameinput = Combobox(window, textvariable=filevar, values=self.filelist.get(0, END), width=30) filenameinput.grid(row=1, column=1, columnspan=2, padx=5, pady=5, sticky=W) fromfieldlabel = Label(window, text="Field in From File: ") fromfieldlabel.grid(row=3, column=0, padx=5, pady=5, sticky=W) fromfields = csvhandler.getfields(self._getselectedfile().filename) fromfieldvar = StringVar(window) fieldinput = Combobox(window, textvariable=fromfieldvar, values=fromfields, width=20) fieldinput.grid(row=3, column=1, columnspan=2, padx=5, pady=5, sticky=W) tofieldlabel = Label(window, text="Field in Target File: ") tofieldlabel.grid(row=4, column=0, padx=5, pady=5, sticky=W) tofields = [] tofieldvar = StringVar(window) tofieldinput = Combobox(window, textvariable=tofieldvar, values=tofields, width=20) tofieldinput.grid(row=4, column=1, columnspan=2, padx=5, pady=5, sticky=W) def updatetofieldinput(evt): if filevar.get() is not None and len(filevar.get()) > 0: tofields = csvhandler.getfields(filevar.get()) window.grid_slaves(4, 1)[0].grid_forget() tofieldinput = Combobox(window, textvariable=tofieldvar, values=tofields, width=20) tofieldinput.grid(row=4, column=1, columnspan=2, padx=5, pady=5, sticky=W) filenameinput.bind('<<ComboboxSelected>>', updatetofieldinput) # init value filevar.set(tofilename) fromfieldvar.set(cond.fieldtuple[0]) updatetofieldinput(None) tofieldvar.set(cond.fieldtuple[1]) def _newcond(): '''create new condition ''' cond = JoinSearchCondition((fromfieldvar.get(), tofieldvar.get())) tofilename = filevar.get() selectedfile = self._getselectedfile() if index < 0: selectedfile.joincondtuples.append((cond, tofilename)) else: del selectedfile.joincondtuples[index] selectedfile.joincondtuples[index:index] = [(cond, tofilename)] self._inflatejoincondlist(selectedfile.joincondtuples) window.destroy() okbtn = Button(window, text="Confirm", width=7, command=_newcond) okbtn.grid(row=6, column=1, rowspan=1, columnspan=1, sticky=E, padx=5, pady=5) clsbtn = Button(window, text="Close", width=7, command=lambda: window.destroy()) clsbtn.grid(row=6, column=2, rowspan=1, columnspan=1, sticky=W, padx=5, pady=5) def _getselectedfile(self): if self._getselectedfileindex() < 0: return None return self.filemodels[self._getselectedfileindex()] def _getselectedfileindex(self): return self.selectedfileindex def _setselectedfileindex(self, index): self.selectedfileindex = index if index >= 0: self.filelist.selection_set(index) def _getselectedsearchcondindex(self): if len(self.searchcondlist.curselection()) > 0: return self.searchcondlist.curselection()[0] return -1 def _getselectedjoincondindex(self): if len(self.joincondlist.curselection()) > 0: return self.joincondlist.curselection()[0] return -1
class Window: FONT = ('Monospace', 11) ITEM_HEIGHT = 22 MAX_FOUND = 10 BG_COLOR = '#202b3a' FG_COLOR = '#ced0db' def resize(self, items): if self.resized: return self.root.geometry('{0}x{1}'.format( self.width, self.height + items * Window.ITEM_HEIGHT)) self.resized = True def __init__(self, root, width, height): self.root = root self.width = width self.height = height self.all_windows = [] self.resized = False # master.geometry(500) root.title("window switcher") root.resizable(width=False, height=False) root.configure(background=Window.BG_COLOR) # ugly tkinter code below sv = StringVar() sv.trace("w", lambda name, index, mode, sv=sv: self.on_entry(sv)) self.main_entry = Entry(root, font=Window.FONT, width=1000, textvariable=sv, bg=Window.BG_COLOR, fg=Window.FG_COLOR, insertbackground=Window.FG_COLOR, bd=0) self.main_entry.grid(row=0, column=0, padx=10) self.main_entry.focus_set() self.listbox = Listbox(root, height=Window.ITEM_HEIGHT, font=Window.FONT, highlightthickness=0, borderwidth=0, bg=Window.BG_COLOR, fg=Window.FG_COLOR, selectbackground='#2c3c51', selectforeground='#cedaed') self.listbox.grid(row=1, column=0, sticky='we', padx=10, pady=10) # key bindings self.main_entry.bind('<Control-a>', self.select_all) self.main_entry.bind('<Up>', self.select_prev) self.main_entry.bind('<Down>', self.select_next) self.main_entry.bind('<Return>', self.select_window) self.root.bind('<Escape>', lambda e: sys.exit()) # self.resize(Window.MAX_FOUND) self.initial_get(None) def initial_get(self, event): self.all_windows = get_windows() self.find_windows('') def select_all(self, event): # select text self.main_entry.select_clear() self.main_entry.select_range(0, 'end') # move cursor to the end self.main_entry.icursor('end') return 'break' def find_windows(self, text): text = text.lower() found = [ window for window in self.all_windows if window['name'].find(text) != -1 ] # print(found) self.found = found self.listbox.delete(0, 'end') for i, item in enumerate(found): if i >= Window.MAX_FOUND: break self.listbox.insert('end', item['name']) self.resize(min(len(found), Window.MAX_FOUND)) # select first element self.listbox.selection_set(0) def select_next(self, event): if len(self.found) == 0: return idx = self.listbox.curselection()[0] max = self.listbox.size() idx += 1 if idx >= max: idx = 0 self.listbox.selection_clear(0, 'end') self.listbox.selection_set(idx) def select_prev(self, event): if len(self.found) == 0: return idx = self.listbox.curselection()[0] max = self.listbox.size() idx -= 1 if idx < 0: idx = max - 1 self.listbox.selection_clear(0, 'end') self.listbox.selection_set(idx) def select_window(self, event): idx = self.listbox.curselection()[0] id = self.found[idx]['id'] # switch to window and exit # wmctrl -ia <id`> subprocess.call(['wmctrl', '-ia', id]) # print(subprocess.check_output(['wmctrl', '-ia', id]).decode('utf-8')) sys.exit(0) def on_entry(self, newtext): search_test = newtext.get() self.find_windows(search_test) return True
class InstanceEditor(Toplevel): def __init__(self): Toplevel.__init__(self) self.focus_set() self.grab_set() self.result = None self.module_data = None self.mod_applis = None self.title(ugettext("Instance editor")) self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=1) self.ntbk = ttk.Notebook(self) self.ntbk.grid(row=0, column=0, columnspan=1, sticky=(N, S, E, W)) self.frm_general = Frame(self.ntbk, width=350, height=150) self.frm_general.grid_columnconfigure(0, weight=0) self.frm_general.grid_columnconfigure(1, weight=1) self._general_tabs() self.ntbk.add(self.frm_general, text=ugettext('General')) self.frm_database = Frame(self.ntbk, width=350, height=150) self.frm_database.grid_columnconfigure(0, weight=0) self.frm_database.grid_columnconfigure(1, weight=1) self._database_tabs() self.ntbk.add(self.frm_database, text=ugettext('Database')) btnframe = Frame(self, bd=1) btnframe.grid(row=1, column=0, columnspan=1) Button(btnframe, text=ugettext("OK"), width=10, command=self.apply).grid( row=0, column=0, sticky=(N, S, E)) Button(btnframe, text=ugettext("Cancel"), width=10, command=self.destroy).grid( row=0, column=1, sticky=(N, S, W)) def _database_tabs(self): Label(self.frm_database, text=ugettext("Type")).grid( row=0, column=0, sticky=(N, W), padx=5, pady=3) self.typedb = ttk.Combobox( self.frm_database, textvariable=StringVar(), state=READLONY) self.typedb.bind("<<ComboboxSelected>>", self.typedb_selection) self.typedb.grid(row=0, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_database, text=ugettext("Name")).grid( row=1, column=0, sticky=(N, W), padx=5, pady=3) self.namedb = Entry(self.frm_database) self.namedb.grid(row=1, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_database, text=ugettext("User")).grid( row=2, column=0, sticky=(N, W), padx=5, pady=3) self.userdb = Entry(self.frm_database) self.userdb.grid(row=2, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_database, text=ugettext("Password")).grid( row=3, column=0, sticky=(N, W), padx=5, pady=3) self.pwddb = Entry(self.frm_database) self.pwddb.grid(row=3, column=1, sticky=(N, S, E, W), padx=5, pady=3) def _general_tabs(self): Label(self.frm_general, text=ugettext("Name")).grid( row=0, column=0, sticky=(N, W), padx=5, pady=3) self.name = Entry(self.frm_general) self.name.grid(row=0, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_general, text=ugettext("Appli")).grid( row=1, column=0, sticky=(N, W), padx=5, pady=3) self.applis = ttk.Combobox( self.frm_general, textvariable=StringVar(), state=READLONY) self.applis.bind("<<ComboboxSelected>>", self.appli_selection) self.applis.grid(row=1, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_general, text=ugettext("Modules")).grid( row=2, column=0, sticky=(N, W), padx=5, pady=3) self.modules = Listbox(self.frm_general, selectmode=EXTENDED) self.modules.configure(exportselection=False) self.modules.grid(row=2, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_general, text=ugettext("Language")).grid( row=3, column=0, sticky=(N, W), padx=5, pady=3) self.language = ttk.Combobox( self.frm_general, textvariable=StringVar(), state=READLONY) self.language.grid( row=3, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_general, text=ugettext("CORE-connectmode") ).grid(row=4, column=0, sticky=(N, W), padx=5, pady=3) self.mode = ttk.Combobox( self.frm_general, textvariable=StringVar(), state=READLONY) self.mode.bind("<<ComboboxSelected>>", self.mode_selection) self.mode.grid(row=4, column=1, sticky=(N, S, E, W), padx=5, pady=3) Label(self.frm_general, text=ugettext("Password")).grid( row=5, column=0, sticky=(N, W), padx=5, pady=3) self.password = Entry(self.frm_general, show="*") self.password.grid( row=5, column=1, sticky=(N, S, E, W), padx=5, pady=3) def typedb_selection(self, event): visible = list(self.typedb[VALUES]).index(self.typedb.get()) != 0 for child_cmp in self.frm_database.winfo_children()[2:]: if visible: child_cmp.config(state=NORMAL) else: child_cmp.config(state=DISABLED) def appli_selection(self, event): if self.applis.get() != '': appli_id = list(self.applis[VALUES]).index(self.applis.get()) luct_glo = LucteriosGlobal() current_inst_names = luct_glo.listing() appli_root_name = self.mod_applis[appli_id][0].split('.')[-1] default_name_idx = 1 while appli_root_name + six.text_type(default_name_idx) in current_inst_names: default_name_idx += 1 self.name.delete(0, END) self.name.insert( 0, appli_root_name + six.text_type(default_name_idx)) mod_depended = self.mod_applis[appli_id][2] self.modules.select_clear(0, self.modules.size()) for mod_idx in range(len(self.module_data)): current_mod = self.module_data[mod_idx] if current_mod in mod_depended: self.modules.selection_set(mod_idx) def mode_selection(self, event): visible = list(self.mode[VALUES]).index(self.mode.get()) != 2 for child_cmp in self.frm_general.winfo_children()[-2:]: if visible: child_cmp.config(state=NORMAL) else: child_cmp.config(state=DISABLED) def apply(self): from lucterios.framework.settings import DEFAULT_LANGUAGES, get_locale_lang if self.name.get() == '': showerror(ugettext("Instance editor"), ugettext("Name empty!")) return if self.applis.get() == '': showerror(ugettext("Instance editor"), ugettext("No application!")) return db_param = "%s:name=%s,user=%s,password=%s" % ( self.typedb.get(), self.namedb.get(), self.userdb.get(), self.pwddb.get()) security = "MODE=%s" % list( self.mode[VALUES]).index(self.mode.get()) if self.password.get() != '': security += ",PASSWORD=%s" % self.password.get() module_list = [ self.module_data[int(item)] for item in self.modules.curselection()] appli_id = list(self.applis[VALUES]).index(self.applis.get()) current_lang = get_locale_lang() for lang in DEFAULT_LANGUAGES: if lang[1] == self.language.get(): current_lang = lang[0] self.result = (self.name.get(), self.mod_applis[appli_id][ 0], ",".join(module_list), security, db_param, current_lang) self.destroy() def _load_current_data(self, instance_name): from lucterios.framework.settings import DEFAULT_LANGUAGES, get_locale_lang lct_inst = LucteriosInstance(instance_name) lct_inst.read() self.name.delete(0, END) self.name.insert(0, lct_inst.name) self.name.config(state=DISABLED) applis_id = 0 for appli_iter in range(len(self.mod_applis)): if self.mod_applis[appli_iter][0] == lct_inst.appli_name: applis_id = appli_iter break self.applis.current(applis_id) if lct_inst.extra['']['mode'] is not None: self.mode.current(lct_inst.extra['']['mode'][0]) else: self.mode.current(2) self.mode_selection(None) typedb_index = 0 for typedb_idx in range(len(self.typedb[VALUES])): if self.typedb[VALUES][typedb_idx].lower() == lct_inst.database[0].lower(): typedb_index = typedb_idx break self.typedb.current(typedb_index) self.typedb.config(state=DISABLED) self.typedb_selection(None) self.namedb.delete(0, END) if 'name' in lct_inst.database[1].keys(): self.namedb.insert(0, lct_inst.database[1]['name']) self.userdb.delete(0, END) if 'user' in lct_inst.database[1].keys(): self.userdb.insert(0, lct_inst.database[1]['user']) self.pwddb.delete(0, END) if 'password' in lct_inst.database[1].keys(): self.pwddb.insert(0, lct_inst.database[1]['password']) self.modules.select_clear(0, self.modules.size()) for mod_idx in range(len(self.module_data)): current_mod = self.module_data[mod_idx] if current_mod in lct_inst.modules: self.modules.select_set(mod_idx) current_lang = get_locale_lang() if 'LANGUAGE_CODE' in lct_inst.extra.keys(): current_lang = lct_inst.extra['LANGUAGE_CODE'] for lang in DEFAULT_LANGUAGES: if lang[0] == current_lang: self.language.current(self.language[VALUES].index(lang[1])) def execute(self, instance_name=None): from lucterios.framework.settings import DEFAULT_LANGUAGES, get_locale_lang self.mode[VALUES] = [ugettext( "CORE-connectmode.0"), ugettext("CORE-connectmode.1"), ugettext("CORE-connectmode.2")] self.language[VALUES] = [lang[1] for lang in DEFAULT_LANGUAGES] self.typedb[VALUES] = ["SQLite", "MySQL", "PostgreSQL"] lct_glob = LucteriosGlobal() _, self.mod_applis, mod_modules = lct_glob.installed() self.mod_applis.sort(key=lambda item: get_module_title(item[0])) self.modules.delete(0, END) self.module_data = [] module_list = [] for mod_module_item in mod_modules: module_list.append( (get_module_title(mod_module_item[0]), mod_module_item[0])) module_list.sort(key=lambda module: module[0]) for module_title, module_name in module_list: self.modules.insert(END, module_title) self.module_data.append(module_name) appli_list = [] for mod_appli_item in self.mod_applis: appli_list.append(get_module_title(mod_appli_item[0])) self.applis[VALUES] = appli_list if instance_name is not None: self._load_current_data(instance_name) else: self.typedb.current(0) self.mode.current(2) if len(appli_list) > 0: self.applis.current(0) self.appli_selection(None) self.mode_selection(None) self.typedb_selection(None) for lang in DEFAULT_LANGUAGES: if lang[0] == get_locale_lang(): self.language.current(self.language[VALUES].index(lang[1])) center(self)
class Controller: SNAP_RADIUS = 5 def __init__(self, tk: Tk): tk.title("Layered Polygons") menubar = Menu(tk) menu_file = Menu(menubar, tearoff=0) menu_file.add_command(label="New...", command=self._new_scene) menu_file.add_command(label="Open...", command=self._open_scene) menu_file.add_separator() menu_file.add_command(label="Save", command=self._save_scene) menu_file.add_command(label="Save As...", command=self._save_scene_as) menu_file.add_separator() menu_export = Menu(menu_file, tearoff=0) menu_export.add_command(label="Wavefront (.obj)...", command=self._export_obj) menu_file.add_cascade(label="Export As", menu=menu_export) menu_file.add_separator() menu_file.add_command(label="Quit", command=self._quit_app) menubar.add_cascade(label="File", menu=menu_file) tk.config(menu=menubar) paned = PanedWindow(tk, relief=RAISED) paned.pack(fill=BOTH, expand=1) frame = Frame(paned) paned.add(frame) self._canvas = LayPolyCanvas(frame) bar_x = Scrollbar(frame, orient=HORIZONTAL) bar_x.pack(side=BOTTOM, fill=X) bar_x.config(command=self._canvas.xview) bar_y = Scrollbar(frame, orient=VERTICAL) bar_y.pack(side=RIGHT, fill=Y) bar_y.config(command=self._canvas.yview) self._canvas.config(xscrollcommand=bar_x.set, yscrollcommand=bar_y.set) self._canvas.pack(side=LEFT, expand=True, fill=BOTH) # Thanks to the two guys on Stack Overflow for that! # ( http://stackoverflow.com/a/7734187 ) self._layer_list = Listbox(paned, selectmode=SINGLE) paned.add(self._layer_list) self._scene = None self._current_layer = None self._is_drawing_polygon = False self._tk = tk self._canvas.bind("<Button-1>", self._canvas_left_click) self._canvas.bind("<Button-3>", self._canvas_right_click) self._canvas.bind("<Motion>", self._canvas_mouse_moved) self._layer_list.bind("<<ListboxSelect>>", self._layer_change) self._current_path = None def _canvas_left_click(self, event): if not self._scene or not self._current_layer: return x, y = self._canvas.window_to_canvas_coords(event.x, event.y) if self._is_drawing_polygon: polygon = self._current_layer.get_polygon_at(-1) # Move vtx away from mouse to not interfere with search for closest polygon.get_vertex_at(-1).\ set_coords(x-self.SNAP_RADIUS, y-self.SNAP_RADIUS) closest_vertex = self._current_layer.get_closest_vertex( x, y, self.SNAP_RADIUS) if closest_vertex: polygon.remove_vertex_at(-1) if closest_vertex is polygon.get_vertex_at(0): self._is_drawing_polygon = False else: polygon.add_vertex(closest_vertex) polygon.add_vertex(Vertex(x, y)) else: polygon.get_vertex_at(-1)\ .set_coords(x, y) polygon.add_vertex(Vertex(x, y)) self._canvas.notify_polygon_change(self._current_layer .get_polygon_count()-1) else: # Create start vertex or use already existing one start_vertex = self._current_layer\ .get_closest_vertex(x, y, self.SNAP_RADIUS) if not start_vertex: start_vertex = Vertex(x, y) # Vertex for mouse cursor next_vertex = Vertex(x, y) self._current_layer.add_polygon( Polygon([start_vertex, next_vertex])) self._is_drawing_polygon = True self._canvas.notify_layer_change() def _canvas_right_click(self, event): if not self._current_layer: return if self._is_drawing_polygon: self._current_layer.remove_polygon_at(-1) self._is_drawing_polygon = False else: x, y = self._canvas.window_to_canvas_coords(event.x, event.y) for i in range(0, self._current_layer.get_polygon_count()): if self._current_layer.get_polygon_at(i).contains(x, y): self._current_layer.remove_polygon_at(i) break self._canvas.notify_layer_change() def _canvas_mouse_moved(self, event): if self._is_drawing_polygon: x, y = self._canvas.window_to_canvas_coords(event.x, event.y) self._current_layer.get_polygon_at(-1).get_vertex_at(-1)\ .set_coords(x, y) self._canvas.notify_polygon_change(self._current_layer .get_polygon_count()-1) def _layer_change(self, event): selection = self._layer_list.curselection() if len(selection) > 0 and self._scene: layer = self._scene.get_layer_at(selection[0]) if layer: self._is_drawing_polygon = False self._current_layer = layer self._canvas.notify_new_layer(self._current_layer) def _set_scene(self, scene: Scene) -> bool: if scene.get_layer_count() <= 0: messagebox.showerror("Error!", "Scene has no layers!") return False self._scene = scene # Prepare canvas # TODO Extra 10px padding for canvas width, height = self._scene.get_size() self._canvas.config(scrollregion=(0, 0, width, height)) # Empty listbox, fill it, select first entry self._layer_list.delete(0, self._layer_list.size()-1) for i in range(0, self._scene.get_layer_count()): self._layer_list.insert(i, self._scene.get_layer_at(i).get_name()) self._layer_list.selection_set(0) self._layer_list.event_generate("<<ListboxSelect>>") return True def _set_current_path(self, path): self._current_path = path if path: self._tk.title(path + " - Layered Polygons") else: self._tk.title("Untitled - Layered Polygons") def _new_scene(self): path = filedialog.askopenfilename(defaultextension=".ora", filetypes=[("OpenRaster files", ".ora")]) if not path: return scene = ora.read(path) if not scene: messagebox.showerror("Error!", "File could not be opened!") return if self._set_scene(scene): self._set_current_path(None) def _open_scene(self): path = filedialog.askopenfilename(defaultextension=".lp", filetypes=[("Layered polygons" " files", ".lp")]) if not path: return scene = lp.read(path) if not scene: messagebox.showerror("Error!", "File could not be opened!") return if self._set_scene(scene): self._set_current_path(path) def _save_scene_help(self, path): if lp.save(path, self._scene): self._set_current_path(path) else: messagebox.showerror("Error!", "File could not be saved!") def _save_scene(self): if not self._current_path: self._save_scene_as() return self._save_scene_help(self._current_path) def _save_scene_as(self): if not self._scene: return path = filedialog.asksaveasfilename(defaultextension=".lp", filetypes=[("Layered polygons" " files", ".lp")]) if path: self._save_scene_help(path) def _export_obj(self): if not self._scene: return path_obj = filedialog.asksaveasfilename(defaultextension=".obj", filetypes=[("Wavefront object" " files", ".obj")]) if not path_obj: return path_obj, path_mtl, path_data = obj.get_paths(path_obj) obj.save(path_obj, path_mtl, path_data, self._scene) def _quit_app(self): self._tk.quit() exit()
class DMselector(ttk.Frame): """Listbox for DM Creation, Selection, and Sorting.""" def __init__(self, master, conflict): """Initialize DM selection/creation Widget.""" ttk.Frame.__init__(self, master) self.conflict = conflict self.dms = conflict.decisionMakers # variables self.listVariable = StringVar( value=tuple(['Double Click to Add Item'])) self.selIdx = None self.selectedDM = None # widgets self.label = ttk.Label(self, text="Decision Makers") self.dmListDisp = Listbox(self, listvariable=self.listVariable) self.scrollY = ttk.Scrollbar(self, orient=VERTICAL, command=self.dmListDisp.yview) self.upBtn = ttk.Button(self, width=10, text='Up', command=self.upCmd) self.downBtn = ttk.Button(self, width=10, text='Down', command=self.downCmd) self.delBtn = ttk.Button(self, width=10, text='Delete', command=self.delCmd) # configuration self.dmListDisp.configure(yscrollcommand=self.scrollY.set) self.columnconfigure(0, weight=1) self.rowconfigure(1, weight=1) self.label.grid(column=0, row=0, columnspan=5, sticky=NSEW) self.dmListDisp.grid(column=0, row=1, columnspan=5, sticky=NSEW) self.scrollY.grid(column=5, row=1, sticky=NSEW) self.upBtn.grid(column=2, row=2, sticky=NSEW) self.downBtn.grid(column=3, row=2, sticky=NSEW) self.delBtn.grid(column=4, row=2, sticky=NSEW) self.dmListDisp.bind('<<ListboxSelect>>', self.selChgCmd) self.dmListDisp.bind('<Double-1>', self.editCmd) self.dmListDisp.bind('<Delete>', self.delCmd) self.dmListDisp.bind('<Return>', self.editCmd) def refresh(self, event=None): """Refresh widget contents.""" self.updateList() for idx in range(len(self.dms)): self.dmListDisp.itemconfigure(idx, foreground='black') self.dmListDisp.itemconfigure(len(self.dms), foreground='#A0A0A0') def updateList(self, event=None): """Update the value in the displayed listVariable.""" self.listVariable.set( tuple(self.dms.names() + ['Double Click to Add Item'])) def moveEntry(self, idx, idx2): """Move an item from idx to idx2.""" self.dms.insert(idx2, self.dms.pop(idx)) self.updateList() self.dmListDisp.selection_clear(idx) self.dmListDisp.selection_set(idx2) self.selChgCmd() self.event_generate("<<dmOptChg>>") def upCmd(self, event=None): """Move the selected element up one space in the list.""" idx = self.selIdx # check that there is an entry selected, and it isn't the first entry. if idx not in [-1, 0, len(self.dms)]: self.moveEntry(idx, idx - 1) def downCmd(self, event=None): """Move the selected element down one space in the list.""" idx = self.selIdx # check that there is an entry selected, and it isn't the last entry if idx not in [-1, len(self.dms) - 1, len(self.dms)]: self.moveEntry(idx, idx + 1) def delCmd(self, *args): """Delete the selected element from the list.""" idx = self.selIdx if idx != len(self.dms): # check that a valid entry is selected del self.dms[idx] self.refresh() self.event_generate("<<dmOptChg>>") def selChgCmd(self, *args): """Called when the selection changes.""" self.selIdx = int(self.dmListDisp.curselection()[0]) if self.selIdx != len(self.conflict.decisionMakers): self.selectedDM = self.conflict.decisionMakers[self.selIdx] else: self.selectedDM = None self.event_generate('<<DMselected>>') def editCmd(self, *args): """Called when a list entry is selected for editing.""" self.event_generate('<<EditDM>>') def reselect(self, event=None): """Return selection focus to a dm after an action is completed.""" if self.selIdx is not None: self.dmListDisp.selection_set(self.selIdx) self.dmListDisp.event_generate('<<ListboxSelect>>')
class Combobox_Autocomplete(Entry, object): def __init__(self, master, list_of_items=None, autocomplete_function=None, listbox_width=None, listbox_height=7, ignorecase_match=False, startswith_match=True, vscrollbar=True, hscrollbar=True, **kwargs): if hasattr(self, "autocomplete_function"): if autocomplete_function is not None: raise ValueError( "Combobox_Autocomplete subclass has 'autocomplete_function' implemented") else: if autocomplete_function is not None: self.autocomplete_function = autocomplete_function else: if list_of_items is None: raise ValueError( "If not given complete function, list_of_items can't be 'None'") elif ignorecase_match: if startswith_match: def matches_function(entry_data, item): return item.startswith(entry_data) else: def matches_function(entry_data, item): return item in entry_data self.autocomplete_function = lambda entry_data: [ item for item in self.list_of_items if matches_function(entry_data, item)] else: if startswith_match: def matches_function(escaped_entry_data, item): if re.match(escaped_entry_data, item, re.IGNORECASE): return True else: return False else: def matches_function(escaped_entry_data, item): if re.search(escaped_entry_data, item, re.IGNORECASE): return True else: return False def autocomplete_function2(entry_data): escaped_entry_data = re.escape(entry_data) return [item for item in self.list_of_items if matches_function(escaped_entry_data, item)] self.autocomplete_function = autocomplete_function2 self._listbox_height = int(listbox_height) self._listbox_width = listbox_width self.list_of_items = list_of_items self._use_vscrollbar = vscrollbar self._use_hscrollbar = hscrollbar kwargs.setdefault("background", "white") if "textvariable" in kwargs: self._entry_var = kwargs["textvariable"] else: self._entry_var = kwargs["textvariable"] = StringVar() Entry.__init__(self, master, **kwargs) self._trace_id = self._entry_var.trace('w', self._on_change_entry_var) self._listbox = None self.bind("<Tab>", self._on_tab) self.bind("<Up>", self._previous) self.bind("<Down>", self._next) self.bind('<Control-n>', self._next) self.bind('<Control-p>', self._previous) self.bind("<Return>", self._update_entry_from_listbox) self.bind("<Escape>", lambda event: self.unpost_listbox()) def _on_tab(self, event): self.post_listbox() return "break" def _on_change_entry_var(self, name, index, mode): entry_data = self._entry_var.get() if entry_data == '': self.unpost_listbox() self.focus() else: values = self.autocomplete_function(entry_data) if values: if self._listbox is None: self._build_listbox(values) else: self._listbox.delete(0, END) height = min(self._listbox_height, len(values)) self._listbox.configure(height=height) for item in values: self._listbox.insert(END, item) else: self.unpost_listbox() self.focus() def _build_listbox(self, values): listbox_frame = Frame() self._listbox = Listbox(listbox_frame, background="white", selectmode=SINGLE, activestyle="none", exportselection=False) self._listbox.grid(row=0, column=0, sticky=N+E+W+S) self._listbox.bind("<ButtonRelease-1>", self._update_entry_from_listbox) self._listbox.bind("<Return>", self._update_entry_from_listbox) self._listbox.bind("<Escape>", lambda event: self.unpost_listbox()) self._listbox.bind('<Control-n>', self._next) self._listbox.bind('<Control-p>', self._previous) if self._use_vscrollbar: vbar = Scrollbar(listbox_frame, orient=VERTICAL, command=self._listbox.yview) vbar.grid(row=0, column=1, sticky=N+S) self._listbox.configure( yscrollcommand=lambda f, l: autoscroll(vbar, f, l)) elif self._use_hscrollbar: hbar = Scrollbar(listbox_frame, orient=HORIZONTAL, command=self._listbox.xview) hbar.grid(row=1, column=0, sticky=E+W) self._listbox.configure( xscrollcommand=lambda f, l: autoscroll(hbar, f, l)) listbox_frame.grid_columnconfigure(0, weight=1) listbox_frame.grid_rowconfigure(0, weight=1) x = -self.cget("borderwidth") - self.cget("highlightthickness") y = self.winfo_height()-self.cget("borderwidth") - \ self.cget("highlightthickness") elif self._listbox_width: width = self._listbox_width else: width = self.winfo_width() listbox_frame.place(in_=self, x=x, y=y, width=width) height = min(self._listbox_height, len(values)) self._listbox.configure(height=height) for item in values: self._listbox.insert(END, item) def post_listbox(self): if self._listbox is not None: return entry_data = self._entry_var.get() if entry_data == '': return values = self.autocomplete_function(entry_data) if values: self._build_listbox(values) def unpost_listbox(self): if self._listbox is not None: self._listbox.master.destroy() self._listbox = None def get_value(self): return self._entry_var.get() def set_value(self, text, close_dialog=False): self._set_var(text) if close_dialog: self.unpost_listbox() self.icursor(END) self.xview_moveto(1.0) def _set_var(self, text): self._entry_var.trace_vdelete("w", self._trace_id) self._entry_var.set(text) self._trace_id = self._entry_var.trace('w', self._on_change_entry_var) def _update_entry_from_listbox(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if current_selection: text = self._listbox.get(current_selection) self._set_var(text) self._listbox.master.destroy() self._listbox = None self.focus() self.icursor(END) self.xview_moveto(1.0) return "break" def _previous(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if len(current_selection) == 0: self._listbox.selection_set(0) self._listbox.activate(0) else: index = int(current_selection[0]) self._listbox.selection_clear(index) if index == 0: index = END else: index -= 1 self._listbox.see(index) self._listbox.selection_set(first=index) self._listbox.activate(index) return "break" def _next(self, event): if self._listbox is not None: current_selection = self._listbox.curselection() if len(current_selection) == 0: self._listbox.selection_set(0) self._listbox.activate(0) else: index = int(current_selection[0]) self._listbox.selection_clear(index) if index == self._listbox.size() - 1: index = 0 else: index += 1 self._listbox.see(index) self._listbox.selection_set(index) self._listbox.activate(index) return "break"
class Add_Recipe_Modal(Modal): def __init__(self, parent=None, title="Add Recipe"): self.shorts = [] self.amounts = None self.ingredients = None Modal.__init__(self, parent, title, geometry="375x410" if system() == "Windows" else "375x350") def initialize(self): amount_label = Label(self, width=8, text="Amount") amount_label.grid(row=0, column=0) ingredients_label = Label(self, width=35, text="Item Name") ingredients_label.grid(row=0, column=1) self.amounts_list = Listbox(self, width=8, selectmode="single") self.amounts_list.bind("<Double-Button-1>", self.edit) self.amounts_list.grid(row=1, column=0, sticky="E") self.ingredients_list = Listbox(self, width=35, selectmode="single") self.ingredients_list.bind("<Double-Button-1>", self.edit) self.ingredients_list.grid(row=1, column=1) add_button = Button(self, width=7, text="Add", command=self.add) self.bind("<Control-+>", self.add) self.bind("<Insert>", self.add) add_button.grid(row=2, column=1, sticky="E") remove_button = Button(self, text="Remove", command=self.remove) self.bind("<Delete>", self.remove) remove_button.grid(row=2, column=0) produces_label = Label(self, text="Produces: ") produces_label.grid(row=3, column=0, sticky="W") self.produces_box = Entry(self, width=5) self.produces_box.insert(0, "1") self.produces_box.grid(row=3, column=1, sticky="W") machine_label = Label(self, text="Machine: ") machine_label.grid(row=4, column=0, sticky="W") self.machine_box = Entry(self) self.machine_box.grid(row=4, column=1, sticky="EW") info_label = Label(self, text="Extra Info: ") info_label.grid(row=5, column=0, sticky="W") self.info_box = Entry(self) self.info_box.grid(row=5, column=1, sticky="EW") cancel_button = Button(self, text="Cancel", width=7, command=self.cancel) self.bind("<Escape>", self.cancel) cancel_button.grid(row=6, column=0, pady=10) ok_button = Button(self, text="OK", width=7, command=self.ok) self.bind("<Return>", self.ok) ok_button.grid(row=6, column=1, pady=10, sticky="E") self.bind("<<ListboxSelect>>", self.sync) def sync(self, event): if event.widget is self.amounts_list and len(self.amounts_list.curselection()) != 0: self.ingredients_list.selection_set(self.amounts_list.curselection()[0]) def add(self, event=None): short, name, amount = Add_Ingredient_Modal().show() if short is not None and name is not None and amount is not None: if name not in self.ingredients_list.get(0, "end"): self.ingredients_list.insert("end", name) self.amounts_list.insert("end", amount) self.shorts.append(short) def edit(self, event=None): if len(self.ingredients_list.curselection()) != 0: index = self.ingredients_list.curselection()[0] current_short = self.shorts[index] current_name = self.ingredients_list.get(index) current_amount = self.amounts_list.get(index) new_short, new_name, new_amount = Edit_Ingredient_Modal().show(current_short, current_name, current_amount) if new_short is not None and new_name is not None and new_amount is not None: self.amounts_list.delete(index) self.ingredients_list.delete(index) self.amounts_list.insert(index, new_amount) self.ingredients_list.insert(index, new_name) self.shorts[index] = new_short def remove(self, event=None): if len(self.ingredients_list.curselection()) != 0: slct = int(self.ingredients_list.curselection()[0]) self.ingredients_list.delete(slct) self.amounts_list.delete(slct) del(self.shorts[slct]) def ok(self, event=None): if len(self.ingredients_list.get(0)) != 0 and self.produces_box is not None and self.produces_box.get().isdigit(): self.produces = int(self.produces_box.get()) self.amounts = self.amounts_list.get(0, last="end") self.ingredients = self.ingredients_list.get(0, last="end") self.machine = self.machine_box.get() self.info = self.info_box.get() self.destroy() def cancel(self, event=None): self.amounts = None self.ingredients = None self.shorts = None self.produces = None self.machine = None self.info = None self.destroy() def show(self): Modal.show(self) return self.shorts, self.ingredients, self.amounts, self.produces, self.machine, self.info
def second_window(root, info): def next_step(): idxs = lbox.curselection() for blogger in idxs: name = bloggers()[blogger] if name not in info['bloggers']: info['bloggers'].append(name) if 'Blogs' in info['platforms']: blog_posts(root, info) else: third_window(root, info) def cantfind(info=info): idxs = lbox.curselection() for blogger in idxs: name = bloggers()[blogger] if name not in info['bloggers']: info['bloggers'].append(name) add_blogger(info=info) def active_next(*args): send.state(['!disabled', 'active']) def back(): idxs = lbox.curselection() for blogger in idxs: name = bloggers()[blogger] if name not in info['bloggers']: info['bloggers'].append(name) first_window(root, info=info) c = ttk.Frame(root, padding=(5, 0, 0, 0)) c.grid(column=0, row=0, sticky=(N, W, E, S)) background_image = tkinter.PhotoImage(file='%s/Desktop/natappy/images/moon.gif' % home) background_label = tkinter.Label(c, image=background_image) background_label.image = background_image background_label.place(x=0, y=0, relwidth=1, relheight=1) root.grid_columnconfigure(0, weight=3) root.grid_rowconfigure(0, weight=3) lbox = Listbox(c, selectmode=MULTIPLE) lbox.grid(column=0, row=1, rowspan=11, columnspan=7, sticky=( N, W, S), padx=(10, 10), pady=(1, 1), ipadx=75) yscroll = ttk.Scrollbar(command=lbox.yview, orient=VERTICAL) yscroll.grid(row=0, column=0, padx=(0, 10), sticky=(N, W, S)) lbox.configure(yscrollcommand=yscroll.set) for blogger in bloggers(): lbox.insert(END, blogger) lbox.bind("<<ListboxSelect>>") lbox.yview_scroll(40, 'units') cantfind = ttk.Button(c, text='Add new bloggers', command=cantfind) cantfind.grid(column=4, row=1, padx=(10, 0), sticky=(N, S, E, W), pady=(20, 10)) send = ttk.Button(c, text='Next', command=next_step, default='active', state='disabled') send.grid(column=6, row=11, sticky=E, pady=20, padx=(2, 20)) close = ttk.Button(c, text='Back', command=back, default='active') close.grid(column=5, row=11, sticky=S + E, pady=20, padx=2) lbox.bind('<<ListboxSelect>>', active_next) if info['bloggers']: for blogger in info['bloggers']: i = bloggers().index(blogger) lbox.selection_set(i) active_next() for i in range(len(bloggers()), 2): lbox.itemconfigure(i, background='#f0f0ff') c.grid_columnconfigure(0, weight=1) c.grid_rowconfigure(5, weight=1) root.title('2/5 Select bloggers') root.geometry('680x550+300+40')
class TPolygonWindow(simpledialog.Dialog): """ Class represents a polygon vertices edit window. :param master: master window object. :type master: tkinter.Tk :param app: main app object. :type app: TApp :param polygon: edited polygon object. :type polygon: TPolygon """ def __init__(self, master, app, polygon): """ Initialise object variables and call the parent class constructor. """ self._app = app self._polygon = polygon super().__init__(master) def body(self, master): """ Initialise widgets. :param master: master window object. :type master: tkinter.Tk """ # Frame for widgets self.main_frame = Frame(self) # Listbox self.vertices_list = Listbox(self.main_frame, exportselection = False, \ width = 40) self.vertices_list.config(exportselection=0) self.vertices_list.pack(expand=True, fill=BOTH, side=LEFT) self.vertices_list.bind("<Double-Button-1>", self.vertices_list_selected_item) # Listbox's yscrollbar self.vertices_list_scrollbar = Scrollbar(self.main_frame, orient = VERTICAL, \ command = self.vertices_list.yview) self.vertices_list_scrollbar.pack(expand=True, fill=Y, side=LEFT) self.vertices_list.config( yscrollcommand=self.vertices_list_scrollbar.set) self.main_frame.pack(expand=True, fill=BOTH) # Fill list with vertices data self.update_vertices_list() def buttonbox(self): """ Redefine default Ok/Cancel buttons in the bottom of the window with Apply/Add/Delete. """ self.bbox = Frame(self) self.apply_button = Button(self.bbox, text = "Apply", width = 10, \ command = self.apply) self.apply_button.pack(side=LEFT, padx=5, pady=5) self.add_button = Button(self.bbox, text = "Add", width = 10, \ command = self.add_vertex) self.add_button.pack(side=LEFT, padx=5, pady=5) self.delete_button = Button(self.bbox, text = "Delete", width = 10, \ command = self.delete_vertex) self.delete_button.pack(side=LEFT, padx=5, pady=5) self.bbox.pack() def get_current_selection(self): """ Retrieve the selected vertex index. :rtype: integer """ try: cursel = self.vertices_list.curselection()[0] except: cursel = 0 return cursel def vertices_list_selected_item(self, event): """ Display and edit selected vertex parameters. :param event: listbox LMB click event. :type event: tkinter.Event """ try: vertex_num = (self.vertices_list.curselection())[0] except IndexError: return except Exception as message: messagebox.showerror("Error while picking shape!", message) if (vertex_num < 0): return else: try: vertex = self._polygon.points_mod[vertex_num] except Exception as message: messagebox.showerror("Materials list error", message) return initialvalue = str(vertex.x) + " " + str(vertex.y) input_str = simpledialog.askstring("Input coordinates", "Give vertex coordinates", \ initialvalue = initialvalue) try: new_x, new_y = input_str.split() except AttributeError: pass except ValueError as e: messagebox.showerror("Wrong input!", e) else: edited_point = self._polygon.points_mod[vertex_num] edited_point.x, edited_point.y = float(new_x), float(new_y) self._polygon.update_window_positions() self._app.main_canvas.delete("all") self._app.canvas_refresh() finally: self.update_vertices_list() self.vertices_list.select_clear(0, END) self.vertices_list.selection_set(vertex_num) self.vertices_list.activate(vertex_num) self.vertices_list.focus_set() def update_vertices_list(self): """ Update entries in the vertices listbox. """ cursel = self.get_current_selection() self.vertices_list.delete(0, END) for i, v in enumerate(self._polygon.points_mod): self.vertices_list.insert(i, str(i + 1) + ". (" + str(v.x) + ", " + \ str(v.y) + ")") self.vertices_list.select_clear(0, END) if (cursel >= self.vertices_list.size()): self.vertices_list.selection_set(cursel - 1) self.vertices_list.activate(cursel) else: self.vertices_list.selection_set(cursel) self.vertices_list.activate(cursel) def add_vertex(self): """ Add a vertex to the polygon. """ cursel = self.get_current_selection() input_str = simpledialog.askstring("Input coordinates", "Give vertex coordinates") try: new_x, new_y = input_str.split() except AttributeError: pass except ValueError as e: messagebox.showerror("Wrong input", e) else: self._polygon.add_vertex(x_mod=float(new_x), y_mod=float(new_y)) self._app.main_canvas.delete("all") self._app.canvas_refresh() finally: self.update_vertices_list() self.vertices_list.select_clear(0, END) self.vertices_list.selection_set(cursel) self.vertices_list.activate(cursel) self.vertices_list.focus_set() def delete_vertex(self): """ Delete a vertex from the polygon. """ cursel = self.get_current_selection() self._polygon.remove_vertex(cursel) self._app.main_canvas.delete("all") self._app.canvas_refresh() self.update_vertices_list() self.vertices_list.select_clear(0, END) self.vertices_list.selection_set(cursel) self.vertices_list.activate(cursel) self.vertices_list.focus_set() def apply(self): """ Destroy window upon clicking Apply button. """ self.destroy()
class ShiftReduceApp(object): """ A graphical tool for exploring the shift-reduce parser. The tool displays the parser's stack and the remaining text, and allows the user to control the parser's operation. In particular, the user can shift tokens onto the stack, and can perform reductions on the top elements of the stack. A "step" button simply steps through the parsing process, performing the operations that ``nltk.parse.ShiftReduceParser`` would use. """ def __init__(self, grammar, sent, trace=0): self._sent = sent self._parser = SteppingShiftReduceParser(grammar, trace) # Set up the main window. self._top = Tk() self._top.title('Shift Reduce Parser Application') # Animations. animating_lock is a lock to prevent the demo # from performing new operations while it's animating. self._animating_lock = 0 self._animate = IntVar(self._top) self._animate.set(10) # = medium # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Initialize fonts. self._init_fonts(self._top) # Set up key bindings. self._init_bindings() # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_feedback(self._top) self._init_grammar(self._top) self._init_canvas(self._top) # A popup menu for reducing. self._reduce_menu = Menu(self._canvas, tearoff=0) # Reset the demo, and set the feedback frame to empty. self.reset() self._lastoper1['text'] = '' ######################################### ## Initialization Helpers ######################################### def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = tkinter.font.Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget('size')) self._boldfont = tkinter.font.Font(family='helvetica', weight='bold', size=self._size.get()) self._font = tkinter.font.Font(family='helvetica', size=self._size.get()) def _init_grammar(self, parent): # Grammar view. self._prodframe = listframe = Frame(parent) self._prodframe.pack(fill='both', side='left', padx=2) self._prodlist_label = Label(self._prodframe, font=self._boldfont, text='Available Reductions') self._prodlist_label.pack() self._prodlist = Listbox(self._prodframe, selectmode='single', relief='groove', background='white', foreground='#909090', font=self._font, selectforeground='#004040', selectbackground='#c0f0c0') self._prodlist.pack(side='right', fill='both', expand=1) self._productions = list(self._parser.grammar().productions()) for production in self._productions: self._prodlist.insert('end', (' %s' % production)) self._prodlist.config(height=min(len(self._productions), 25)) # Add a scrollbar if there are more than 25 productions. if 1: #len(self._productions) > 25: listscroll = Scrollbar(self._prodframe, orient='vertical') self._prodlist.config(yscrollcommand=listscroll.set) listscroll.config(command=self._prodlist.yview) listscroll.pack(side='left', fill='y') # If they select a production, apply it. self._prodlist.bind('<<ListboxSelect>>', self._prodlist_select) # When they hover over a production, highlight it. self._hover = -1 self._prodlist.bind('<Motion>', self._highlight_hover) self._prodlist.bind('<Leave>', self._clear_hover) def _init_bindings(self): # Quit self._top.bind('<Control-q>', self.destroy) self._top.bind('<Control-x>', self.destroy) self._top.bind('<Alt-q>', self.destroy) self._top.bind('<Alt-x>', self.destroy) # Ops (step, shift, reduce, undo) self._top.bind('<space>', self.step) self._top.bind('<s>', self.shift) self._top.bind('<Alt-s>', self.shift) self._top.bind('<Control-s>', self.shift) self._top.bind('<r>', self.reduce) self._top.bind('<Alt-r>', self.reduce) self._top.bind('<Control-r>', self.reduce) self._top.bind('<Delete>', self.reset) self._top.bind('<u>', self.undo) self._top.bind('<Alt-u>', self.undo) self._top.bind('<Control-u>', self.undo) self._top.bind('<Control-z>', self.undo) self._top.bind('<BackSpace>', self.undo) # Misc self._top.bind('<Control-p>', self.postscript) self._top.bind('<Control-h>', self.help) self._top.bind('<F1>', self.help) self._top.bind('<Control-g>', self.edit_grammar) self._top.bind('<Control-t>', self.edit_sentence) # Animation speed control self._top.bind('-', lambda e, a=self._animate: a.set(20)) self._top.bind('=', lambda e, a=self._animate: a.set(10)) self._top.bind('+', lambda e, a=self._animate: a.set(4)) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill='none', side='bottom') Button( buttonframe, text='Step', background='#90c0d0', foreground='black', command=self.step, ).pack(side='left') Button(buttonframe, text='Shift', underline=0, background='#90f090', foreground='black', command=self.shift).pack(side='left') Button(buttonframe, text='Reduce', underline=0, background='#90f090', foreground='black', command=self.reduce).pack(side='left') Button(buttonframe, text='Undo', underline=0, background='#f0a0a0', foreground='black', command=self.undo).pack(side='left') def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label='Reset Parser', underline=0, command=self.reset, accelerator='Del') filemenu.add_command(label='Print to Postscript', underline=0, command=self.postscript, accelerator='Ctrl-p') filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-x') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) editmenu.add_command(label='Edit Grammar', underline=5, command=self.edit_grammar, accelerator='Ctrl-g') editmenu.add_command(label='Edit Text', underline=5, command=self.edit_sentence, accelerator='Ctrl-t') menubar.add_cascade(label='Edit', underline=0, menu=editmenu) rulemenu = Menu(menubar, tearoff=0) rulemenu.add_command(label='Step', underline=1, command=self.step, accelerator='Space') rulemenu.add_separator() rulemenu.add_command(label='Shift', underline=0, command=self.shift, accelerator='Ctrl-s') rulemenu.add_command(label='Reduce', underline=0, command=self.reduce, accelerator='Ctrl-r') rulemenu.add_separator() rulemenu.add_command(label='Undo', underline=0, command=self.undo, accelerator='Ctrl-u') menubar.add_cascade(label='Apply', underline=0, menu=rulemenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_checkbutton(label="Show Grammar", underline=0, variable=self._show_grammar, command=self._toggle_grammar) viewmenu.add_separator() viewmenu.add_radiobutton(label='Tiny', variable=self._size, underline=0, value=10, command=self.resize) viewmenu.add_radiobutton(label='Small', variable=self._size, underline=0, value=12, command=self.resize) viewmenu.add_radiobutton(label='Medium', variable=self._size, underline=0, value=14, command=self.resize) viewmenu.add_radiobutton(label='Large', variable=self._size, underline=0, value=18, command=self.resize) viewmenu.add_radiobutton(label='Huge', variable=self._size, underline=0, value=24, command=self.resize) menubar.add_cascade(label='View', underline=0, menu=viewmenu) animatemenu = Menu(menubar, tearoff=0) animatemenu.add_radiobutton(label="No Animation", underline=0, variable=self._animate, value=0) animatemenu.add_radiobutton(label="Slow Animation", underline=0, variable=self._animate, value=20, accelerator='-') animatemenu.add_radiobutton(label="Normal Animation", underline=0, variable=self._animate, value=10, accelerator='=') animatemenu.add_radiobutton(label="Fast Animation", underline=0, variable=self._animate, value=4, accelerator='+') menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label='About', underline=0, command=self.about) helpmenu.add_command(label='Instructions', underline=0, command=self.help, accelerator='F1') menubar.add_cascade(label='Help', underline=0, menu=helpmenu) parent.config(menu=menubar) def _init_feedback(self, parent): self._feedbackframe = feedbackframe = Frame(parent) feedbackframe.pack(fill='x', side='bottom', padx=3, pady=3) self._lastoper_label = Label(feedbackframe, text='Last Operation:', font=self._font) self._lastoper_label.pack(side='left') lastoperframe = Frame(feedbackframe, relief='sunken', border=1) lastoperframe.pack(fill='x', side='right', expand=1, padx=5) self._lastoper1 = Label(lastoperframe, foreground='#007070', background='#f0f0f0', font=self._font) self._lastoper2 = Label(lastoperframe, anchor='w', width=30, foreground='#004040', background='#f0f0f0', font=self._font) self._lastoper1.pack(side='left') self._lastoper2.pack(side='left', fill='x', expand=1) def _init_canvas(self, parent): self._cframe = CanvasFrame(parent, background='white', width=525, closeenough=10, border=2, relief='sunken') self._cframe.pack(expand=1, fill='both', side='top', pady=2) canvas = self._canvas = self._cframe.canvas() self._stackwidgets = [] self._rtextwidgets = [] self._titlebar = canvas.create_rectangle(0, 0, 0, 0, fill='#c0f0f0', outline='black') self._exprline = canvas.create_line(0, 0, 0, 0, dash='.') self._stacktop = canvas.create_line(0, 0, 0, 0, fill='#408080') size = self._size.get() + 4 self._stacklabel = TextWidget(canvas, 'Stack', color='#004040', font=self._boldfont) self._rtextlabel = TextWidget(canvas, 'Remaining Text', color='#004040', font=self._boldfont) self._cframe.add_widget(self._stacklabel) self._cframe.add_widget(self._rtextlabel) ######################################### ## Main draw procedure ######################################### def _redraw(self): scrollregion = self._canvas['scrollregion'].split() (cx1, cy1, cx2, cy2) = [int(c) for c in scrollregion] # Delete the old stack & rtext widgets. for stackwidget in self._stackwidgets: self._cframe.destroy_widget(stackwidget) self._stackwidgets = [] for rtextwidget in self._rtextwidgets: self._cframe.destroy_widget(rtextwidget) self._rtextwidgets = [] # Position the titlebar & exprline (x1, y1, x2, y2) = self._stacklabel.bbox() y = y2 - y1 + 10 self._canvas.coords(self._titlebar, -5000, 0, 5000, y - 4) self._canvas.coords(self._exprline, 0, y * 2 - 10, 5000, y * 2 - 10) # Position the titlebar labels.. (x1, y1, x2, y2) = self._stacklabel.bbox() self._stacklabel.move(5 - x1, 3 - y1) (x1, y1, x2, y2) = self._rtextlabel.bbox() self._rtextlabel.move(cx2 - x2 - 5, 3 - y1) # Draw the stack. stackx = 5 for tok in self._parser.stack(): if isinstance(tok, Tree): attribs = { 'tree_color': '#4080a0', 'tree_width': 2, 'node_font': self._boldfont, 'node_color': '#006060', 'leaf_color': '#006060', 'leaf_font': self._font } widget = tree_to_treesegment(self._canvas, tok, **attribs) widget.label()['color'] = '#000000' else: widget = TextWidget(self._canvas, tok, color='#000000', font=self._font) widget.bind_click(self._popup_reduce) self._stackwidgets.append(widget) self._cframe.add_widget(widget, stackx, y) stackx = widget.bbox()[2] + 10 # Draw the remaining text. rtextwidth = 0 for tok in self._parser.remaining_text(): widget = TextWidget(self._canvas, tok, color='#000000', font=self._font) self._rtextwidgets.append(widget) self._cframe.add_widget(widget, rtextwidth, y) rtextwidth = widget.bbox()[2] + 4 # Allow enough room to shift the next token (for animations) if len(self._rtextwidgets) > 0: stackx += self._rtextwidgets[0].width() # Move the remaining text to the correct location (keep it # right-justified, when possible); and move the remaining text # label, if necessary. stackx = max(stackx, self._stacklabel.width() + 25) rlabelwidth = self._rtextlabel.width() + 10 if stackx >= cx2 - max(rtextwidth, rlabelwidth): cx2 = stackx + max(rtextwidth, rlabelwidth) for rtextwidget in self._rtextwidgets: rtextwidget.move(4 + cx2 - rtextwidth, 0) self._rtextlabel.move(cx2 - self._rtextlabel.bbox()[2] - 5, 0) midx = (stackx + cx2 - max(rtextwidth, rlabelwidth)) / 2 self._canvas.coords(self._stacktop, midx, 0, midx, 5000) (x1, y1, x2, y2) = self._stacklabel.bbox() # Set up binding to allow them to shift a token by dragging it. if len(self._rtextwidgets) > 0: def drag_shift(widget, midx=midx, self=self): if widget.bbox()[0] < midx: self.shift() else: self._redraw() self._rtextwidgets[0].bind_drag(drag_shift) self._rtextwidgets[0].bind_click(self.shift) # Draw the stack top. self._highlight_productions() def _draw_stack_top(self, widget): # hack.. midx = widget.bbox()[2] + 50 self._canvas.coords(self._stacktop, midx, 0, midx, 5000) def _highlight_productions(self): # Highlight the productions that can be reduced. self._prodlist.selection_clear(0, 'end') for prod in self._parser.reducible_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) ######################################### ## Button Callbacks ######################################### def destroy(self, *e): if self._top is None: return self._top.destroy() self._top = None def reset(self, *e): self._parser.initialize(self._sent) self._lastoper1['text'] = 'Reset App' self._lastoper2['text'] = '' self._redraw() def step(self, *e): if self.reduce(): return True elif self.shift(): return True else: if list(self._parser.parses()): self._lastoper1['text'] = 'Finished:' self._lastoper2['text'] = 'Success' else: self._lastoper1['text'] = 'Finished:' self._lastoper2['text'] = 'Failure' def shift(self, *e): if self._animating_lock: return if self._parser.shift(): tok = self._parser.stack()[-1] self._lastoper1['text'] = 'Shift:' self._lastoper2['text'] = '%r' % tok if self._animate.get(): self._animate_shift() else: self._redraw() return True return False def reduce(self, *e): if self._animating_lock: return production = self._parser.reduce() if production: self._lastoper1['text'] = 'Reduce:' self._lastoper2['text'] = '%s' % production if self._animate.get(): self._animate_reduce() else: self._redraw() return production def undo(self, *e): if self._animating_lock: return if self._parser.undo(): self._redraw() def postscript(self, *e): self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) ######################################### ## Menubar callbacks ######################################### def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) #self._stacklabel['font'] = ('helvetica', -size-4, 'bold') #self._rtextlabel['font'] = ('helvetica', -size-4, 'bold') #self._lastoper_label['font'] = ('helvetica', -size) #self._lastoper1['font'] = ('helvetica', -size) #self._lastoper2['font'] = ('helvetica', -size) #self._prodlist['font'] = ('helvetica', -size) #self._prodlist_label['font'] = ('helvetica', -size-2, 'bold') self._redraw() def help(self, *e): # The default font's not very legible; try using 'fixed' instead. try: ShowText(self._top, 'Help: Shift-Reduce Parser Application', (__doc__ or '').strip(), width=75, font='fixed') except: ShowText(self._top, 'Help: Shift-Reduce Parser Application', (__doc__ or '').strip(), width=75) def about(self, *e): ABOUT = ("NLTK Shift-Reduce Parser Application\n" + "Written by Edward Loper") TITLE = 'About: Shift-Reduce Parser Application' try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def edit_grammar(self, *e): CFGEditor(self._top, self._parser.grammar(), self.set_grammar) def set_grammar(self, grammar): self._parser.set_grammar(grammar) self._productions = list(grammar.productions()) self._prodlist.delete(0, 'end') for production in self._productions: self._prodlist.insert('end', (' %s' % production)) def edit_sentence(self, *e): sentence = " ".join(self._sent) title = 'Edit Text' instr = 'Enter a new sentence to parse.' EntryDialog(self._top, sentence, instr, self.set_sentence, title) def set_sentence(self, sent): self._sent = sent.split() #[XX] use tagged? self.reset() ######################################### ## Reduce Production Selection ######################################### def _toggle_grammar(self, *e): if self._show_grammar.get(): self._prodframe.pack(fill='both', side='left', padx=2, after=self._feedbackframe) self._lastoper1['text'] = 'Show Grammar' else: self._prodframe.pack_forget() self._lastoper1['text'] = 'Hide Grammar' self._lastoper2['text'] = '' def _prodlist_select(self, event): selection = self._prodlist.curselection() if len(selection) != 1: return index = int(selection[0]) production = self._parser.reduce(self._productions[index]) if production: self._lastoper1['text'] = 'Reduce:' self._lastoper2['text'] = '%s' % production if self._animate.get(): self._animate_reduce() else: self._redraw() else: # Reset the production selections. self._prodlist.selection_clear(0, 'end') for prod in self._parser.reducible_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) def _popup_reduce(self, widget): # Remove old commands. productions = self._parser.reducible_productions() if len(productions) == 0: return self._reduce_menu.delete(0, 'end') for production in productions: self._reduce_menu.add_command(label=str(production), command=self.reduce) self._reduce_menu.post(self._canvas.winfo_pointerx(), self._canvas.winfo_pointery()) ######################################### ## Animations ######################################### def _animate_shift(self): # What widget are we shifting? widget = self._rtextwidgets[0] # Where are we shifting from & to? right = widget.bbox()[0] if len(self._stackwidgets) == 0: left = 5 else: left = self._stackwidgets[-1].bbox()[2] + 10 # Start animating. dt = self._animate.get() dx = (left - right) * 1.0 / dt self._animate_shift_frame(dt, widget, dx) def _animate_shift_frame(self, frame, widget, dx): if frame > 0: self._animating_lock = 1 widget.move(dx, 0) self._top.after(10, self._animate_shift_frame, frame - 1, widget, dx) else: # but: stacktop?? # Shift the widget to the stack. del self._rtextwidgets[0] self._stackwidgets.append(widget) self._animating_lock = 0 # Display the available productions. self._draw_stack_top(widget) self._highlight_productions() def _animate_reduce(self): # What widgets are we shifting? numwidgets = len(self._parser.stack()[-1]) # number of children widgets = self._stackwidgets[-numwidgets:] # How far are we moving? if isinstance(widgets[0], TreeSegmentWidget): ydist = 15 + widgets[0].label().height() else: ydist = 15 + widgets[0].height() # Start animating. dt = self._animate.get() dy = ydist * 2.0 / dt self._animate_reduce_frame(dt / 2, widgets, dy) def _animate_reduce_frame(self, frame, widgets, dy): if frame > 0: self._animating_lock = 1 for widget in widgets: widget.move(0, dy) self._top.after(10, self._animate_reduce_frame, frame - 1, widgets, dy) else: del self._stackwidgets[-len(widgets):] for widget in widgets: self._cframe.remove_widget(widget) tok = self._parser.stack()[-1] if not isinstance(tok, Tree): raise ValueError() label = TextWidget(self._canvas, str(tok.label()), color='#006060', font=self._boldfont) widget = TreeSegmentWidget(self._canvas, label, widgets, width=2) (x1, y1, x2, y2) = self._stacklabel.bbox() y = y2 - y1 + 10 if not self._stackwidgets: x = 5 else: x = self._stackwidgets[-1].bbox()[2] + 10 self._cframe.add_widget(widget, x, y) self._stackwidgets.append(widget) # Display the available productions. self._draw_stack_top(widget) self._highlight_productions() # # Delete the old widgets.. # del self._stackwidgets[-len(widgets):] # for widget in widgets: # self._cframe.destroy_widget(widget) # # # Make a new one. # tok = self._parser.stack()[-1] # if isinstance(tok, Tree): # attribs = {'tree_color': '#4080a0', 'tree_width': 2, # 'node_font': bold, 'node_color': '#006060', # 'leaf_color': '#006060', 'leaf_font':self._font} # widget = tree_to_treesegment(self._canvas, tok.type(), # **attribs) # widget.node()['color'] = '#000000' # else: # widget = TextWidget(self._canvas, tok.type(), # color='#000000', font=self._font) # widget.bind_click(self._popup_reduce) # (x1, y1, x2, y2) = self._stacklabel.bbox() # y = y2-y1+10 # if not self._stackwidgets: x = 5 # else: x = self._stackwidgets[-1].bbox()[2] + 10 # self._cframe.add_widget(widget, x, y) # self._stackwidgets.append(widget) #self._redraw() self._animating_lock = 0 ######################################### ## Hovering. ######################################### def _highlight_hover(self, event): # What production are we hovering over? index = self._prodlist.nearest(event.y) if self._hover == index: return # Clear any previous hover highlighting. self._clear_hover() # If the production corresponds to an available reduction, # highlight the stack. selection = [int(s) for s in self._prodlist.curselection()] if index in selection: rhslen = len(self._productions[index].rhs()) for stackwidget in self._stackwidgets[-rhslen:]: if isinstance(stackwidget, TreeSegmentWidget): stackwidget.label()['color'] = '#00a000' else: stackwidget['color'] = '#00a000' # Remember what production we're hovering over. self._hover = index def _clear_hover(self, *event): # Clear any previous hover highlighting. if self._hover == -1: return self._hover = -1 for stackwidget in self._stackwidgets: if isinstance(stackwidget, TreeSegmentWidget): stackwidget.label()['color'] = 'black' else: stackwidget['color'] = 'black'
class Controller: SNAP_RADIUS = 5 def __init__(self, tk: Tk): tk.title("Layered Polygons") menubar = Menu(tk) menu_file = Menu(menubar, tearoff=0) menu_file.add_command(label="New...", command=self._new_scene) menu_file.add_command(label="Open...", command=self._open_scene) menu_file.add_separator() menu_file.add_command(label="Save", command=self._save_scene) menu_file.add_command(label="Save As...", command=self._save_scene_as) menu_file.add_separator() menu_export = Menu(menu_file, tearoff=0) menu_export.add_command(label="Wavefront (.obj)...", command=self._export_obj) menu_file.add_cascade(label="Export As", menu=menu_export) menu_file.add_separator() menu_file.add_command(label="Quit", command=self._quit_app) menubar.add_cascade(label="File", menu=menu_file) tk.config(menu=menubar) paned = PanedWindow(tk, relief=RAISED) paned.pack(fill=BOTH, expand=1) frame = Frame(paned) paned.add(frame) self._canvas = LayPolyCanvas(frame) bar_x = Scrollbar(frame, orient=HORIZONTAL) bar_x.pack(side=BOTTOM, fill=X) bar_x.config(command=self._canvas.xview) bar_y = Scrollbar(frame, orient=VERTICAL) bar_y.pack(side=RIGHT, fill=Y) bar_y.config(command=self._canvas.yview) self._canvas.config(xscrollcommand=bar_x.set, yscrollcommand=bar_y.set) self._canvas.pack(side=LEFT, expand=True, fill=BOTH) # Thanks to the two guys on Stack Overflow for that! # ( http://stackoverflow.com/a/7734187 ) self._layer_list = Listbox(paned, selectmode=SINGLE) paned.add(self._layer_list) self._scene = None self._current_layer = None self._is_drawing_polygon = False self._tk = tk self._canvas.bind("<Button-1>", self._canvas_left_click) self._canvas.bind("<Button-3>", self._canvas_right_click) self._canvas.bind("<Motion>", self._canvas_mouse_moved) self._layer_list.bind("<<ListboxSelect>>", self._layer_change) self._current_path = None def _canvas_left_click(self, event): if not self._scene or not self._current_layer: return x, y = self._canvas.window_to_canvas_coords(event.x, event.y) if self._is_drawing_polygon: polygon = self._current_layer.get_polygon_at(-1) # Move vtx away from mouse to not interfere with search for closest polygon.get_vertex_at(-1).\ set_coords(x-self.SNAP_RADIUS, y-self.SNAP_RADIUS) closest_vertex = self._current_layer.get_closest_vertex( x, y, self.SNAP_RADIUS) if closest_vertex: polygon.remove_vertex_at(-1) if closest_vertex is polygon.get_vertex_at(0): self._is_drawing_polygon = False else: polygon.add_vertex(closest_vertex) polygon.add_vertex(Vertex(x, y)) else: polygon.get_vertex_at(-1)\ .set_coords(x, y) polygon.add_vertex(Vertex(x, y)) self._canvas.notify_polygon_change( self._current_layer.get_polygon_count() - 1) else: # Create start vertex or use already existing one start_vertex = self._current_layer\ .get_closest_vertex(x, y, self.SNAP_RADIUS) if not start_vertex: start_vertex = Vertex(x, y) # Vertex for mouse cursor next_vertex = Vertex(x, y) self._current_layer.add_polygon( Polygon([start_vertex, next_vertex])) self._is_drawing_polygon = True self._canvas.notify_layer_change() def _canvas_right_click(self, event): if not self._current_layer: return if self._is_drawing_polygon: self._current_layer.remove_polygon_at(-1) self._is_drawing_polygon = False else: x, y = self._canvas.window_to_canvas_coords(event.x, event.y) for i in range(0, self._current_layer.get_polygon_count()): if self._current_layer.get_polygon_at(i).contains(x, y): self._current_layer.remove_polygon_at(i) break self._canvas.notify_layer_change() def _canvas_mouse_moved(self, event): if self._is_drawing_polygon: x, y = self._canvas.window_to_canvas_coords(event.x, event.y) self._current_layer.get_polygon_at(-1).get_vertex_at(-1)\ .set_coords(x, y) self._canvas.notify_polygon_change( self._current_layer.get_polygon_count() - 1) def _layer_change(self, event): selection = self._layer_list.curselection() if len(selection) > 0 and self._scene: layer = self._scene.get_layer_at(selection[0]) if layer: self._is_drawing_polygon = False self._current_layer = layer self._canvas.notify_new_layer(self._current_layer) def _set_scene(self, scene: Scene) -> bool: if scene.get_layer_count() <= 0: messagebox.showerror("Error!", "Scene has no layers!") return False self._scene = scene # Prepare canvas # TODO Extra 10px padding for canvas width, height = self._scene.get_size() self._canvas.config(scrollregion=(0, 0, width, height)) # Empty listbox, fill it, select first entry self._layer_list.delete(0, self._layer_list.size() - 1) for i in range(0, self._scene.get_layer_count()): self._layer_list.insert(i, self._scene.get_layer_at(i).get_name()) self._layer_list.selection_set(0) self._layer_list.event_generate("<<ListboxSelect>>") return True def _set_current_path(self, path): self._current_path = path if path: self._tk.title(path + " - Layered Polygons") else: self._tk.title("Untitled - Layered Polygons") def _new_scene(self): path = filedialog.askopenfilename(defaultextension=".ora", filetypes=[("OpenRaster files", ".ora")]) if not path: return scene = ora.read(path) if not scene: messagebox.showerror("Error!", "File could not be opened!") return if self._set_scene(scene): self._set_current_path(None) def _open_scene(self): path = filedialog.askopenfilename(defaultextension=".lp", filetypes=[("Layered polygons" " files", ".lp")]) if not path: return scene = lp.read(path) if not scene: messagebox.showerror("Error!", "File could not be opened!") return if self._set_scene(scene): self._set_current_path(path) def _save_scene_help(self, path): if lp.save(path, self._scene): self._set_current_path(path) else: messagebox.showerror("Error!", "File could not be saved!") def _save_scene(self): if not self._current_path: self._save_scene_as() return self._save_scene_help(self._current_path) def _save_scene_as(self): if not self._scene: return path = filedialog.asksaveasfilename(defaultextension=".lp", filetypes=[("Layered polygons" " files", ".lp")]) if path: self._save_scene_help(path) def _export_obj(self): if not self._scene: return path_obj = filedialog.asksaveasfilename(defaultextension=".obj", filetypes=[("Wavefront object" " files", ".obj")]) if not path_obj: return path_obj, path_mtl, path_data = obj.get_paths(path_obj) obj.save(path_obj, path_mtl, path_data, self._scene) def _quit_app(self): self._tk.quit() exit()
class HoursBankGUI(): def __init__(self, window): # define the window self.window = window window.title("Hours Management Tool") # define the size of the main window window.geometry('{}x{}'.format(700, 500)) self.window.grid_rowconfigure(1, weight=1) self.window.grid_columnconfigure(0, weight=1) # pretends the window from being resized self.window.resizable(0, 0) # font test # appHighlightFont = font.Font(family='Helvetica', size=12, weight='bold') # define the panels ############################################################################## # LEFT PANEL # This panel houses the list box which allows the user to select the employee self.leftFrame = Frame(self.window, bg="white", width=300, height=500, pady=3, borderwidth=1, relief="solid") self.leftFrame.grid(row=0, sticky="nsw") # keep the frame the same size even if the widgets chnage size self.leftFrame.grid_propagate(False) # label self.listLabel = Label(self.leftFrame, text="Select User", bg="white", font="Ariel") self.listLabel.grid(row=0, sticky="nwe") # Listbox self.list_box = Listbox(self.leftFrame, width=23, height=18, bg="white", font=("Ariel", 14)) self.list_box.grid(row=1, sticky="wes", padx=10, pady=20) # scroll Bar self.scrollbar = Scrollbar(self.leftFrame) self.list_box.config(yscrollcommand=self.scrollbar.set) self.scrollbar.config(command=self.list_box.yview) self.scrollbar.grid(column=2, row=1, sticky="ns") # fire an event when a employee in the list box is selected self.list_box.bind('<<ListboxSelect>>', self.showDetailsLive) # RIGHT PANEL TOP # When a user clicks on an employee in the employee list box then their details will be shown in the panel self.rightFrame1 = Frame(self.window, bg="white", width=400, height=300, relief="solid", borderwidth=1, padx=20, pady=5) self.rightFrame1.grid(row=0, sticky="ne") # keep the frame the same size even if the widgets change size self.rightFrame1.grid_propagate(False) # Label Widgets for the right top Frame self.details_title = Label(self.rightFrame1, text="Employee Details", font="Ariel", bg="white") self.details_title.grid(row=0, column=0, columnspan=4, sticky="nw") self.name_label = Label(self.rightFrame1, text="Name", font="Arial", bg="white") self.name_label.grid(row=3, column=1, sticky="w") self.ID_label = Label(self.rightFrame1, text="ID", font="Arial", bg="white") self.ID_label.grid(row=2, column=1, sticky="w") self.Rate_label = Label(self.rightFrame1, text="Rate", font="Arial", bg="white") self.Rate_label.grid(row=4, column=1, sticky="w") self.Hol_Rate_label = Label(self.rightFrame1, text="Holiday Rate", font="Arial", bg="white") self.Hol_Rate_label.grid(row=5, column=1, sticky="w") self.Banked_Hours_label = Label(self.rightFrame1, text="Banked Hours", font="Arial", bg="white") self.Banked_Hours_label.grid(row=6, column=1, sticky="w") self.Banked_Salary_label = Label(self.rightFrame1, text="Banked Salary", font="Arial", bg="white") self.Banked_Salary_label.grid(row=7, column=1, sticky="w") # RIGHT BOTTOM PANEL # This panel contains the buttons the user can select to perform operations on the employees data self.rightFrame2 = Frame(self.window, bg="white", width=400, height=200) self.rightFrame2.grid(row=0, sticky="se") # organises the grid of the panel to look good self.rightFrame2.grid_propagate( False) #Stops the frame resizing to widget self.rightFrame2.grid_columnconfigure(0, weight=1) self.rightFrame2.grid_columnconfigure(1, weight=1) self.rightFrame2.grid_rowconfigure(0, weight=1) self.rightFrame2.grid_rowconfigure(1, weight=1) self.rightFrame2.grid_rowconfigure(2, weight=1) # Define the buttons of the bottom right panel self.add_user_button = Button(self.rightFrame2, text="New Employee", font="Arial", relief="groove", command=self.addTodB) self.add_user_button.grid(row=0, column=0, sticky="nsew") self.add_hours_button = Button(self.rightFrame2, text="Add Hours", font="Arial", relief="groove", command=lambda: self.ChangeHours(False)) self.add_hours_button.grid(row=0, column=1, sticky="nsew") self.delete_employee_button = Button(self.rightFrame2, text="Delete Employee", relief="groove", font="Arial", command=self.deleteUser) self.delete_employee_button.grid(row=1, column=0, sticky="nsew") self.edit_user_info_bt = Button(self.rightFrame2, text="Edit Employee", relief="groove", font="Arial", command=self.editEmployeeDetials) self.edit_user_info_bt.grid(row=1, column=1, sticky="nsew") self.remove_hours_btn = Button(self.rightFrame2, text="Remove Hours", relief="groove", font="Arial", command=lambda: self.ChangeHours(True)) self.remove_hours_btn.grid(row=2, column=0, sticky="nsew") self.number = 0 # check if there are any employees in the database # if there is not then display no employees in the list list_box # If there are then display the employees dbList = dbFunctionsV2.getAllUsers() print(dbList) if len(dbList) > 0: # lets get a list of employees and display them self.viewAll() self.list_box.selection_set(0) self.showDetails(self.list_box.curselection()[0]) else: self.list_box.insert(END, "No Employees in Database") # When a item in the list is clicked then show the data in the RIGHT TOP FRAME def showDetailsLive(self, event): # get list item number index = self.list_box.curselection()[0] # pass to showDetails function self.showDetails(index) # gets the information of the employee selected in the listbox and loads it into the right panel def showDetails(self, index): #get tme employee details from the selection name = self.names[index] ID = self.IDs[index] rate = self.rates[index] hol_rate = self.hol_rate[index] banked_hours = self.banked_hours[index] banked_salary = round(self.banked_salary[index], 2) #note event is passed but is not needed print("Hello dynamic label") try: # if the labels have already been created than destroy them so that another employees details can be # displayed self.employee_name.destroy() self.employee_ID.destroy() self.employee_rate.destroy() self.employee_hol_rate.destroy() self.employee_banked_hours.destroy() self.employee_banked_salary.destroy() print("Destroyed") # Now display the newly selected employees details in the right top panel # name self.employee_name = Label(self.rightFrame1, text=name, font="Ariel", bg="white") self.employee_name.grid(row=3, column=2, sticky="w") # ID self.employee_ID = Label(self.rightFrame1, text=ID, font="Ariel", bg="white") self.employee_ID.grid(row=2, column=2, sticky="w") # Rate self.employee_rate = Label(self.rightFrame1, text="£ {} per hour".format(rate), font="Ariel", bg="white") self.employee_rate.grid(row=4, column=2, sticky="w") # holiday Hours self.employee_hol_rate = Label( self.rightFrame1, text="£ {} per hour".format(hol_rate), font="Ariel", bg="white") self.employee_hol_rate.grid(row=5, column=2, sticky="w") # non holiday hours self.employee_banked_hours = Label(self.rightFrame1, text=banked_hours, font="Ariel", bg="white") self.employee_banked_hours.grid(row=6, column=2, sticky="w") # total hours self.employee_banked_salary = Label( self.rightFrame1, text="£ {}".format(banked_salary), font="Ariel", bg="white") self.employee_banked_salary.grid(row=7, column=2, sticky="w") except: # If it the first employee to be selected since the program was opened then you do not need to deastroy the # labels print("First Time") # name self.employee_name = Label(self.rightFrame1, text=name, font="Ariel", bg="white") self.employee_name.grid(row=3, column=2, sticky="w") # ID self.employee_ID = Label(self.rightFrame1, text=ID, font="Ariel", bg="white") self.employee_ID.grid(row=2, column=2, sticky="w") # Rate self.employee_rate = Label(self.rightFrame1, text="£ {} per hour".format(rate), font="Ariel", bg="white") self.employee_rate.grid(row=4, column=2, sticky="w") # holiday Hours self.employee_hol_rate = Label( self.rightFrame1, text="£ {} per hour".format(hol_rate), font="Ariel", bg="white") self.employee_hol_rate.grid(row=5, column=2, sticky="w") # non holiday hours self.employee_banked_hours = Label(self.rightFrame1, text=banked_hours, font="Ariel", bg="white") self.employee_banked_hours.grid(row=6, column=2, sticky="w") # total hours self.employee_banked_salary = Label( self.rightFrame1, text="£ {}".format(banked_salary), font="Ariel", bg="white") self.employee_banked_salary.grid(row=7, column=2, sticky="w") # When a window that has been previously called by the user e.g. new employee has been closed this function is # called. It grabs all the employees from the database and displays them in the list box (especially good if a new # employee has just been added). It then sets the previous selection in the listbox to the current selction and # grabs the data from the database and displays it in the top right panel def destroyTest(self, event): print("Testing Destroyed") self.viewAll() # self.showDetails(True) self.showDetails(self.currentSelectionIndex) self.list_box.select_set(self.currentSelectionIndex) # This function calls to the database and gets all the employee data from the database. It then puts thsi information # into lists, it then creates a list of lists and returns that to the caller def getLatestData(self): usersList = dbFunctionsV2.getAllUsers() self.names = [] self.IDs = [] self.rates = [] self.hol_rate = [] self.banked_hours = [] self.banked_salary = [] # put that into some List for a in usersList: self.names.append(a[1]) self.IDs.append(a[0]) self.rates.append(a[2]) self.hol_rate.append(a[3]) self.banked_hours.append(a[4]) self.banked_salary.append(a[5]) return (self.names, self.IDs, self.rates, self.hol_rate, self.banked_hours, self.banked_salary) # This function displays all the employees in the database into the list box def viewAll(self): # clear the list first self.list_box.delete(0, END) # get a list of the users from the database self.getLatestData() # the emumerate function gets the index from the list value, this index # is then used to get the corresponding ID value # This loop puts the employee name and ID into the list box for the user to select for (i, e) in enumerate(self.names): self.list_box.insert(END, "{} {}".format(self.IDs[i], e)) # function that was used for some basic testing def add(self): self.number = self.number + 1 #print(self.number) # function that was used for some basic testing def printNumber(self): return self.number # This function is called when the new Employee button is pressed in the bottom right panel def addTodB(self): # Get the value from the list_box selected = self.list_box.curselection() if selected: print(selected) for index in selected: print(self.list_box.get(index)) name = self.list_box.get(index) else: name = "No Name" print("Open New Window") # Toplevel creates a window dependent on the Tk window root = Toplevel(self.window) # creates an instance of the UGUI class which opens the window to add an new employee userUpdateGUI = UGUI(root, name) # When the new employee window is closed the destroyNewUserWindow function is called root.bind("<Destroy>", self.destroyNewUserWindow) # When the add new employee window is closed this function is called # It selects the last employee in the listbox, which should be the new user and then shows their details def destroyNewUserWindow(self, message): self.viewAll() # self the last last item in the list which is the new users self.list_box.selection_set(len(self.names) - 1) print(len(self.names)) self.showDetails(self.list_box.curselection()[0]) # This functions deletes the employee that is selected in the listbox def deleteUser(self): print("Are you sure???") # get the ID of the current employee selected currentID = str(self.IDs[self.list_box.curselection()[0]]) # check is the user meant to hit the cancel button answer = messagebox.askyesno( "Delete", "Are you user you want to delete employee {}?".format(currentID)) # if the user does not want to delete the user than exit without deleting if not answer: return # call the database delete function passing the employee to be deleted ID dbFunctionsV2.deleteUser(currentID) # display the updated list of employees self.viewAll() # if there are no employees left in the database need to blank the details labels in the # right top panel print("Length of ID Array: ", len(self.IDs)) if len(self.IDs) == 0: self.employee_name.destroy() self.employee_ID.destroy() self.employee_rate.destroy() self.employee_hol_rate.destroy() self.employee_banked_hours.destroy() self.employee_banked_salary.destroy() else: # select first item in list self.list_box.selection_set(0) # show data for current selection self.showDetails(self.list_box.curselection()[0]) # this function is called when the employee details are to be editted def editEmployeeDetials(self): # get the details of the current selected employee BankedHours = float(self.banked_hours[self.list_box.curselection()[0]]) BankedSalary = float( self.banked_salary[self.list_box.curselection()[0]]) currentID = self.IDs[self.list_box.curselection()[0]] rate = float(self.rates[self.list_box.curselection()[0]]) hol_rate = float(self.hol_rate[self.list_box.curselection()[0]]) currentName = self.names[self.list_box.curselection()[0]] root = Toplevel(self.window) # create an instance of the editEmployeeDetails class and pass the employees details to it # this will launch an edit employee window for the user newWindow = editEmployeeDetails(root, currentID, currentName, rate, hol_rate) # save the position of the current selected employee in the list box self.currentSelectionIndex = self.list_box.curselection()[0] # When the window for the edit employee is closed run detroyTest function root.bind("<Destroy>", self.destroyTest) def finish(self): self.window.destroy() # this function is called when the user wants to add or subtract hours from the employees Hours Bank. # when the add hours button is pressed this function is called with remove = False, if the remove hours # button is pressed then remove = True. def ChangeHours(self, remove): # get the data for the selected employee BankedHours = float(self.banked_hours[self.list_box.curselection()[0]]) BankedSalary = float( self.banked_salary[self.list_box.curselection()[0]]) currentID = self.IDs[self.list_box.curselection()[0]] rate = float(self.rates[self.list_box.curselection()[0]]) hol_rate = float(self.hol_rate[self.list_box.curselection()[0]]) #if remove True then remove hours, if false then add hours # open a new window root = Toplevel(self.window) # create an instance of the class ChnageHoursWindow where the change hours window is launched ChangeHours = ChangeHoursWindow(root, BankedHours, BankedSalary, currentID, rate, hol_rate, remove) # have the slection in the list box that the user make` self.currentSelectionIndex = self.list_box.curselection()[0] # when the window to change hours is destroyed run the destroyTest function root.bind("<Destroy>", self.destroyTest)
class DrtGlueDemo: def __init__(self, examples): # Set up the main window. self._top = Tk() self._top.title("DRT Glue Demo") # Set up key bindings. self._init_bindings() # Initialize the fonts.self._error = None self._init_fonts(self._top) self._examples = examples self._readingCache = [None for example in examples] # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Set the data to None self._curExample = -1 self._readings = [] self._drs = None self._drsWidget = None self._error = None self._init_glue() # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_exampleListbox(self._top) self._init_readingListbox(self._top) self._init_canvas(self._top) # Resize callback self._canvas.bind("<Configure>", self._configure) ######################################### ## Initialization Helpers ######################################### def _init_glue(self): tagger = RegexpTagger([ ("^(David|Mary|John)$", "NNP"), ( "^(walks|sees|eats|chases|believes|gives|sleeps|chases|persuades|tries|seems|leaves)$", "VB", ), ("^(go|order|vanish|find|approach)$", "VB"), ("^(a)$", "ex_quant"), ("^(every)$", "univ_quant"), ("^(sandwich|man|dog|pizza|unicorn|cat|senator)$", "NN"), ("^(big|gray|former)$", "JJ"), ("^(him|himself)$", "PRP"), ]) depparser = MaltParser(tagger=tagger) self._glue = DrtGlue(depparser=depparser, remove_duplicates=False) def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget("size")) self._boldfont = Font(family="helvetica", weight="bold", size=self._size.get()) self._font = Font(family="helvetica", size=self._size.get()) if self._size.get() < 0: big = self._size.get() - 2 else: big = self._size.get() + 2 self._bigfont = Font(family="helvetica", weight="bold", size=big) def _init_exampleListbox(self, parent): self._exampleFrame = listframe = Frame(parent) self._exampleFrame.pack(fill="both", side="left", padx=2) self._exampleList_label = Label(self._exampleFrame, font=self._boldfont, text="Examples") self._exampleList_label.pack() self._exampleList = Listbox( self._exampleFrame, selectmode="single", relief="groove", background="white", foreground="#909090", font=self._font, selectforeground="#004040", selectbackground="#c0f0c0", ) self._exampleList.pack(side="right", fill="both", expand=1) for example in self._examples: self._exampleList.insert("end", (" %s" % example)) self._exampleList.config(height=min(len(self._examples), 25), width=40) # Add a scrollbar if there are more than 25 examples. if len(self._examples) > 25: listscroll = Scrollbar(self._exampleFrame, orient="vertical") self._exampleList.config(yscrollcommand=listscroll.set) listscroll.config(command=self._exampleList.yview) listscroll.pack(side="left", fill="y") # If they select a example, apply it. self._exampleList.bind("<<ListboxSelect>>", self._exampleList_select) def _init_readingListbox(self, parent): self._readingFrame = listframe = Frame(parent) self._readingFrame.pack(fill="both", side="left", padx=2) self._readingList_label = Label(self._readingFrame, font=self._boldfont, text="Readings") self._readingList_label.pack() self._readingList = Listbox( self._readingFrame, selectmode="single", relief="groove", background="white", foreground="#909090", font=self._font, selectforeground="#004040", selectbackground="#c0f0c0", ) self._readingList.pack(side="right", fill="both", expand=1) # Add a scrollbar if there are more than 25 examples. listscroll = Scrollbar(self._readingFrame, orient="vertical") self._readingList.config(yscrollcommand=listscroll.set) listscroll.config(command=self._readingList.yview) listscroll.pack(side="right", fill="y") self._populate_readingListbox() def _populate_readingListbox(self): # Populate the listbox with integers self._readingList.delete(0, "end") for i in range(len(self._readings)): self._readingList.insert("end", (" %s" % (i + 1))) self._readingList.config(height=min(len(self._readings), 25), width=5) # If they select a example, apply it. self._readingList.bind("<<ListboxSelect>>", self._readingList_select) def _init_bindings(self): # Key bindings are a good thing. self._top.bind("<Control-q>", self.destroy) self._top.bind("<Control-x>", self.destroy) self._top.bind("<Escape>", self.destroy) self._top.bind("n", self.next) self._top.bind("<space>", self.next) self._top.bind("p", self.prev) self._top.bind("<BackSpace>", self.prev) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill="none", side="bottom", padx=3, pady=2) Button( buttonframe, text="Prev", background="#90c0d0", foreground="black", command=self.prev, ).pack(side="left") Button( buttonframe, text="Next", background="#90c0d0", foreground="black", command=self.next, ).pack(side="left") def _configure(self, event): self._autostep = 0 (x1, y1, x2, y2) = self._cframe.scrollregion() y2 = event.height - 6 self._canvas["scrollregion"] = "%d %d %d %d" % (x1, y1, x2, y2) self._redraw() def _init_canvas(self, parent): self._cframe = CanvasFrame( parent, background="white", # width=525, height=250, closeenough=10, border=2, relief="sunken", ) self._cframe.pack(expand=1, fill="both", side="top", pady=2) canvas = self._canvas = self._cframe.canvas() # Initially, there's no tree or text self._tree = None self._textwidgets = [] self._textline = None def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="Exit", underline=1, command=self.destroy, accelerator="q") menubar.add_cascade(label="File", underline=0, menu=filemenu) actionmenu = Menu(menubar, tearoff=0) actionmenu.add_command(label="Next", underline=0, command=self.next, accelerator="n, Space") actionmenu.add_command(label="Previous", underline=0, command=self.prev, accelerator="p, Backspace") menubar.add_cascade(label="Action", underline=0, menu=actionmenu) optionmenu = Menu(menubar, tearoff=0) optionmenu.add_checkbutton( label="Remove Duplicates", underline=0, variable=self._glue.remove_duplicates, command=self._toggle_remove_duplicates, accelerator="r", ) menubar.add_cascade(label="Options", underline=0, menu=optionmenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_radiobutton( label="Tiny", variable=self._size, underline=0, value=10, command=self.resize, ) viewmenu.add_radiobutton( label="Small", variable=self._size, underline=0, value=12, command=self.resize, ) viewmenu.add_radiobutton( label="Medium", variable=self._size, underline=0, value=14, command=self.resize, ) viewmenu.add_radiobutton( label="Large", variable=self._size, underline=0, value=18, command=self.resize, ) viewmenu.add_radiobutton( label="Huge", variable=self._size, underline=0, value=24, command=self.resize, ) menubar.add_cascade(label="View", underline=0, menu=viewmenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label="About", underline=0, command=self.about) menubar.add_cascade(label="Help", underline=0, menu=helpmenu) parent.config(menu=menubar) ######################################### ## Main draw procedure ######################################### def _redraw(self): canvas = self._canvas # Delete the old DRS, widgets, etc. if self._drsWidget is not None: self._drsWidget.clear() if self._drs: self._drsWidget = DrsWidget(self._canvas, self._drs) self._drsWidget.draw() if self._error: self._drsWidget = DrsWidget(self._canvas, self._error) self._drsWidget.draw() ######################################### ## Button Callbacks ######################################### def destroy(self, *e): self._autostep = 0 if self._top is None: return self._top.destroy() self._top = None def prev(self, *e): selection = self._readingList.curselection() readingListSize = self._readingList.size() # there are readings if readingListSize > 0: # if one reading is currently selected if len(selection) == 1: index = int(selection[0]) # if it's on (or before) the first item if index <= 0: self._select_previous_example() else: self._readingList_store_selection(index - 1) else: # select its first reading self._readingList_store_selection(readingListSize - 1) else: self._select_previous_example() def _select_previous_example(self): # if the current example is not the first example if self._curExample > 0: self._exampleList_store_selection(self._curExample - 1) else: # go to the last example self._exampleList_store_selection(len(self._examples) - 1) def next(self, *e): selection = self._readingList.curselection() readingListSize = self._readingList.size() # if there are readings if readingListSize > 0: # if one reading is currently selected if len(selection) == 1: index = int(selection[0]) # if it's on (or past) the last item if index >= (readingListSize - 1): self._select_next_example() else: self._readingList_store_selection(index + 1) else: # select its first reading self._readingList_store_selection(0) else: self._select_next_example() def _select_next_example(self): # if the current example is not the last example if self._curExample < len(self._examples) - 1: self._exampleList_store_selection(self._curExample + 1) else: # go to the first example self._exampleList_store_selection(0) def about(self, *e): ABOUT = ( "NLTK Discourse Representation Theory (DRT) Glue Semantics Demo\n" + "Written by Daniel H. Garrette") TITLE = "About: NLTK DRT Glue Demo" try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def postscript(self, *e): self._autostep = 0 self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) self._bigfont.configure(size=-(abs(size + 2))) self._redraw() def _toggle_remove_duplicates(self): self._glue.remove_duplicates = not self._glue.remove_duplicates self._exampleList.selection_clear(0, "end") self._readings = [] self._populate_readingListbox() self._readingCache = [None for ex in self._examples] self._curExample = -1 self._error = None self._drs = None self._redraw() def _exampleList_select(self, event): selection = self._exampleList.curselection() if len(selection) != 1: return self._exampleList_store_selection(int(selection[0])) def _exampleList_store_selection(self, index): self._curExample = index example = self._examples[index] self._exampleList.selection_clear(0, "end") if example: cache = self._readingCache[index] if cache: if isinstance(cache, list): self._readings = cache self._error = None else: self._readings = [] self._error = cache else: try: self._readings = self._glue.parse_to_meaning(example) self._error = None self._readingCache[index] = self._readings except Exception as e: self._readings = [] self._error = DrtVariableExpression( Variable("Error: " + str(e))) self._readingCache[index] = self._error # add a star to the end of the example self._exampleList.delete(index) self._exampleList.insert(index, (" %s *" % example)) self._exampleList.config(height=min( len(self._examples), 25), width=40) self._populate_readingListbox() self._exampleList.selection_set(index) self._drs = None self._redraw() def _readingList_select(self, event): selection = self._readingList.curselection() if len(selection) != 1: return self._readingList_store_selection(int(selection[0])) def _readingList_store_selection(self, index): reading = self._readings[index] self._readingList.selection_clear(0, "end") if reading: self._readingList.selection_set(index) self._drs = reading.simplify().normalize().resolve_anaphora() self._redraw()
class FPLGUI: SPLASH_WIDTH = 350 SPLASH_HEIGHT = 250 def __init__(self): # Get database folder. self.srcDir = os.path.dirname(os.path.abspath(__file__)) self.databaseDir = os.path.join(os.path.dirname(self.srcDir),'database') self.supportFilesDir = os.path.join(os.path.dirname(self.srcDir),'supportFiles') # Create Database folder. if not os.path.isdir(self.databaseDir): os.makedirs(self.databaseDir) # Show splash splashWindow = Tk() splashWindow.title('FPLGUI') self.screenWidth = splashWindow.winfo_screenwidth() # width of the screen self.screenHeight = splashWindow.winfo_screenheight() # height of the screen x = round((self.screenWidth/2) - (self.SPLASH_WIDTH/2)) y = round((self.screenHeight/2) - (self.SPLASH_HEIGHT/2)) splashWindow.geometry('{}x{}+{}+{}'.format(self.SPLASH_WIDTH,self.SPLASH_HEIGHT,x,y)) splashWindow.resizable(0, 0) splashWindow.iconbitmap(os.path.join(self.supportFilesDir,'FPLGUI.ico')) Label(splashWindow,text="Loading Navdata, Please wait.",justify='left',font=("Helvetica", 14)).place(relx=0.1,rely=0.1,anchor='nw') with open(os.path.join(self.supportFilesDir,'startupMessage.txt')) as startupFile: Label(splashWindow, text=startupFile.read(),justify='left',font=("Helvetica", 8)).place(relx=0.1, rely=0.4, anchor='nw') splashWindow.update() # check options for X-Plane directory self.getOptions() # Let user select X-Plane dir and write options. if self.xPlaneDir is None: OptionsWindow(splashWindow,self.databaseDir,'FPLGUI: Set inital options') self.getOptions() while self.xPlaneDir is None: showwarning('XplaneDir not specified', 'XplaneDir is mandatory. Specify it first!') OptionsWindow(splashWindow,self.databaseDir,'FPLGUI: Set inital options') self.getOptions() # Get navdata folder. if os.path.exists(os.path.join(self.xPlaneDir,'Custom Data','earth_fix.dat')) and \ os.path.exists(os.path.join(self.xPlaneDir,'Custom Data','earth_nav.dat')) and \ os.path.exists(os.path.join(self.xPlaneDir,'Custom Data','earth_awy.dat')) and \ os.path.exists(os.path.join(self.xPlaneDir,'Custom Data','apt.csv')): self.navdataDir = os.path.join(self.xPlaneDir,'Custom Data') else: self.navdataDir = os.path.join(self.xPlaneDir,'Resources','default data') #inititalize Fpl-object self.fplPath = os.path.join(self.xPlaneDir,'Resources\\plugins\\X-IvAp Resources\\Flightplans') self.fpl = Fpl(self.fplPath) # Load Fixes # self.fpl.getFixes(os.path.join(self.navdataDir,'earth_fix.dat')) # self.fpl.getNavaids(os.path.join(self.navdataDir,'earth_nav.dat')) # self.fpl.getAirports(os.path.join(self.navdataDir,'apt.csv')) # self.fpl.getAirways(os.path.join(self.navdataDir,'earth_awy.dat')) # Remove Splash. splashWindow.destroy() # Create main window self.master = Tk() self.master.title('FPLGUI') self.master.resizable(0, 0) ## menu ## menubar = Menu(self.master) filemenu = Menu(menubar,tearoff=0) filemenu.add_command(label="Clear",command=self.clear) filemenu.add_command(label="Send to XP",command=self.send) filemenu.add_separator() filemenu.add_command(label="Load",command=self.load) filemenu.add_command(label="Save",command=self.save) filemenu.add_separator() filemenu.add_command(label="Exit",command=self.master.quit) menubar.add_cascade(label="File", menu=filemenu) acmenu = Menu(menubar,tearoff=0) acmenu.add_command(label="Load Template",command=self.acLoad) acmenu.add_command(label="Save Template",command=self.acSave) menubar.add_cascade(label="Aircraft", menu=acmenu) utilmenu = Menu(menubar,tearoff=0) utilmenu.add_command(label="Import Route",command=self.importRoute) utilmenu.add_separator() utilmenu.add_command(label="Open Simbrief",command=self.simbrief) utilmenu.add_command(label="Simbrief process",command= lambda: self.simbrief(True)) utilmenu.add_command(label="Flightaware",command=self.flightaware) utilmenu.add_separator() utilmenu.add_command(label="Show at Skyvector",command=self.showSkyvector) utilmenu.add_command(label="Export to X-Plane",command=self.export2xp) utilmenu.add_command(label="Export to FF A320",command=self.export2FFA320) utilmenu.add_separator() utilmenu.add_command(label="Show FPL text",command=self.showFplText) utilmenu.add_separator() utilmenu.add_command(label="Options",command=lambda: OptionsWindow(self.master,self.databaseDir)) menubar.add_cascade(label="Extras",menu=utilmenu) self.master.config(menu=menubar) ## row 0-1 ## ## send button self.b_send = Button(self.master, text = "Send", command=self.send) self.b_send.grid(row=0, column=0, rowspan = 2) ## callsign self.l_callsign = Label(self.master, text="7 a/c ident") self.l_callsign.grid(row=0, column=1) self.callsign = StringVar(self.master) self.e_callsign = Entry(self.master, textvariable=self.callsign) self.e_callsign.grid(row=1, column=1) self.callsign.trace_add('write', self.e_callsignCB) ## rules self.l_rules = Label(self.master, text="8 flight rules") self.l_rules.grid(row=0, column=2) self.rules = StringVar(self.master) self.rules.set("V") self.o_rules = OptionMenu(self.master, self.rules, "V", "I", "Y", "Z") self.o_rules.grid(row=1, column=2) ## flighttype self.l_flighttype = Label(self.master, text=" type of flight") self.l_flighttype.grid(row=0, column=3) self.flighttype = StringVar(self.master) self.flighttype.set("S") self.o_flighttype = OptionMenu(self.master, self.flighttype, "S", "N", "G", "M", "X") self.o_flighttype.grid(row=1, column=3) ## row 2-3 ## ## number self.l_number = Label(self.master, text="9 number") self.l_number.grid(row=2, column=0) self.number = StringVar(self.master) self.e_number = Entry(self.master, textvariable=self.number) self.e_number.grid(row=3, column=0) self.number.trace_add('write', self.e_numberCB) ## type of aircraft self.l_actype = Label(self.master, text="type of aircraft") self.l_actype.grid(row=2, column=1) self.actype = StringVar(self.master) self.e_actype = Entry(self.master, textvariable=self.actype) self.e_actype.grid(row=3, column=1) self.actype.trace_add('write', self.e_actypeCB) ## wakecat self.l_wakecat = Label(self.master, text="wake turb cat") self.l_wakecat.grid(row=2, column=2) self.wakecat = StringVar(self.master) self.wakecat.set("L") self.o_wakecat = OptionMenu(self.master, self.wakecat, "L", "M", "H", "J") self.o_wakecat.grid(row=3, column=2) ## equipment self.l_equipment = Label(self.master, text="10 equipment") self.l_equipment.grid(row=2, column=3) self.equipment = StringVar(self.master) self.e_equipment = Entry(self.master, textvariable=self.equipment) self.e_equipment.grid(row=3, column=3) self.equipment.trace_add('write', self.e_equipmentCB) ## equipment self.l_transponder = Label(self.master, text="transponder") self.l_transponder.grid(row=2, column=4) self.transponder = StringVar(self.master) self.e_transponder = Entry(self.master, textvariable=self.transponder) self.e_transponder.grid(row=3, column=4) self.transponder.trace_add('write', self.e_transponderCB) ## row 4-5 ## ## depicao self.l_depicao = Label(self.master, text="13 departure aerodrome") self.l_depicao.grid(row=4, column=0) self.depicao = StringVar(self.master) self.e_depicao = Entry(self.master, textvariable=self.depicao) self.e_depicao.grid(row=5, column=0) self.depicao.trace_add('write', self.e_depicaoCB) ## deptime self.l_deptime = Label(self.master, text="departure time") self.l_deptime.grid(row=4, column=1) self.deptime = StringVar(self.master) self.e_deptime = Entry(self.master, textvariable=self.deptime) self.e_deptime.grid(row=5, column=1) self.deptime.trace_add('write', self.e_deptimeCB) ## row 6-7 ## ## speed self.l_speed = Label(self.master, text="15 cruising speed") self.l_speed.grid(row=6, column=0, columnspan=2) self.speedtype = StringVar(self.master) self.speedtype.set("N") self.o_speedtype = OptionMenu(self.master, self.speedtype, "N", "M") self.o_speedtype.grid(row=7, column=0) self.speed = StringVar(self.master) self.e_speed = Entry(self.master, textvariable=self.speed) self.e_speed.grid(row=7, column=1) self.speed.trace_add('write', self.e_speedCB) ## level self.l_level = Label(self.master, text="flight altutude/level") self.l_level.grid(row=6, column=2, columnspan=2) self.leveltype = StringVar(self.master) self.leveltype.set("F") self.o_level = OptionMenu(self.master, self.leveltype, "F", "A", "VFR") self.o_level.grid(row=7, column=2) self.level = StringVar(self.master) self.e_level = Entry(self.master, textvariable=self.level) self.e_level.grid(row=7, column=3) self.level.trace_add('write', self.e_levelCB) ## row 8-9 ## ##route self.l_route = Label(self.master, text=" route") self.l_route.grid(row=8, column=0, sticky=W) self.route = StringVar(self.master) self.e_route = Entry(self.master, width=105, textvariable=self.route) self.e_route.grid(row=9, column=0, columnspan=5) self.route.trace_add('write', self.e_routeCB) ## row 10-11 ## ## destinationAP self.l_desticao = Label(self.master, text="13 destination aerodrome") self.l_desticao.grid(row=10, column=0) self.desticao = StringVar(self.master) self.e_desticao = Entry(self.master, textvariable=self.desticao) self.e_desticao.grid(row=11, column=0) self.desticao.trace_add('write', self.e_desticaoCB) ## duration self.l_eet = Label(self.master, text="EET") self.l_eet.grid(row=10, column=1) self.eet = StringVar(self.master) self.e_eet = Entry(self.master, textvariable=self.eet) self.e_eet.grid(row=11, column=1) self.eet.trace_add('write', self.e_eetCB) ## alternates self.l_alticao = Label(self.master, text="alternate") self.l_alticao.grid(row=10, column=2) self.alticao = StringVar(self.master) self.e_alticao = Entry(self.master, textvariable=self.alticao) self.e_alticao.grid(row=11, column=2) self.alticao.trace_add('write', self.e_alticaoCB) self.l_alt2icao = Label(self.master, text="2nd alternate") self.l_alt2icao.grid(row=10, column=3) self.alt2icao = StringVar(self.master) self.e_alt2icao = Entry(self.master, textvariable=self.alt2icao) self.e_alt2icao.grid(row=11, column=3) self.alt2icao.trace_add('write', self.e_alt2icaoCB) ## row 12-13 ## ##other self.l_other = Label(self.master, text="other") self.l_other.grid(row=12, column=0, sticky=W) self.other = StringVar(self.master) self.e_other = Entry(self.master, width=105, textvariable=self.other) self.e_other.grid(row=13, column=0, columnspan=5) self.other.trace_add('write', self.e_otherCB) ## row 14-15 ## ##endurance self.l_endurance = Label(self.master, text="19 endurance") self.l_endurance.grid(row=14, column=0) self.endurance = StringVar(self.master) self.e_endurance = Entry(self.master, textvariable=self.endurance) self.e_endurance.grid(row=15, column=0) self.endurance.trace_add('write', self.e_enduranceCB) ##persons self.l_pob = Label(self.master, text="persons on board") self.l_pob.grid(row=14, column=1) self.pob = StringVar(self.master) self.e_pob = Entry(self.master, textvariable=self.pob) self.e_pob.grid(row=15, column=1) self.pob.trace_add('write', self.e_pobCB) ##pic self.l_pic = Label(self.master, text="pilot in command") self.l_pic.grid(row=14, column=2) self.pic = StringVar(self.master) self.e_pic = Entry(self.master, width=40, textvariable=self.pic) self.e_pic.grid(row=15, column=2, columnspan=2) self.pic.trace_add('write', self.e_picCB) ## row 16 ## ##empty empty = Label(self.master, text="") empty.grid(row=16, column=0) self.updateContent() # Set master window options self.master.update() masterWidth = self.master.winfo_width() masterHeight = self.master.winfo_height() x = round((self.screenWidth/2) - (masterWidth/2)) y = round((self.screenHeight/2) - (masterHeight/2)) self.master.geometry('{}x{}+{}+{}'.format(masterWidth,masterHeight,x,y)) self.master.title('FPLGUI') self.master.resizable(0, 0) self.master.iconbitmap(os.path.join(self.supportFilesDir,'FPLGUI.ico')) # Start master mainloop. self.master.mainloop() def updateContent(self): ## row 0-1 ## ## callsign self.e_callsign.delete(0, END) self.e_callsign.insert(0,self.fpl.callsign) ## rules if self.fpl.rules: self.rules.set(self.fpl.rules) else: self.rules.set("V") ## flightType if self.fpl.flighttype: self.flighttype.set(self.fpl.flighttype) else: self.flighttype.set("S") ## row 2-3 ## ## number self.e_number.delete(0, END) self.e_number.insert(0,self.fpl.number) ## type of aircraft self.e_actype.delete(0, END) self.e_actype.insert(0,self.fpl.actype) ## wakecat if self.fpl.wakecat: self.wakecat.set(self.fpl.wakecat) else: self.wakecat.set("L") ## equipment self.e_equipment.delete(0, END) self.e_equipment.insert(0,self.fpl.equipment) ## equipment self.e_transponder.delete(0, END) self.e_transponder.insert(0,self.fpl.transponder) ## row 4-5 ## ## depicao self.e_depicao.delete(0, END) self.e_depicao.insert(0,self.fpl.depicao) ## deptime self.e_deptime.delete(0, END) self.e_deptime.insert(0,self.fpl.deptime) ## row 6-7 ## ## speed if self.fpl.speedtype: self.speedtype.set(self.fpl.speedtype) else: self.speedtype.set("N") self.e_speed.delete(0, END) self.e_speed.insert(0,self.fpl.speed) ## level if self.fpl.leveltype: self.leveltype.set(self.fpl.leveltype) else: self.leveltype.set("N") self.e_level.delete(0, END) self.e_level.insert(0,self.fpl.level) ## row 8-9 ## ##route self.e_route.delete(0, END) self.e_route.insert(0,self.fpl.route) ## row 10-11 ## ## destinationAP self.e_desticao.delete(0, END) self.e_desticao.insert(0,self.fpl.desticao) ## eet self.e_eet.delete(0, END) self.e_eet.insert(0,self.fpl.eet) ## alternates self.e_alticao.delete(0, END) self.e_alticao.insert(0,self.fpl.alticao) self.e_alt2icao.delete(0, END) self.e_alt2icao.insert(0,self.fpl.alt2icao) ## row 12-13 ## ##other self.e_other.delete(0, END) self.e_other.insert(0,self.fpl.other) ## row 14-15 ## ##endurance self.e_endurance.delete(0, END) self.e_endurance.insert(0,self.fpl.endurance) ##persons self.e_pob.delete(0, END) self.e_pob.insert(0,self.fpl.pob) ##pic self.e_pic.delete(0, END) self.e_pic.insert(0,self.fpl.pic) def updateFpl(self): self.fpl.callsign = self.e_callsign.get() self.fpl.pic = self.e_pic.get() self.fpl.speedtype = self.speedtype.get() self.fpl.pob = self.e_pob.get() self.fpl.endurance = self.e_endurance.get() self.fpl.other = self.e_other.get() self.fpl.alt2icao = self.e_alt2icao.get() self.fpl.alticao = self.e_alticao.get() self.fpl.eet = self.e_eet.get() self.fpl.desticao = self.e_desticao.get() self.fpl.route = self.e_route.get() self.fpl.level = self.e_level.get() self.fpl.leveltype = self.leveltype.get() self.fpl.speed = self.e_speed.get() self.fpl.deptime = self.e_deptime.get() self.fpl.depicao = self.e_depicao.get() self.fpl.transponder = self.e_transponder.get() self.fpl.equipment = self.e_equipment.get() self.fpl.wakecat = self.wakecat.get() self.fpl.actype = self.e_actype.get() self.fpl.number = self.e_number.get() self.fpl.flighttype = self.flighttype.get() self.fpl.rules = self.rules.get() def load(self): filepath = askopenfilename(filetypes=[("X-Plane Flightplan","*.fpl"),("All","*")],initialdir=self.fpl.path) self.fpl.load(filepath) self.updateContent() def save(self): self.updateFpl() filepath = asksaveasfilename(filetypes=[("X-Plane Flightplan","*.fpl"),("All","*")],initialdir=self.fpl.path) if filepath[-4:] != ".fpl": filepath += ".fpl" self.fpl.save(filepath) print("saved!") def send(self): self.updateFpl() self.fpl.save(self.fpl.path + "\\Default.fpl") if (len(self.fpl.route) + len(self.fpl.other)) > 255: showwarning("Too long entries",'"Route" and "Other" entries are too long ({}/255 characters combined)!\nThis will lead to disconnection in flight.\nTry to shorten these fields.'.format(len(self.fpl.route) + len(self.fpl.other))) print("generated!") def clear(self): self.e_callsign.delete(0, END) self.e_number.delete(0, END) self.e_actype.delete(0, END) self.e_equipment.delete(0, END) self.e_transponder.delete(0, END) self.e_depicao.delete(0, END) self.e_deptime.delete(0, END) self.e_speed.delete(0, END) self.e_level.delete(0, END) self.e_route.delete(0, END) self.e_desticao.delete(0, END) self.e_eet.delete(0, END) self.e_alticao.delete(0, END) self.e_alt2icao.delete(0, END) self.e_other.delete(0, END) self.e_endurance.delete(0, END) self.e_pob.delete(0, END) self.e_pic.delete(0, END) def getOptions(self): # Init options with None self.xPlaneDir = None # Get options if os.path.isfile(os.path.join(self.databaseDir,'FPLGUI.cfg')): self.config = ConfigParser.RawConfigParser() self.config.read(os.path.join(self.databaseDir,'FPLGUI.cfg')) # xPlaneDir try: self.xPlaneDir = self.config.get('FPLGUI','XPLANEDIR') except ConfigParser.NoSectionError: return except ConfigParser.NoOptionError: pass if self.xPlaneDir is not None and not re.match(r'[A-Za-z]:\\',self.xPlaneDir): self.xPlaneDir = None def updateRouteDbButtonCB(self): # dbUpdated = False downloadUrl = "https://www.ivao.de/scripts/php/cms/pfpx" # curDate = int(time.time()) # lastUpdate = 0 #TODO: replace, when options implemented # timeDiff = curDate - lastUpdate timeDiff = 0 if timeDiff > 864000: ivaoDatabase = urlopen(downloadUrl) database = ivaoDatabase.read() # lastUpdate = int(time.time()) # dbUpdated = True DataFile = open(self.srcDir + r"\routeDatabase.txt",'w') DataFile.write(database) DataFile.close() def optionsButtonOkCB(self): self.top.destroy() def optionsButtonCancelCB(self): self.top.destroy() def simbrief(self,*args): self.updateFpl() url = 'https://www.simbrief.com/system/dispatch.php?' options = '' # Airports. if self.fpl.depicao and self.fpl.desticao: url = '{}&orig={}&dest={}'.format(url,self.fpl.depicao,self.fpl.desticao) else: showwarning('Airport missing','Departure and destination airport is mandatory for Simbrief.\nSpecify them first!') # Times if self.fpl.deptime: deph = self.fpl.deptime[0:1] depm = self.fpl.deptime[2:3] options = '{}&deph={}&depm={}'.format(options,deph,depm) else: showwarning('Time missing','Departure time is mandatory for Simbrief.\nSpecify it first!') return # Aircraft. if self.fpl.actype: url = '{}&type={}'.format(url,self.fpl.actype) else: showwarning('Aircraft type missing','Aircraft type is mandatory for Simbrief.\nSpecify it first!') # Route. if self.fpl.route: url = '{}&route={}'.format(url,self.fpl.route) else: showinfo('Route missing','The route is missing and will be created by Simbrief.\nNote: These routes are often not CFMU valid!') # Flightlevel. if self.fpl.level: url = '{}&fl={}00'.format(url,self.fpl.level) # Date. reFind = re.search('(?<=DOF/)\d{6}',self.fpl.other) if reFind: date = reFind.group() date = datetime.datetime(int(date[0:2])+2000,int(date[2:4]),int(date[4:6])) else: date = datetime.datetime.today() url = '{}&date={}'.format(url,date.strftime('%d%b%y')) # Airline, fltnum, registration, selcal. airline = '' fltnum = '' registration = '' selcal = '' # Airline, fltnum, registration from Callsign field. reFind = re.search('[A-Z]{5}',self.fpl.callsign) if reFind: registration = reFind.group() else: reFindAirline = re.search('[A-Z]{3}(?=\w+)',self.fpl.callsign) reFindFltnum = re.search('(?<=[A-Z]{3})\w+',self.fpl.callsign) if reFindAirline and reFindFltnum: airline = reFindAirline.group() fltnum = reFindFltnum.group() else: print('invalid Callsign!') # Registration (REG/) from other field. if not registration: reFind = re.search('(?<=REG/)[A-Z]{5}',self.fpl.other) if reFind: registration = reFind.group() # Airline (OPR/) from other field. if not airline: reFind = re.search('(?<=OPR/)[A-Z]{3}',self.fpl.other) if reFind: airline = reFind.group() # Selcal (SEL) from other field. reFind = re.search('(?<=SEL/)[A-S]{4}',self.fpl.other) if reFind: selcal = reFind.group() # Add the found values if airline: url = '{}&airline={}'.format(url,airline) if fltnum: url = '{}&fltnum={}'.format(url,fltnum) if registration: url = '{}®={}'.format(url,registration) if selcal: url = '{}&selcal={}'.format(url,selcal) # ----FPL---- # Alternates. # ----ADD---- # Extra Fuel. # Cont fuel # Reserve Fuel. # Taxi out. # Taxi in. # Cargo. # Pax. # Dep rwy. # Arr rwy. # CI # ETOPS. # Specify options. # For Simbrief process. if len(args) and args[0]: url = '{}&planformat=LIDO&units=KGS&navlog=0&etops=0&stepclimbs=0&tlr=0¬ams=0&firnot=0&maps=none'.format(url) # For show Simbiref. else: url = '{}&planformat=LIDO&units=KGS&navlog=1&etops=1&stepclimbs=0&tlr=0¬ams=1&firnot=1&maps=detail'.format(url) # print(url) # pass # Open simbrief. webbrowser.open(url,new=2) def simbriefOpen(self): pass def simbriefProcess(self): pass ## Display flights for departure and destination airport on flightaware. def flightaware(self): self.updateFpl() url = 'https://de.flightaware.com/live/findflight?origin={}&destination={}'.format(self.fpl.depicao,self.fpl.desticao) webbrowser.open(url,new=2) def importRoute(self): self.updateFpl() with open(self.databaseDir + r"\routeDatabase.txt") as dataFile: database = dataFile.read() self.fpl.desticao = self.fpl.desticao.upper() self.fpl.depicao = self.fpl.depicao.upper() patRte = re.compile(self.fpl.depicao + self.fpl.desticao + r"\d{2};.+\n") routes = patRte.findall(database) ## parse routes self.routing = [] self.comment = [] self.fl = [] maxLenCom = 0 for k in range(len(routes)): parts = re.split(";",routes[k]) self.routing.append(parts[1]) curComment = parts[2] #curComment = curComment[1:-2] curComment = re.split(",",curComment) curFL = curComment[0] curCommentList = curComment[1:] curComment = "" for l in curCommentList: curComment += l self.comment.append(curComment[1:-2]) if len(curComment[1:-2]) > maxLenCom: maxLenCom = len(curComment[1:-2]) self.fl.append(curFL) ## show window self.importRouteTop = Toplevel(self.master) if len(self.routing) > 0: Label(self.importRouteTop, text="Choose a Route").pack() self.importRouteListboxTl = Listbox(self.importRouteTop,width=180) self.importRouteListboxTl.pack() for k in range(len(self.routing)): self.importRouteListboxTl.insert(END, "{:11} {:50} {}".format(self.fl[k],self.comment[k],self.routing[k])) self.importRouteListboxTl.selection_set(0) self.tlOkButton = Button(self.importRouteTop,text="OK",command=self.routeListCB,width=80) self.tlOkButton.pack() self.master.wait_window(self.importRouteTop) else: Label(self.importRouteTop, text="No Routes found!").pack() self.tlOkButton = Button(self.importRouteTop,text="OK",command=self.importRouteTop.destroy,width=10) self.tlOkButton.pack() def export2FFA320(self): """ Write the route to Flight Factor A320 Company Routes Database. Import to aircraft via the MCDU """ self.updateFpl() #self.fpl.actype,self.fpl.depicao,self.fpl.desticao,deph,depm,reg,selcal,self.fpl.route,self.fpl.level fpl = self.fpl # check if ac type is A320 if fpl.actype != 'A320': warn('A/C type is not A320!') # remove SID/STAR from route route = re.sub('[A-Z]{5}\d[A-Z]','',fpl.route).strip() # write route string routeString = 'RTE {}{} {} {} {} CI30 FL{}'.format(fpl.depicao, fpl.desticao, fpl.depicao, route, fpl.desticao, fpl.level ) # find and open route database # check for duplicate and overwrite or append route coRoutePath = os.path.join(self.xPlaneDir,r'Aircraft\FlightFactorA320\data\corte.in') with open(coRoutePath,'r') as coRouteFile: lines = coRouteFile.readlines() written = False for k in range(len(lines)): if 'RTE {}{}'.format(fpl.depicao,fpl.desticao) in lines[k]: lines[k] = '{}\n'.format(routeString) written = True break if not written: if lines[-1][-1] == '\n': lines.append(routeString) else: lines.append('\n{}'.format(routeString)) # write new file with open(coRoutePath,'w') as coRouteFile: for k in lines: coRouteFile.write(k) # print success message print('exported (FF A320)!') def export2xp(self): self.updateFpl() # Get file path for export. fileCount = 0 fmsFilePath = os.path.abspath(__file__) while os.path.isfile(fmsFilePath): fileCount += 1 fmsFilePath = os.path.join(self.xPlaneDir,'Output','FMS plans','{}{}{:02}.fms'.format(self.fpl.depicao,self.fpl.desticao,fileCount)) # Get coordinates of dep. curCoordinates = self.fpl.airports[self.fpl.depicao] # Get start altitude. curAltitude = int(self.fpl.level) * 100 newAltitude = curAltitude # Remove SID/STAR from route and split in parts route = re.sub('[A-Z]{5}\d[A-Z]','',self.fpl.route).strip() route = route.split() # with open(fmsFilePath,'w') as fmsFile: # Write header and departure. fmsStr = '' # Process route. nWaypoints = 1 curWaypoint = None curWaypointName = None lastWaypointName = None curAirway = None for rpId,rp in enumerate(route): if not(rpId % 2): # Waypoint # Split altitude from wp if '/' in rp: wpSplit = rp.split('/') curWaypointName = wpSplit[0] altMatch = re.search('F\d+', wpSplit[1]) if altMatch is not None: newAltitude = int(wpSplit[1][altMatch.start()+1:altMatch.end()]) * 100 else: curWaypointName = rp if curAirway is None: # After DCT curAltitude = newAltitude curWaypoint = self.fpl.waypoints[curWaypointName] minDistance = 3.2 # slightly greater than pi for wp in curWaypoint: distance = avFormula.gcDistance(curCoordinates[0],curCoordinates[1],wp[0],wp[1]) if distance < minDistance: minDistance = distance nearWp = wp fmsStr = '{}{} {} DRCT {} {} {}\n'.format(fmsStr,nearWp[2],curWaypointName,curAltitude,nearWp[0],nearWp[1]) nWaypoints += 1 else: # After Airway curAirwayParts = self.fpl.airways[curAirway].parts curAirwayName = curAirway curAirway = None # Get part with both waypoints. for pa in curAirwayParts: if lastWaypointName in [k for m in pa for k in m] and curWaypointName in [n for o in pa for n in o]: curAirway = pa break if curAirway is None: print('One or both waypoints are no part of airway {}!'.format(curAirwayName)) raise(Exception,'Airway Error!') curWaypointId = None lastWaypointId = None for wpId,wp in enumerate(curAirway): if curWaypointName in wp: curWaypointId = wpId elif lastWaypointName in wp: lastWaypointId = wpId if curWaypointId is not None and lastWaypointId is not None: step = int(copysign(1,curWaypointId - lastWaypointId)) break for wp in range(lastWaypointId+step,curWaypointId+step,step): if curAirway[wp][0] == curWaypointName: curAltitude = newAltitude fmsStr = '{}{} {} {} {} {} {}\n'.format(fmsStr,curAirway[wp][3],curAirway[wp][0],curAirwayName,curAltitude,curAirway[wp][1],curAirway[wp][2]) nWaypoints += 1 curAirway = None elif rp != 'DCT': # Airway curAirway = rp lastWaypointName = curWaypointName curCoordinates = self.fpl.airports[self.fpl.desticao] fmsStr = '{}1 {} ADES 0.000000 {} {}'.format(fmsStr,self.fpl.desticao,curCoordinates[0],curCoordinates[1]) nWaypoints += 1 curCoordinates = self.fpl.airports[self.fpl.depicao] fmsStr = 'I\n1100 Version\nCYCLE {}\nADEP {}\nADES {}\nNUMENR {}\n1 {} ADEP 0.000000 {} {}\n{}'.format(self.fpl.cycleNumber, self.fpl.depicao, self.fpl.desticao, nWaypoints, self.fpl.depicao, curCoordinates[0],curCoordinates[1], fmsStr) # print(fmsStr) with open(fmsFilePath,'w') as fmsFile: fmsFile.write(fmsStr) print('fms file exported to XP!') def acLoad(self): self.updateFpl() self.getAcTemplates() # get the right template. if self.fpl.actype in self.acTemplates: template = self.acTemplates[self.fpl.actype] # Assign values to FPL. self.fpl.equipment = template[0] self.fpl.transponder = template[1] matchObj = re.search(r'PBN/\w+', self.fpl.other,flags=re.A) # @UndefinedVariable if matchObj is not None: self.fpl.other = self.fpl.other.replace(self.fpl.other[matchObj.start():matchObj.end()], '') self.fpl.other = re.sub(' +',' ',self.fpl.other) self.fpl.other = self.fpl.other.strip() self.fpl.other = 'PBN/{} {}'.format(template[2],self.fpl.other) self.fpl.other = self.fpl.other.strip() self.fpl.wakecat = template[3] self.fpl.speed = template[4] self.fpl.pob = template[5] # Update Fields. self.updateContent() else: messagebox.showinfo("FPL", "No templates found for\naircraft {}!".format(self.fpl.actype)) def acSave(self): # Preparations. self.updateFpl() self.getAcTemplates() # Check if template already exists and ask what to do. if self.fpl.actype in self.acTemplates: if messagebox.askyesno("Overwrite?","A template for the aircraft {} already exists.\nOverwrite?".format(self.fpl.actype)): self.acTemplates.pop(self.fpl.actype) else: return # Update Aircraft templates. self.acTemplates[self.fpl.actype] = [] self.acTemplates[self.fpl.actype].append(self.fpl.equipment) self.acTemplates[self.fpl.actype].append(self.fpl.transponder) matchObj = re.search(r'PBN/\w+', self.fpl.other,flags=re.A) # @UndefinedVariable if matchObj is not None: self.acTemplates[self.fpl.actype].append(self.fpl.other[matchObj.start():matchObj.end()].replace('PBN/','')) else: self.acTemplates[self.fpl.actype].append('') self.acTemplates[self.fpl.actype].append(self.fpl.wakecat) self.acTemplates[self.fpl.actype].append(self.fpl.speed) self.acTemplates[self.fpl.actype].append(self.fpl.pob) # Write the new list with open(os.path.join(self.databaseDir,'aircraft.csv'),'w') as acFile: acFile.write('ac;equip;transponder;PBN;wakeCat;speed;x;POB\n') for te in self.acTemplates: curTemplate = self.acTemplates[te] acFile.write('{};{};{};{};{};{};{}\n'.format(te,curTemplate[0],curTemplate[1],curTemplate[2],curTemplate[3],curTemplate[4],curTemplate[5])) def getAcTemplates(self): self.acTemplates = {} if not os.path.exists(os.path.join(self.databaseDir,'aircraft.csv')): open(os.path.join(self.databaseDir,'aircraft.csv'),'w').close() with open(os.path.join(self.databaseDir,'aircraft.csv')) as acFile: for lineNr,line in enumerate(acFile): if lineNr: lineSplit = line.rstrip('\n').split(';') self.acTemplates[lineSplit[0]] = [lineSplit[1],lineSplit[2],lineSplit[3],lineSplit[4],lineSplit[5],lineSplit[6]] def showSkyvector(self): # Calculate middle point. depCoordinates = self.fpl.airports[self.fpl.depicao] destCoordinates = self.fpl.airports[self.fpl.desticao] intermediatePoint = avFormula.gcIntermediatePoint(depCoordinates[0], depCoordinates[1], destCoordinates[0], destCoordinates[1]) skyvectorUrl = 'http://skyvector.com/?ll={:9.6f},{:9.6f}&chart=304&zoom=6&fpl=%20{}%20{}%20{}'.format(intermediatePoint[0], intermediatePoint[1], self.fpl.depicao, self.fpl.route.replace(' ','%20'), self.fpl.desticao) webbrowser.open(skyvectorUrl,new=2) def showFplText(self): # Get Field contents. self.updateFpl() # Init string. fplString = '(FPL\n' # Complete string. fplString = '{}-{}-{}{}\n'.format(fplString, self.fpl.callsign, self.fpl.rules, self.fpl.flighttype) fplString = '{}-{}{}/{}-{}/{}\n'.format(fplString, self.fpl.number, self.fpl.actype, self.fpl.wakecat, self.fpl.equipment, self.fpl.transponder) fplString = '{}-{}{}\n'.format(fplString, self.fpl.depicao, self.fpl.deptime) fplString = '{}-N{:04}F{:03} {}\n'.format(fplString, int(self.fpl.speed), int(self.fpl.level), self.fpl.route) fplString = '{}-{}{} {} {}\n'.format(fplString, self.fpl.desticao, self.fpl.eet, self.fpl.alticao, self.fpl.alt2icao) fplString = '{}-{})'.format(fplString,self.fpl.other) # Print string. print(fplString) # Copy to clipboard. r = Tk() r.withdraw() r.clipboard_clear() r.clipboard_append(str(fplString)) r.update() r.destroy() # Show in window. showinfo("Flightplan text", '{}\n\n(Copied to clipboard.)'.format(fplString)) # Callbacks def routeListCB(self): selectedRoute = self.importRouteListboxTl.curselection() selectedRoute = selectedRoute[0] self.fpl.route = self.routing[selectedRoute] self.fpl.route = self.fpl.route[5:-5] self.importRouteTop.destroy() self.updateContent() def e_callsignCB(self,*args): #@UnusedVariable string = self.callsign.get() if len(string): enteredChar = string[-1] if not enteredChar.isalnum(): self.callsign.set(string[0:-1]) else: self.callsign.set(self.callsign.get().upper()) def e_numberCB(self,*args): #@UnusedVariable string = self.number.get() if len(string): enteredChar = string[-1] if not enteredChar.isdigit(): self.number.set(string[0:-1]) def e_actypeCB(self,*args): #@UnusedVariable string = self.actype.get() if len(string): enteredChar = string[-1] if not enteredChar.isalnum() or len(string) > 4: self.actype.set(string[0:-1]) else: self.actype.set(self.actype.get().upper()) def e_equipmentCB(self,*args): #@UnusedVariable string = self.equipment.get() if len(string): enteredChar = string[-1] if not enteredChar.isalnum(): self.equipment.set(string[0:-1]) else: self.equipment.set(self.equipment.get().upper()) def e_transponderCB(self,*args): #@UnusedVariable string = self.transponder.get() if len(string): enteredChar = string[-1] if not enteredChar.isalnum(): self.transponder.set(string[0:-1]) else: self.transponder.set(self.transponder.get().upper()) def e_depicaoCB(self,*args): #@UnusedVariable string = self.depicao.get() if len(string): enteredChar = string[-1] if not enteredChar.isalnum() or len(string) > 4: self.depicao.set(string[0:-1]) else: self.depicao.set(self.depicao.get().upper()) def e_deptimeCB(self,*args): #@UnusedVariable string = self.deptime.get() if len(string): enteredChar = string[-1] if not enteredChar.isdigit() or len(string) > 4: self.deptime.set(string[0:-1]) def e_speedCB(self,*args): #@UnusedVariable string = self.speed.get() if len(string): enteredChar = string[-1] if not enteredChar.isdigit() or len(string) > 4: self.speed.set(string[0:-1]) def e_levelCB(self,*args): #@UnusedVariable string = self.level.get() if len(string): enteredChar = string[-1] if not enteredChar.isdigit() or len(string) > 3: self.level.set(string[0:-1]) def e_routeCB(self,*args): #@UnusedVariable string = self.route.get() if len(string): enteredChar = string[-1] if not enteredChar.isalnum() and enteredChar != '/' and enteredChar != ' ': self.route.set(string[0:-1]) else: self.route.set(self.route.get().upper()) def e_desticaoCB(self,*args): #@UnusedVariable string = self.desticao.get() if len(string): enteredChar = string[-1] if not enteredChar.isalnum() or len(string) > 4: self.desticao.set(string[0:-1]) else: self.desticao.set(self.desticao.get().upper()) def e_eetCB(self,*args): #@UnusedVariable string = self.eet.get() if len(string): enteredChar = string[-1] if not enteredChar.isdigit() or len(string) > 4: self.eet.set(string[0:-1]) def e_alticaoCB(self,*args): #@UnusedVariable string = self.alticao.get() if len(string): enteredChar = string[-1] if not enteredChar.isalnum() or len(string) > 4: self.alticao.set(string[0:-1]) else: self.alticao.set(self.alticao.get().upper()) def e_alt2icaoCB(self,*args): #@UnusedVariable string = self.alt2icao.get() if len(string): enteredChar = string[-1] if not enteredChar.isalnum() or len(string) > 4: self.alt2icao.set(string[0:-1]) else: self.alt2icao.set(self.alt2icao.get().upper()) def e_otherCB(self,*args): #@UnusedVariable string = self.other.get() if len(string): enteredChar = string[-1] if not enteredChar.isalnum() and enteredChar != '/' and enteredChar != ' ': self.other.set(string[0:-1]) else: self.other.set(self.other.get().upper()) def e_enduranceCB(self,*args): #@UnusedVariable string = self.endurance.get() if len(string): enteredChar = string[-1] if not enteredChar.isdigit() or len(string) > 4: self.endurance.set(string[0:-1]) def e_pobCB(self,*args): #@UnusedVariable string = self.pob.get() if len(string): enteredChar = string[-1] if not enteredChar.isdigit(): self.pob.set(string[0:-1]) def e_picCB(self,*args): #@UnusedVariable string = self.pic.get() if len(string): enteredChar = string[-1] if not enteredChar.isalpha() and enteredChar != ' ' and enteredChar != "'" and enteredChar != '-': self.pic.set(string[0:-1])
class ShiftReduceApp(object): """ A graphical tool for exploring the shift-reduce parser. The tool displays the parser's stack and the remaining text, and allows the user to control the parser's operation. In particular, the user can shift tokens onto the stack, and can perform reductions on the top elements of the stack. A "step" button simply steps through the parsing process, performing the operations that ``nltk.parse.ShiftReduceParser`` would use. """ def __init__(self, grammar, sent, trace=0): self._sent = sent self._parser = SteppingShiftReduceParser(grammar, trace) # Set up the main window. self._top = Tk() self._top.title('Shift Reduce Parser Application') # Animations. animating_lock is a lock to prevent the demo # from performing new operations while it's animating. self._animating_lock = 0 self._animate = IntVar(self._top) self._animate.set(10) # = medium # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Initialize fonts. self._init_fonts(self._top) # Set up key bindings. self._init_bindings() # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_feedback(self._top) self._init_grammar(self._top) self._init_canvas(self._top) # A popup menu for reducing. self._reduce_menu = Menu(self._canvas, tearoff=0) # Reset the demo, and set the feedback frame to empty. self.reset() self._lastoper1['text'] = '' ######################################### ## Initialization Helpers ######################################### def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = tkinter.font.Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget('size')) self._boldfont = tkinter.font.Font(family='helvetica', weight='bold', size=self._size.get()) self._font = tkinter.font.Font(family='helvetica', size=self._size.get()) def _init_grammar(self, parent): # Grammar view. self._prodframe = listframe = Frame(parent) self._prodframe.pack(fill='both', side='left', padx=2) self._prodlist_label = Label(self._prodframe, font=self._boldfont, text='Available Reductions') self._prodlist_label.pack() self._prodlist = Listbox(self._prodframe, selectmode='single', relief='groove', background='white', foreground='#909090', font=self._font, selectforeground='#004040', selectbackground='#c0f0c0') self._prodlist.pack(side='right', fill='both', expand=1) self._productions = list(self._parser.grammar().productions()) for production in self._productions: self._prodlist.insert('end', (' %s' % production)) self._prodlist.config(height=min(len(self._productions), 25)) # Add a scrollbar if there are more than 25 productions. if 1:#len(self._productions) > 25: listscroll = Scrollbar(self._prodframe, orient='vertical') self._prodlist.config(yscrollcommand = listscroll.set) listscroll.config(command=self._prodlist.yview) listscroll.pack(side='left', fill='y') # If they select a production, apply it. self._prodlist.bind('<<ListboxSelect>>', self._prodlist_select) # When they hover over a production, highlight it. self._hover = -1 self._prodlist.bind('<Motion>', self._highlight_hover) self._prodlist.bind('<Leave>', self._clear_hover) def _init_bindings(self): # Quit self._top.bind('<Control-q>', self.destroy) self._top.bind('<Control-x>', self.destroy) self._top.bind('<Alt-q>', self.destroy) self._top.bind('<Alt-x>', self.destroy) # Ops (step, shift, reduce, undo) self._top.bind('<space>', self.step) self._top.bind('<s>', self.shift) self._top.bind('<Alt-s>', self.shift) self._top.bind('<Control-s>', self.shift) self._top.bind('<r>', self.reduce) self._top.bind('<Alt-r>', self.reduce) self._top.bind('<Control-r>', self.reduce) self._top.bind('<Delete>', self.reset) self._top.bind('<u>', self.undo) self._top.bind('<Alt-u>', self.undo) self._top.bind('<Control-u>', self.undo) self._top.bind('<Control-z>', self.undo) self._top.bind('<BackSpace>', self.undo) # Misc self._top.bind('<Control-p>', self.postscript) self._top.bind('<Control-h>', self.help) self._top.bind('<F1>', self.help) self._top.bind('<Control-g>', self.edit_grammar) self._top.bind('<Control-t>', self.edit_sentence) # Animation speed control self._top.bind('-', lambda e,a=self._animate:a.set(20)) self._top.bind('=', lambda e,a=self._animate:a.set(10)) self._top.bind('+', lambda e,a=self._animate:a.set(4)) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill='none', side='bottom') Button(buttonframe, text='Step', background='#90c0d0', foreground='black', command=self.step,).pack(side='left') Button(buttonframe, text='Shift', underline=0, background='#90f090', foreground='black', command=self.shift).pack(side='left') Button(buttonframe, text='Reduce', underline=0, background='#90f090', foreground='black', command=self.reduce).pack(side='left') Button(buttonframe, text='Undo', underline=0, background='#f0a0a0', foreground='black', command=self.undo).pack(side='left') def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label='Reset Parser', underline=0, command=self.reset, accelerator='Del') filemenu.add_command(label='Print to Postscript', underline=0, command=self.postscript, accelerator='Ctrl-p') filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-x') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) editmenu.add_command(label='Edit Grammar', underline=5, command=self.edit_grammar, accelerator='Ctrl-g') editmenu.add_command(label='Edit Text', underline=5, command=self.edit_sentence, accelerator='Ctrl-t') menubar.add_cascade(label='Edit', underline=0, menu=editmenu) rulemenu = Menu(menubar, tearoff=0) rulemenu.add_command(label='Step', underline=1, command=self.step, accelerator='Space') rulemenu.add_separator() rulemenu.add_command(label='Shift', underline=0, command=self.shift, accelerator='Ctrl-s') rulemenu.add_command(label='Reduce', underline=0, command=self.reduce, accelerator='Ctrl-r') rulemenu.add_separator() rulemenu.add_command(label='Undo', underline=0, command=self.undo, accelerator='Ctrl-u') menubar.add_cascade(label='Apply', underline=0, menu=rulemenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_checkbutton(label="Show Grammar", underline=0, variable=self._show_grammar, command=self._toggle_grammar) viewmenu.add_separator() viewmenu.add_radiobutton(label='Tiny', variable=self._size, underline=0, value=10, command=self.resize) viewmenu.add_radiobutton(label='Small', variable=self._size, underline=0, value=12, command=self.resize) viewmenu.add_radiobutton(label='Medium', variable=self._size, underline=0, value=14, command=self.resize) viewmenu.add_radiobutton(label='Large', variable=self._size, underline=0, value=18, command=self.resize) viewmenu.add_radiobutton(label='Huge', variable=self._size, underline=0, value=24, command=self.resize) menubar.add_cascade(label='View', underline=0, menu=viewmenu) animatemenu = Menu(menubar, tearoff=0) animatemenu.add_radiobutton(label="No Animation", underline=0, variable=self._animate, value=0) animatemenu.add_radiobutton(label="Slow Animation", underline=0, variable=self._animate, value=20, accelerator='-') animatemenu.add_radiobutton(label="Normal Animation", underline=0, variable=self._animate, value=10, accelerator='=') animatemenu.add_radiobutton(label="Fast Animation", underline=0, variable=self._animate, value=4, accelerator='+') menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label='About', underline=0, command=self.about) helpmenu.add_command(label='Instructions', underline=0, command=self.help, accelerator='F1') menubar.add_cascade(label='Help', underline=0, menu=helpmenu) parent.config(menu=menubar) def _init_feedback(self, parent): self._feedbackframe = feedbackframe = Frame(parent) feedbackframe.pack(fill='x', side='bottom', padx=3, pady=3) self._lastoper_label = Label(feedbackframe, text='Last Operation:', font=self._font) self._lastoper_label.pack(side='left') lastoperframe = Frame(feedbackframe, relief='sunken', border=1) lastoperframe.pack(fill='x', side='right', expand=1, padx=5) self._lastoper1 = Label(lastoperframe, foreground='#007070', background='#f0f0f0', font=self._font) self._lastoper2 = Label(lastoperframe, anchor='w', width=30, foreground='#004040', background='#f0f0f0', font=self._font) self._lastoper1.pack(side='left') self._lastoper2.pack(side='left', fill='x', expand=1) def _init_canvas(self, parent): self._cframe = CanvasFrame(parent, background='white', width=525, closeenough=10, border=2, relief='sunken') self._cframe.pack(expand=1, fill='both', side='top', pady=2) canvas = self._canvas = self._cframe.canvas() self._stackwidgets = [] self._rtextwidgets = [] self._titlebar = canvas.create_rectangle(0,0,0,0, fill='#c0f0f0', outline='black') self._exprline = canvas.create_line(0,0,0,0, dash='.') self._stacktop = canvas.create_line(0,0,0,0, fill='#408080') size = self._size.get()+4 self._stacklabel = TextWidget(canvas, 'Stack', color='#004040', font=self._boldfont) self._rtextlabel = TextWidget(canvas, 'Remaining Text', color='#004040', font=self._boldfont) self._cframe.add_widget(self._stacklabel) self._cframe.add_widget(self._rtextlabel) ######################################### ## Main draw procedure ######################################### def _redraw(self): scrollregion = self._canvas['scrollregion'].split() (cx1, cy1, cx2, cy2) = [int(c) for c in scrollregion] # Delete the old stack & rtext widgets. for stackwidget in self._stackwidgets: self._cframe.destroy_widget(stackwidget) self._stackwidgets = [] for rtextwidget in self._rtextwidgets: self._cframe.destroy_widget(rtextwidget) self._rtextwidgets = [] # Position the titlebar & exprline (x1, y1, x2, y2) = self._stacklabel.bbox() y = y2-y1+10 self._canvas.coords(self._titlebar, -5000, 0, 5000, y-4) self._canvas.coords(self._exprline, 0, y*2-10, 5000, y*2-10) # Position the titlebar labels.. (x1, y1, x2, y2) = self._stacklabel.bbox() self._stacklabel.move(5-x1, 3-y1) (x1, y1, x2, y2) = self._rtextlabel.bbox() self._rtextlabel.move(cx2-x2-5, 3-y1) # Draw the stack. stackx = 5 for tok in self._parser.stack(): if isinstance(tok, Tree): attribs = {'tree_color': '#4080a0', 'tree_width': 2, 'node_font': self._boldfont, 'node_color': '#006060', 'leaf_color': '#006060', 'leaf_font':self._font} widget = tree_to_treesegment(self._canvas, tok, **attribs) widget.label()['color'] = '#000000' else: widget = TextWidget(self._canvas, tok, color='#000000', font=self._font) widget.bind_click(self._popup_reduce) self._stackwidgets.append(widget) self._cframe.add_widget(widget, stackx, y) stackx = widget.bbox()[2] + 10 # Draw the remaining text. rtextwidth = 0 for tok in self._parser.remaining_text(): widget = TextWidget(self._canvas, tok, color='#000000', font=self._font) self._rtextwidgets.append(widget) self._cframe.add_widget(widget, rtextwidth, y) rtextwidth = widget.bbox()[2] + 4 # Allow enough room to shift the next token (for animations) if len(self._rtextwidgets) > 0: stackx += self._rtextwidgets[0].width() # Move the remaining text to the correct location (keep it # right-justified, when possible); and move the remaining text # label, if necessary. stackx = max(stackx, self._stacklabel.width()+25) rlabelwidth = self._rtextlabel.width()+10 if stackx >= cx2-max(rtextwidth, rlabelwidth): cx2 = stackx + max(rtextwidth, rlabelwidth) for rtextwidget in self._rtextwidgets: rtextwidget.move(4+cx2-rtextwidth, 0) self._rtextlabel.move(cx2-self._rtextlabel.bbox()[2]-5, 0) midx = (stackx + cx2-max(rtextwidth, rlabelwidth))/2 self._canvas.coords(self._stacktop, midx, 0, midx, 5000) (x1, y1, x2, y2) = self._stacklabel.bbox() # Set up binding to allow them to shift a token by dragging it. if len(self._rtextwidgets) > 0: def drag_shift(widget, midx=midx, self=self): if widget.bbox()[0] < midx: self.shift() else: self._redraw() self._rtextwidgets[0].bind_drag(drag_shift) self._rtextwidgets[0].bind_click(self.shift) # Draw the stack top. self._highlight_productions() def _draw_stack_top(self, widget): # hack.. midx = widget.bbox()[2]+50 self._canvas.coords(self._stacktop, midx, 0, midx, 5000) def _highlight_productions(self): # Highlight the productions that can be reduced. self._prodlist.selection_clear(0, 'end') for prod in self._parser.reducible_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) ######################################### ## Button Callbacks ######################################### def destroy(self, *e): if self._top is None: return self._top.destroy() self._top = None def reset(self, *e): self._parser.initialize(self._sent) self._lastoper1['text'] = 'Reset App' self._lastoper2['text'] = '' self._redraw() def step(self, *e): if self.reduce(): return True elif self.shift(): return True else: if list(self._parser.parses()): self._lastoper1['text'] = 'Finished:' self._lastoper2['text'] = 'Success' else: self._lastoper1['text'] = 'Finished:' self._lastoper2['text'] = 'Failure' def shift(self, *e): if self._animating_lock: return if self._parser.shift(): tok = self._parser.stack()[-1] self._lastoper1['text'] = 'Shift:' self._lastoper2['text'] = '%r' % tok if self._animate.get(): self._animate_shift() else: self._redraw() return True return False def reduce(self, *e): if self._animating_lock: return production = self._parser.reduce() if production: self._lastoper1['text'] = 'Reduce:' self._lastoper2['text'] = '%s' % production if self._animate.get(): self._animate_reduce() else: self._redraw() return production def undo(self, *e): if self._animating_lock: return if self._parser.undo(): self._redraw() def postscript(self, *e): self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) ######################################### ## Menubar callbacks ######################################### def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) #self._stacklabel['font'] = ('helvetica', -size-4, 'bold') #self._rtextlabel['font'] = ('helvetica', -size-4, 'bold') #self._lastoper_label['font'] = ('helvetica', -size) #self._lastoper1['font'] = ('helvetica', -size) #self._lastoper2['font'] = ('helvetica', -size) #self._prodlist['font'] = ('helvetica', -size) #self._prodlist_label['font'] = ('helvetica', -size-2, 'bold') self._redraw() def help(self, *e): # The default font's not very legible; try using 'fixed' instead. try: ShowText(self._top, 'Help: Shift-Reduce Parser Application', (__doc__ or '').strip(), width=75, font='fixed') except: ShowText(self._top, 'Help: Shift-Reduce Parser Application', (__doc__ or '').strip(), width=75) def about(self, *e): ABOUT = ("NLTK Shift-Reduce Parser Application\n"+ "Written by Edward Loper") TITLE = 'About: Shift-Reduce Parser Application' try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def edit_grammar(self, *e): CFGEditor(self._top, self._parser.grammar(), self.set_grammar) def set_grammar(self, grammar): self._parser.set_grammar(grammar) self._productions = list(grammar.productions()) self._prodlist.delete(0, 'end') for production in self._productions: self._prodlist.insert('end', (' %s' % production)) def edit_sentence(self, *e): sentence = " ".join(self._sent) title = 'Edit Text' instr = 'Enter a new sentence to parse.' EntryDialog(self._top, sentence, instr, self.set_sentence, title) def set_sentence(self, sent): self._sent = sent.split() #[XX] use tagged? self.reset() ######################################### ## Reduce Production Selection ######################################### def _toggle_grammar(self, *e): if self._show_grammar.get(): self._prodframe.pack(fill='both', side='left', padx=2, after=self._feedbackframe) self._lastoper1['text'] = 'Show Grammar' else: self._prodframe.pack_forget() self._lastoper1['text'] = 'Hide Grammar' self._lastoper2['text'] = '' def _prodlist_select(self, event): selection = self._prodlist.curselection() if len(selection) != 1: return index = int(selection[0]) production = self._parser.reduce(self._productions[index]) if production: self._lastoper1['text'] = 'Reduce:' self._lastoper2['text'] = '%s' % production if self._animate.get(): self._animate_reduce() else: self._redraw() else: # Reset the production selections. self._prodlist.selection_clear(0, 'end') for prod in self._parser.reducible_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) def _popup_reduce(self, widget): # Remove old commands. productions = self._parser.reducible_productions() if len(productions) == 0: return self._reduce_menu.delete(0, 'end') for production in productions: self._reduce_menu.add_command(label=str(production), command=self.reduce) self._reduce_menu.post(self._canvas.winfo_pointerx(), self._canvas.winfo_pointery()) ######################################### ## Animations ######################################### def _animate_shift(self): # What widget are we shifting? widget = self._rtextwidgets[0] # Where are we shifting from & to? right = widget.bbox()[0] if len(self._stackwidgets) == 0: left = 5 else: left = self._stackwidgets[-1].bbox()[2]+10 # Start animating. dt = self._animate.get() dx = (left-right)*1.0/dt self._animate_shift_frame(dt, widget, dx) def _animate_shift_frame(self, frame, widget, dx): if frame > 0: self._animating_lock = 1 widget.move(dx, 0) self._top.after(10, self._animate_shift_frame, frame-1, widget, dx) else: # but: stacktop?? # Shift the widget to the stack. del self._rtextwidgets[0] self._stackwidgets.append(widget) self._animating_lock = 0 # Display the available productions. self._draw_stack_top(widget) self._highlight_productions() def _animate_reduce(self): # What widgets are we shifting? numwidgets = len(self._parser.stack()[-1]) # number of children widgets = self._stackwidgets[-numwidgets:] # How far are we moving? if isinstance(widgets[0], TreeSegmentWidget): ydist = 15 + widgets[0].label().height() else: ydist = 15 + widgets[0].height() # Start animating. dt = self._animate.get() dy = ydist*2.0/dt self._animate_reduce_frame(dt/2, widgets, dy) def _animate_reduce_frame(self, frame, widgets, dy): if frame > 0: self._animating_lock = 1 for widget in widgets: widget.move(0, dy) self._top.after(10, self._animate_reduce_frame, frame-1, widgets, dy) else: del self._stackwidgets[-len(widgets):] for widget in widgets: self._cframe.remove_widget(widget) tok = self._parser.stack()[-1] if not isinstance(tok, Tree): raise ValueError() label = TextWidget(self._canvas, str(tok.label()), color='#006060', font=self._boldfont) widget = TreeSegmentWidget(self._canvas, label, widgets, width=2) (x1, y1, x2, y2) = self._stacklabel.bbox() y = y2-y1+10 if not self._stackwidgets: x = 5 else: x = self._stackwidgets[-1].bbox()[2] + 10 self._cframe.add_widget(widget, x, y) self._stackwidgets.append(widget) # Display the available productions. self._draw_stack_top(widget) self._highlight_productions() # # Delete the old widgets.. # del self._stackwidgets[-len(widgets):] # for widget in widgets: # self._cframe.destroy_widget(widget) # # # Make a new one. # tok = self._parser.stack()[-1] # if isinstance(tok, Tree): # attribs = {'tree_color': '#4080a0', 'tree_width': 2, # 'node_font': bold, 'node_color': '#006060', # 'leaf_color': '#006060', 'leaf_font':self._font} # widget = tree_to_treesegment(self._canvas, tok.type(), # **attribs) # widget.node()['color'] = '#000000' # else: # widget = TextWidget(self._canvas, tok.type(), # color='#000000', font=self._font) # widget.bind_click(self._popup_reduce) # (x1, y1, x2, y2) = self._stacklabel.bbox() # y = y2-y1+10 # if not self._stackwidgets: x = 5 # else: x = self._stackwidgets[-1].bbox()[2] + 10 # self._cframe.add_widget(widget, x, y) # self._stackwidgets.append(widget) #self._redraw() self._animating_lock = 0 ######################################### ## Hovering. ######################################### def _highlight_hover(self, event): # What production are we hovering over? index = self._prodlist.nearest(event.y) if self._hover == index: return # Clear any previous hover highlighting. self._clear_hover() # If the production corresponds to an available reduction, # highlight the stack. selection = [int(s) for s in self._prodlist.curselection()] if index in selection: rhslen = len(self._productions[index].rhs()) for stackwidget in self._stackwidgets[-rhslen:]: if isinstance(stackwidget, TreeSegmentWidget): stackwidget.label()['color'] = '#00a000' else: stackwidget['color'] = '#00a000' # Remember what production we're hovering over. self._hover = index def _clear_hover(self, *event): # Clear any previous hover highlighting. if self._hover == -1: return self._hover = -1 for stackwidget in self._stackwidgets: if isinstance(stackwidget, TreeSegmentWidget): stackwidget.label()['color'] = 'black' else: stackwidget['color'] = 'black'
class AutocompleteEntry(Entry): def __init__(self, lista, *args, **kwargs): Entry.__init__(self, *args, **kwargs) self.lista = lista self.var = self["textvariable"] if self.var == '': self.var = self["textvariable"] = StringVar() self.var.trace('w', self.changed) self.bind("<Right>", self.selection) self.bind("<Up>", self.up) self.bind("<Down>", self.down) self.lb_up = False def changed(self, name, index, mode): if self.var.get() == '': self.lb.destroy() self.lb_up = False else: words = self.comparison() if words: if not self.lb_up: self.lb = Listbox() self.lb.bind("<Double-Button-1>", self.selection) self.lb.bind("<Right>", self.selection) self.lb.place(x=self.winfo_x(), y=self.winfo_y() + self.winfo_height()) self.lb_up = True self.lb.delete(0, END) for w in words: self.lb.insert(END, w) else: if self.lb_up: self.lb.destroy() self.lb_up = False def selection(self, event): if self.lb_up: self.var.set(self.lb.get(ACTIVE)) self.lb.destroy() self.lb_up = False self.icursor(END) def up(self, event): if self.lb_up: if self.lb.curselection() == (): index = '0' else: index = self.lb.curselection()[0] if index != '0': self.lb.selection_clear(first=index) index = str(int(index) - 1) self.lb.selection_set(first=index) self.lb.activate(index) def down(self, event): if self.lb_up: if self.lb.curselection() == (): index = '0' else: index = self.lb.curselection()[0] if index != END: self.lb.selection_clear(first=index) index = str(int(index) + 1) self.lb.selection_set(first=index) self.lb.activate(index) def comparison(self): pattern = re.compile('.*' + self.var.get() + '.*') return [w for w in self.lista if re.match(pattern, w)]
class RecursiveDescentApp(object): """ A graphical tool for exploring the recursive descent parser. The tool displays the parser's tree and the remaining text, and allows the user to control the parser's operation. In particular, the user can expand subtrees on the frontier, match tokens on the frontier against the text, and backtrack. A "step" button simply steps through the parsing process, performing the operations that ``RecursiveDescentParser`` would use. """ def __init__(self, grammar, sent, trace=0): self._sent = sent self._parser = SteppingRecursiveDescentParser(grammar, trace) # Set up the main window. self._top = Tk() self._top.title('Recursive Descent Parser Application') # Set up key bindings. self._init_bindings() # Initialize the fonts. self._init_fonts(self._top) # Animations. animating_lock is a lock to prevent the demo # from performing new operations while it's animating. self._animation_frames = IntVar(self._top) self._animation_frames.set(5) self._animating_lock = 0 self._autostep = 0 # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_feedback(self._top) self._init_grammar(self._top) self._init_canvas(self._top) # Initialize the parser. self._parser.initialize(self._sent) # Resize callback self._canvas.bind('<Configure>', self._configure) ######################################### ## Initialization Helpers ######################################### def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = tkinter.font.Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget('size')) self._boldfont = tkinter.font.Font(family='helvetica', weight='bold', size=self._size.get()) self._font = tkinter.font.Font(family='helvetica', size=self._size.get()) if self._size.get() < 0: big = self._size.get()-2 else: big = self._size.get()+2 self._bigfont = tkinter.font.Font(family='helvetica', weight='bold', size=big) def _init_grammar(self, parent): # Grammar view. self._prodframe = listframe = Frame(parent) self._prodframe.pack(fill='both', side='left', padx=2) self._prodlist_label = Label(self._prodframe, font=self._boldfont, text='Available Expansions') self._prodlist_label.pack() self._prodlist = Listbox(self._prodframe, selectmode='single', relief='groove', background='white', foreground='#909090', font=self._font, selectforeground='#004040', selectbackground='#c0f0c0') self._prodlist.pack(side='right', fill='both', expand=1) self._productions = list(self._parser.grammar().productions()) for production in self._productions: self._prodlist.insert('end', (' %s' % production)) self._prodlist.config(height=min(len(self._productions), 25)) # Add a scrollbar if there are more than 25 productions. if len(self._productions) > 25: listscroll = Scrollbar(self._prodframe, orient='vertical') self._prodlist.config(yscrollcommand = listscroll.set) listscroll.config(command=self._prodlist.yview) listscroll.pack(side='left', fill='y') # If they select a production, apply it. self._prodlist.bind('<<ListboxSelect>>', self._prodlist_select) def _init_bindings(self): # Key bindings are a good thing. self._top.bind('<Control-q>', self.destroy) self._top.bind('<Control-x>', self.destroy) self._top.bind('<Escape>', self.destroy) self._top.bind('e', self.expand) #self._top.bind('<Alt-e>', self.expand) #self._top.bind('<Control-e>', self.expand) self._top.bind('m', self.match) self._top.bind('<Alt-m>', self.match) self._top.bind('<Control-m>', self.match) self._top.bind('b', self.backtrack) self._top.bind('<Alt-b>', self.backtrack) self._top.bind('<Control-b>', self.backtrack) self._top.bind('<Control-z>', self.backtrack) self._top.bind('<BackSpace>', self.backtrack) self._top.bind('a', self.autostep) #self._top.bind('<Control-a>', self.autostep) self._top.bind('<Control-space>', self.autostep) self._top.bind('<Control-c>', self.cancel_autostep) self._top.bind('<space>', self.step) self._top.bind('<Delete>', self.reset) self._top.bind('<Control-p>', self.postscript) #self._top.bind('<h>', self.help) #self._top.bind('<Alt-h>', self.help) self._top.bind('<Control-h>', self.help) self._top.bind('<F1>', self.help) #self._top.bind('<g>', self.toggle_grammar) #self._top.bind('<Alt-g>', self.toggle_grammar) #self._top.bind('<Control-g>', self.toggle_grammar) self._top.bind('<Control-g>', self.edit_grammar) self._top.bind('<Control-t>', self.edit_sentence) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill='none', side='bottom', padx=3, pady=2) Button(buttonframe, text='Step', background='#90c0d0', foreground='black', command=self.step,).pack(side='left') Button(buttonframe, text='Autostep', background='#90c0d0', foreground='black', command=self.autostep,).pack(side='left') Button(buttonframe, text='Expand', underline=0, background='#90f090', foreground='black', command=self.expand).pack(side='left') Button(buttonframe, text='Match', underline=0, background='#90f090', foreground='black', command=self.match).pack(side='left') Button(buttonframe, text='Backtrack', underline=0, background='#f0a0a0', foreground='black', command=self.backtrack).pack(side='left') # Replace autostep... # self._autostep_button = Button(buttonframe, text='Autostep', # underline=0, command=self.autostep) # self._autostep_button.pack(side='left') def _configure(self, event): self._autostep = 0 (x1, y1, x2, y2) = self._cframe.scrollregion() y2 = event.height - 6 self._canvas['scrollregion'] = '%d %d %d %d' % (x1,y1,x2,y2) self._redraw() def _init_feedback(self, parent): self._feedbackframe = feedbackframe = Frame(parent) feedbackframe.pack(fill='x', side='bottom', padx=3, pady=3) self._lastoper_label = Label(feedbackframe, text='Last Operation:', font=self._font) self._lastoper_label.pack(side='left') lastoperframe = Frame(feedbackframe, relief='sunken', border=1) lastoperframe.pack(fill='x', side='right', expand=1, padx=5) self._lastoper1 = Label(lastoperframe, foreground='#007070', background='#f0f0f0', font=self._font) self._lastoper2 = Label(lastoperframe, anchor='w', width=30, foreground='#004040', background='#f0f0f0', font=self._font) self._lastoper1.pack(side='left') self._lastoper2.pack(side='left', fill='x', expand=1) def _init_canvas(self, parent): self._cframe = CanvasFrame(parent, background='white', #width=525, height=250, closeenough=10, border=2, relief='sunken') self._cframe.pack(expand=1, fill='both', side='top', pady=2) canvas = self._canvas = self._cframe.canvas() # Initially, there's no tree or text self._tree = None self._textwidgets = [] self._textline = None def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label='Reset Parser', underline=0, command=self.reset, accelerator='Del') filemenu.add_command(label='Print to Postscript', underline=0, command=self.postscript, accelerator='Ctrl-p') filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-x') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) editmenu.add_command(label='Edit Grammar', underline=5, command=self.edit_grammar, accelerator='Ctrl-g') editmenu.add_command(label='Edit Text', underline=5, command=self.edit_sentence, accelerator='Ctrl-t') menubar.add_cascade(label='Edit', underline=0, menu=editmenu) rulemenu = Menu(menubar, tearoff=0) rulemenu.add_command(label='Step', underline=1, command=self.step, accelerator='Space') rulemenu.add_separator() rulemenu.add_command(label='Match', underline=0, command=self.match, accelerator='Ctrl-m') rulemenu.add_command(label='Expand', underline=0, command=self.expand, accelerator='Ctrl-e') rulemenu.add_separator() rulemenu.add_command(label='Backtrack', underline=0, command=self.backtrack, accelerator='Ctrl-b') menubar.add_cascade(label='Apply', underline=0, menu=rulemenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_checkbutton(label="Show Grammar", underline=0, variable=self._show_grammar, command=self._toggle_grammar) viewmenu.add_separator() viewmenu.add_radiobutton(label='Tiny', variable=self._size, underline=0, value=10, command=self.resize) viewmenu.add_radiobutton(label='Small', variable=self._size, underline=0, value=12, command=self.resize) viewmenu.add_radiobutton(label='Medium', variable=self._size, underline=0, value=14, command=self.resize) viewmenu.add_radiobutton(label='Large', variable=self._size, underline=0, value=18, command=self.resize) viewmenu.add_radiobutton(label='Huge', variable=self._size, underline=0, value=24, command=self.resize) menubar.add_cascade(label='View', underline=0, menu=viewmenu) animatemenu = Menu(menubar, tearoff=0) animatemenu.add_radiobutton(label="No Animation", underline=0, variable=self._animation_frames, value=0) animatemenu.add_radiobutton(label="Slow Animation", underline=0, variable=self._animation_frames, value=10, accelerator='-') animatemenu.add_radiobutton(label="Normal Animation", underline=0, variable=self._animation_frames, value=5, accelerator='=') animatemenu.add_radiobutton(label="Fast Animation", underline=0, variable=self._animation_frames, value=2, accelerator='+') menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label='About', underline=0, command=self.about) helpmenu.add_command(label='Instructions', underline=0, command=self.help, accelerator='F1') menubar.add_cascade(label='Help', underline=0, menu=helpmenu) parent.config(menu=menubar) ######################################### ## Helper ######################################### def _get(self, widget, treeloc): for i in treeloc: widget = widget.subtrees()[i] if isinstance(widget, TreeSegmentWidget): widget = widget.label() return widget ######################################### ## Main draw procedure ######################################### def _redraw(self): canvas = self._canvas # Delete the old tree, widgets, etc. if self._tree is not None: self._cframe.destroy_widget(self._tree) for twidget in self._textwidgets: self._cframe.destroy_widget(twidget) if self._textline is not None: self._canvas.delete(self._textline) # Draw the tree. helv = ('helvetica', -self._size.get()) bold = ('helvetica', -self._size.get(), 'bold') attribs = {'tree_color': '#000000', 'tree_width': 2, 'node_font': bold, 'leaf_font': helv,} tree = self._parser.tree() self._tree = tree_to_treesegment(canvas, tree, **attribs) self._cframe.add_widget(self._tree, 30, 5) # Draw the text. helv = ('helvetica', -self._size.get()) bottom = y = self._cframe.scrollregion()[3] self._textwidgets = [TextWidget(canvas, word, font=self._font) for word in self._sent] for twidget in self._textwidgets: self._cframe.add_widget(twidget, 0, 0) twidget.move(0, bottom-twidget.bbox()[3]-5) y = min(y, twidget.bbox()[1]) # Draw a line over the text, to separate it from the tree. self._textline = canvas.create_line(-5000, y-5, 5000, y-5, dash='.') # Highlight appropriate nodes. self._highlight_nodes() self._highlight_prodlist() # Make sure the text lines up. self._position_text() def _redraw_quick(self): # This should be more-or-less sufficient after an animation. self._highlight_nodes() self._highlight_prodlist() self._position_text() def _highlight_nodes(self): # Highlight the list of nodes to be checked. bold = ('helvetica', -self._size.get(), 'bold') for treeloc in self._parser.frontier()[:1]: self._get(self._tree, treeloc)['color'] = '#20a050' self._get(self._tree, treeloc)['font'] = bold for treeloc in self._parser.frontier()[1:]: self._get(self._tree, treeloc)['color'] = '#008080' def _highlight_prodlist(self): # Highlight the productions that can be expanded. # Boy, too bad tkinter doesn't implement Listbox.itemconfig; # that would be pretty useful here. self._prodlist.delete(0, 'end') expandable = self._parser.expandable_productions() untried = self._parser.untried_expandable_productions() productions = self._productions for index in range(len(productions)): if productions[index] in expandable: if productions[index] in untried: self._prodlist.insert(index, ' %s' % productions[index]) else: self._prodlist.insert(index, ' %s (TRIED)' % productions[index]) self._prodlist.selection_set(index) else: self._prodlist.insert(index, ' %s' % productions[index]) def _position_text(self): # Line up the text widgets that are matched against the tree numwords = len(self._sent) num_matched = numwords - len(self._parser.remaining_text()) leaves = self._tree_leaves()[:num_matched] xmax = self._tree.bbox()[0] for i in range(0, len(leaves)): widget = self._textwidgets[i] leaf = leaves[i] widget['color'] = '#006040' leaf['color'] = '#006040' widget.move(leaf.bbox()[0] - widget.bbox()[0], 0) xmax = widget.bbox()[2] + 10 # Line up the text widgets that are not matched against the tree. for i in range(len(leaves), numwords): widget = self._textwidgets[i] widget['color'] = '#a0a0a0' widget.move(xmax - widget.bbox()[0], 0) xmax = widget.bbox()[2] + 10 # If we have a complete parse, make everything green :) if self._parser.currently_complete(): for twidget in self._textwidgets: twidget['color'] = '#00a000' # Move the matched leaves down to the text. for i in range(0, len(leaves)): widget = self._textwidgets[i] leaf = leaves[i] dy = widget.bbox()[1] - leaf.bbox()[3] - 10.0 dy = max(dy, leaf.parent().label().bbox()[3] - leaf.bbox()[3] + 10) leaf.move(0, dy) def _tree_leaves(self, tree=None): if tree is None: tree = self._tree if isinstance(tree, TreeSegmentWidget): leaves = [] for child in tree.subtrees(): leaves += self._tree_leaves(child) return leaves else: return [tree] ######################################### ## Button Callbacks ######################################### def destroy(self, *e): self._autostep = 0 if self._top is None: return self._top.destroy() self._top = None def reset(self, *e): self._autostep = 0 self._parser.initialize(self._sent) self._lastoper1['text'] = 'Reset Application' self._lastoper2['text'] = '' self._redraw() def autostep(self, *e): if self._animation_frames.get() == 0: self._animation_frames.set(2) if self._autostep: self._autostep = 0 else: self._autostep = 1 self._step() def cancel_autostep(self, *e): #self._autostep_button['text'] = 'Autostep' self._autostep = 0 # Make sure to stop auto-stepping if we get any user input. def step(self, *e): self._autostep = 0; self._step() def match(self, *e): self._autostep = 0; self._match() def expand(self, *e): self._autostep = 0; self._expand() def backtrack(self, *e): self._autostep = 0; self._backtrack() def _step(self): if self._animating_lock: return # Try expanding, matching, and backtracking (in that order) if self._expand(): pass elif self._parser.untried_match() and self._match(): pass elif self._backtrack(): pass else: self._lastoper1['text'] = 'Finished' self._lastoper2['text'] = '' self._autostep = 0 # Check if we just completed a parse. if self._parser.currently_complete(): self._autostep = 0 self._lastoper2['text'] += ' [COMPLETE PARSE]' def _expand(self, *e): if self._animating_lock: return old_frontier = self._parser.frontier() rv = self._parser.expand() if rv is not None: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = rv self._prodlist.selection_clear(0, 'end') index = self._productions.index(rv) self._prodlist.selection_set(index) self._animate_expand(old_frontier[0]) return 1 else: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = '(all expansions tried)' return 0 def _match(self, *e): if self._animating_lock: return old_frontier = self._parser.frontier() rv = self._parser.match() if rv is not None: self._lastoper1['text'] = 'Match:' self._lastoper2['text'] = rv self._animate_match(old_frontier[0]) return 1 else: self._lastoper1['text'] = 'Match:' self._lastoper2['text'] = '(failed)' return 0 def _backtrack(self, *e): if self._animating_lock: return if self._parser.backtrack(): elt = self._parser.tree() for i in self._parser.frontier()[0]: elt = elt[i] self._lastoper1['text'] = 'Backtrack' self._lastoper2['text'] = '' if isinstance(elt, Tree): self._animate_backtrack(self._parser.frontier()[0]) else: self._animate_match_backtrack(self._parser.frontier()[0]) return 1 else: self._autostep = 0 self._lastoper1['text'] = 'Finished' self._lastoper2['text'] = '' return 0 def about(self, *e): ABOUT = ("NLTK Recursive Descent Parser Application\n"+ "Written by Edward Loper") TITLE = 'About: Recursive Descent Parser Application' try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def help(self, *e): self._autostep = 0 # The default font's not very legible; try using 'fixed' instead. try: ShowText(self._top, 'Help: Recursive Descent Parser Application', (__doc__ or '').strip(), width=75, font='fixed') except: ShowText(self._top, 'Help: Recursive Descent Parser Application', (__doc__ or '').strip(), width=75) def postscript(self, *e): self._autostep = 0 self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) self._bigfont.configure(size=-(abs(size+2))) self._redraw() ######################################### ## Expand Production Selection ######################################### def _toggle_grammar(self, *e): if self._show_grammar.get(): self._prodframe.pack(fill='both', side='left', padx=2, after=self._feedbackframe) self._lastoper1['text'] = 'Show Grammar' else: self._prodframe.pack_forget() self._lastoper1['text'] = 'Hide Grammar' self._lastoper2['text'] = '' # def toggle_grammar(self, *e): # self._show_grammar = not self._show_grammar # if self._show_grammar: # self._prodframe.pack(fill='both', expand='y', side='left', # after=self._feedbackframe) # self._lastoper1['text'] = 'Show Grammar' # else: # self._prodframe.pack_forget() # self._lastoper1['text'] = 'Hide Grammar' # self._lastoper2['text'] = '' def _prodlist_select(self, event): selection = self._prodlist.curselection() if len(selection) != 1: return index = int(selection[0]) old_frontier = self._parser.frontier() production = self._parser.expand(self._productions[index]) if production: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = production self._prodlist.selection_clear(0, 'end') self._prodlist.selection_set(index) self._animate_expand(old_frontier[0]) else: # Reset the production selections. self._prodlist.selection_clear(0, 'end') for prod in self._parser.expandable_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) ######################################### ## Animation ######################################### def _animate_expand(self, treeloc): oldwidget = self._get(self._tree, treeloc) oldtree = oldwidget.parent() top = not isinstance(oldtree.parent(), TreeSegmentWidget) tree = self._parser.tree() for i in treeloc: tree = tree[i] widget = tree_to_treesegment(self._canvas, tree, node_font=self._boldfont, leaf_color='white', tree_width=2, tree_color='white', node_color='white', leaf_font=self._font) widget.label()['color'] = '#20a050' (oldx, oldy) = oldtree.label().bbox()[:2] (newx, newy) = widget.label().bbox()[:2] widget.move(oldx-newx, oldy-newy) if top: self._cframe.add_widget(widget, 0, 5) widget.move(30-widget.label().bbox()[0], 0) self._tree = widget else: oldtree.parent().replace_child(oldtree, widget) # Move the children over so they don't overlap. # Line the children up in a strange way. if widget.subtrees(): dx = (oldx + widget.label().width()/2 - widget.subtrees()[0].bbox()[0]/2 - widget.subtrees()[0].bbox()[2]/2) for subtree in widget.subtrees(): subtree.move(dx, 0) self._makeroom(widget) if top: self._cframe.destroy_widget(oldtree) else: oldtree.destroy() colors = ['gray%d' % (10*int(10*x/self._animation_frames.get())) for x in range(self._animation_frames.get(),0,-1)] # Move the text string down, if necessary. dy = widget.bbox()[3] + 30 - self._canvas.coords(self._textline)[1] if dy > 0: for twidget in self._textwidgets: twidget.move(0, dy) self._canvas.move(self._textline, 0, dy) self._animate_expand_frame(widget, colors) def _makeroom(self, treeseg): """ Make sure that no sibling tree bbox's overlap. """ parent = treeseg.parent() if not isinstance(parent, TreeSegmentWidget): return index = parent.subtrees().index(treeseg) # Handle siblings to the right rsiblings = parent.subtrees()[index+1:] if rsiblings: dx = treeseg.bbox()[2] - rsiblings[0].bbox()[0] + 10 for sibling in rsiblings: sibling.move(dx, 0) # Handle siblings to the left if index > 0: lsibling = parent.subtrees()[index-1] dx = max(0, lsibling.bbox()[2] - treeseg.bbox()[0] + 10) treeseg.move(dx, 0) # Keep working up the tree. self._makeroom(parent) def _animate_expand_frame(self, widget, colors): if len(colors) > 0: self._animating_lock = 1 widget['color'] = colors[0] for subtree in widget.subtrees(): if isinstance(subtree, TreeSegmentWidget): subtree.label()['color'] = colors[0] else: subtree['color'] = colors[0] self._top.after(50, self._animate_expand_frame, widget, colors[1:]) else: widget['color'] = 'black' for subtree in widget.subtrees(): if isinstance(subtree, TreeSegmentWidget): subtree.label()['color'] = 'black' else: subtree['color'] = 'black' self._redraw_quick() widget.label()['color'] = 'black' self._animating_lock = 0 if self._autostep: self._step() def _animate_backtrack(self, treeloc): # Flash red first, if we're animating. if self._animation_frames.get() == 0: colors = [] else: colors = ['#a00000', '#000000', '#a00000'] colors += ['gray%d' % (10*int(10*x/(self._animation_frames.get()))) for x in range(1, self._animation_frames.get()+1)] widgets = [self._get(self._tree, treeloc).parent()] for subtree in widgets[0].subtrees(): if isinstance(subtree, TreeSegmentWidget): widgets.append(subtree.label()) else: widgets.append(subtree) self._animate_backtrack_frame(widgets, colors) def _animate_backtrack_frame(self, widgets, colors): if len(colors) > 0: self._animating_lock = 1 for widget in widgets: widget['color'] = colors[0] self._top.after(50, self._animate_backtrack_frame, widgets, colors[1:]) else: for widget in widgets[0].subtrees(): widgets[0].remove_child(widget) widget.destroy() self._redraw_quick() self._animating_lock = 0 if self._autostep: self._step() def _animate_match_backtrack(self, treeloc): widget = self._get(self._tree, treeloc) node = widget.parent().label() dy = (1.0 * (node.bbox()[3] - widget.bbox()[1] + 14) / max(1, self._animation_frames.get())) self._animate_match_backtrack_frame(self._animation_frames.get(), widget, dy) def _animate_match(self, treeloc): widget = self._get(self._tree, treeloc) dy = ((self._textwidgets[0].bbox()[1] - widget.bbox()[3] - 10.0) / max(1, self._animation_frames.get())) self._animate_match_frame(self._animation_frames.get(), widget, dy) def _animate_match_frame(self, frame, widget, dy): if frame > 0: self._animating_lock = 1 widget.move(0, dy) self._top.after(10, self._animate_match_frame, frame-1, widget, dy) else: widget['color'] = '#006040' self._redraw_quick() self._animating_lock = 0 if self._autostep: self._step() def _animate_match_backtrack_frame(self, frame, widget, dy): if frame > 0: self._animating_lock = 1 widget.move(0, dy) self._top.after(10, self._animate_match_backtrack_frame, frame-1, widget, dy) else: widget.parent().remove_child(widget) widget.destroy() self._animating_lock = 0 if self._autostep: self._step() def edit_grammar(self, *e): CFGEditor(self._top, self._parser.grammar(), self.set_grammar) def set_grammar(self, grammar): self._parser.set_grammar(grammar) self._productions = list(grammar.productions()) self._prodlist.delete(0, 'end') for production in self._productions: self._prodlist.insert('end', (' %s' % production)) def edit_sentence(self, *e): sentence = " ".join(self._sent) title = 'Edit Text' instr = 'Enter a new sentence to parse.' EntryDialog(self._top, sentence, instr, self.set_sentence, title) def set_sentence(self, sentence): self._sent = sentence.split() #[XX] use tagged? self.reset()
class DrtGlueDemo(object): def __init__(self, examples): # Set up the main window. self._top = Tk() self._top.title("DRT Glue Demo") # Set up key bindings. self._init_bindings() # Initialize the fonts.self._error = None self._init_fonts(self._top) self._examples = examples self._readingCache = [None for example in examples] # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Set the data to None self._curExample = -1 self._readings = [] self._drs = None self._drsWidget = None self._error = None self._init_glue() # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_exampleListbox(self._top) self._init_readingListbox(self._top) self._init_canvas(self._top) # Resize callback self._canvas.bind("<Configure>", self._configure) ######################################### ## Initialization Helpers ######################################### def _init_glue(self): tagger = RegexpTagger( [ ("^(David|Mary|John)$", "NNP"), ("^(walks|sees|eats|chases|believes|gives|sleeps|chases|persuades|tries|seems|leaves)$", "VB"), ("^(go|order|vanish|find|approach)$", "VB"), ("^(a)$", "ex_quant"), ("^(every)$", "univ_quant"), ("^(sandwich|man|dog|pizza|unicorn|cat|senator)$", "NN"), ("^(big|gray|former)$", "JJ"), ("^(him|himself)$", "PRP"), ] ) depparser = MaltParser(tagger=tagger) self._glue = DrtGlue(depparser=depparser, remove_duplicates=False) def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget("size")) self._boldfont = Font(family="helvetica", weight="bold", size=self._size.get()) self._font = Font(family="helvetica", size=self._size.get()) if self._size.get() < 0: big = self._size.get() - 2 else: big = self._size.get() + 2 self._bigfont = Font(family="helvetica", weight="bold", size=big) def _init_exampleListbox(self, parent): self._exampleFrame = listframe = Frame(parent) self._exampleFrame.pack(fill="both", side="left", padx=2) self._exampleList_label = Label(self._exampleFrame, font=self._boldfont, text="Examples") self._exampleList_label.pack() self._exampleList = Listbox( self._exampleFrame, selectmode="single", relief="groove", background="white", foreground="#909090", font=self._font, selectforeground="#004040", selectbackground="#c0f0c0", ) self._exampleList.pack(side="right", fill="both", expand=1) for example in self._examples: self._exampleList.insert("end", (" %s" % example)) self._exampleList.config(height=min(len(self._examples), 25), width=40) # Add a scrollbar if there are more than 25 examples. if len(self._examples) > 25: listscroll = Scrollbar(self._exampleFrame, orient="vertical") self._exampleList.config(yscrollcommand=listscroll.set) listscroll.config(command=self._exampleList.yview) listscroll.pack(side="left", fill="y") # If they select a example, apply it. self._exampleList.bind("<<ListboxSelect>>", self._exampleList_select) def _init_readingListbox(self, parent): self._readingFrame = listframe = Frame(parent) self._readingFrame.pack(fill="both", side="left", padx=2) self._readingList_label = Label(self._readingFrame, font=self._boldfont, text="Readings") self._readingList_label.pack() self._readingList = Listbox( self._readingFrame, selectmode="single", relief="groove", background="white", foreground="#909090", font=self._font, selectforeground="#004040", selectbackground="#c0f0c0", ) self._readingList.pack(side="right", fill="both", expand=1) # Add a scrollbar if there are more than 25 examples. listscroll = Scrollbar(self._readingFrame, orient="vertical") self._readingList.config(yscrollcommand=listscroll.set) listscroll.config(command=self._readingList.yview) listscroll.pack(side="right", fill="y") self._populate_readingListbox() def _populate_readingListbox(self): # Populate the listbox with integers self._readingList.delete(0, "end") for i in range(len(self._readings)): self._readingList.insert("end", (" %s" % (i + 1))) self._readingList.config(height=min(len(self._readings), 25), width=5) # If they select a example, apply it. self._readingList.bind("<<ListboxSelect>>", self._readingList_select) def _init_bindings(self): # Key bindings are a good thing. self._top.bind("<Control-q>", self.destroy) self._top.bind("<Control-x>", self.destroy) self._top.bind("<Escape>", self.destroy) self._top.bind("n", self.next) self._top.bind("<space>", self.next) self._top.bind("p", self.prev) self._top.bind("<BackSpace>", self.prev) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill="none", side="bottom", padx=3, pady=2) Button(buttonframe, text="Prev", background="#90c0d0", foreground="black", command=self.prev).pack(side="left") Button(buttonframe, text="Next", background="#90c0d0", foreground="black", command=self.next).pack(side="left") def _configure(self, event): self._autostep = 0 (x1, y1, x2, y2) = self._cframe.scrollregion() y2 = event.height - 6 self._canvas["scrollregion"] = "%d %d %d %d" % (x1, y1, x2, y2) self._redraw() def _init_canvas(self, parent): self._cframe = CanvasFrame( parent, background="white", # width=525, height=250, closeenough=10, border=2, relief="sunken", ) self._cframe.pack(expand=1, fill="both", side="top", pady=2) canvas = self._canvas = self._cframe.canvas() # Initially, there's no tree or text self._tree = None self._textwidgets = [] self._textline = None def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label="Exit", underline=1, command=self.destroy, accelerator="q") menubar.add_cascade(label="File", underline=0, menu=filemenu) actionmenu = Menu(menubar, tearoff=0) actionmenu.add_command(label="Next", underline=0, command=self.next, accelerator="n, Space") actionmenu.add_command(label="Previous", underline=0, command=self.prev, accelerator="p, Backspace") menubar.add_cascade(label="Action", underline=0, menu=actionmenu) optionmenu = Menu(menubar, tearoff=0) optionmenu.add_checkbutton( label="Remove Duplicates", underline=0, variable=self._glue.remove_duplicates, command=self._toggle_remove_duplicates, accelerator="r", ) menubar.add_cascade(label="Options", underline=0, menu=optionmenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_radiobutton(label="Tiny", variable=self._size, underline=0, value=10, command=self.resize) viewmenu.add_radiobutton(label="Small", variable=self._size, underline=0, value=12, command=self.resize) viewmenu.add_radiobutton(label="Medium", variable=self._size, underline=0, value=14, command=self.resize) viewmenu.add_radiobutton(label="Large", variable=self._size, underline=0, value=18, command=self.resize) viewmenu.add_radiobutton(label="Huge", variable=self._size, underline=0, value=24, command=self.resize) menubar.add_cascade(label="View", underline=0, menu=viewmenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label="About", underline=0, command=self.about) menubar.add_cascade(label="Help", underline=0, menu=helpmenu) parent.config(menu=menubar) ######################################### ## Main draw procedure ######################################### def _redraw(self): canvas = self._canvas # Delete the old DRS, widgets, etc. if self._drsWidget is not None: self._drsWidget.clear() if self._drs: self._drsWidget = DrsWidget(self._canvas, self._drs) self._drsWidget.draw() if self._error: self._drsWidget = DrsWidget(self._canvas, self._error) self._drsWidget.draw() ######################################### ## Button Callbacks ######################################### def destroy(self, *e): self._autostep = 0 if self._top is None: return self._top.destroy() self._top = None def prev(self, *e): selection = self._readingList.curselection() readingListSize = self._readingList.size() # there are readings if readingListSize > 0: # if one reading is currently selected if len(selection) == 1: index = int(selection[0]) # if it's on (or before) the first item if index <= 0: self._select_previous_example() else: self._readingList_store_selection(index - 1) else: # select its first reading self._readingList_store_selection(readingListSize - 1) else: self._select_previous_example() def _select_previous_example(self): # if the current example is not the first example if self._curExample > 0: self._exampleList_store_selection(self._curExample - 1) else: # go to the last example self._exampleList_store_selection(len(self._examples) - 1) def next(self, *e): selection = self._readingList.curselection() readingListSize = self._readingList.size() # if there are readings if readingListSize > 0: # if one reading is currently selected if len(selection) == 1: index = int(selection[0]) # if it's on (or past) the last item if index >= (readingListSize - 1): self._select_next_example() else: self._readingList_store_selection(index + 1) else: # select its first reading self._readingList_store_selection(0) else: self._select_next_example() def _select_next_example(self): # if the current example is not the last example if self._curExample < len(self._examples) - 1: self._exampleList_store_selection(self._curExample + 1) else: # go to the first example self._exampleList_store_selection(0) def about(self, *e): ABOUT = "NLTK Discourse Representation Theory (DRT) Glue Semantics Demo\n" + "Written by Daniel H. Garrette" TITLE = "About: NLTK DRT Glue Demo" try: from tkMessageBox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def postscript(self, *e): self._autostep = 0 self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) self._bigfont.configure(size=-(abs(size + 2))) self._redraw() def _toggle_remove_duplicates(self): self._glue.remove_duplicates = not self._glue.remove_duplicates self._exampleList.selection_clear(0, "end") self._readings = [] self._populate_readingListbox() self._readingCache = [None for ex in self._examples] self._curExample = -1 self._error = None self._drs = None self._redraw() def _exampleList_select(self, event): selection = self._exampleList.curselection() if len(selection) != 1: return self._exampleList_store_selection(int(selection[0])) def _exampleList_store_selection(self, index): self._curExample = index example = self._examples[index] self._exampleList.selection_clear(0, "end") if example: cache = self._readingCache[index] if cache: if isinstance(cache, list): self._readings = cache self._error = None else: self._readings = [] self._error = cache else: try: self._readings = self._glue.parse_to_meaning(example) self._error = None self._readingCache[index] = self._readings except Exception as e: self._readings = [] self._error = DrtVariableExpression(Variable("Error: " + str(e))) self._readingCache[index] = self._error # add a star to the end of the example self._exampleList.delete(index) self._exampleList.insert(index, (" %s *" % example)) self._exampleList.config(height=min(len(self._examples), 25), width=40) self._populate_readingListbox() self._exampleList.selection_set(index) self._drs = None self._redraw() def _readingList_select(self, event): selection = self._readingList.curselection() if len(selection) != 1: return self._readingList_store_selection(int(selection[0])) def _readingList_store_selection(self, index): reading = self._readings[index] self._readingList.selection_clear(0, "end") if reading: self._readingList.selection_set(index) self._drs = reading.simplify().normalize().resolve_anaphora() self._redraw()
class RecursiveDescentApp(object): """ A graphical tool for exploring the recursive descent parser. The tool displays the parser's tree and the remaining text, and allows the user to control the parser's operation. In particular, the user can expand subtrees on the frontier, match tokens on the frontier against the text, and backtrack. A "step" button simply steps through the parsing process, performing the operations that ``RecursiveDescentParser`` would use. """ def __init__(self, grammar, sent, trace=0): self._sent = sent self._parser = SteppingRecursiveDescentParser(grammar, trace) # Set up the main window. self._top = Tk() self._top.title('Recursive Descent Parser Application') # Set up key bindings. self._init_bindings() # Initialize the fonts. self._init_fonts(self._top) # Animations. animating_lock is a lock to prevent the demo # from performing new operations while it's animating. self._animation_frames = IntVar(self._top) self._animation_frames.set(5) self._animating_lock = 0 self._autostep = 0 # The user can hide the grammar. self._show_grammar = IntVar(self._top) self._show_grammar.set(1) # Create the basic frames. self._init_menubar(self._top) self._init_buttons(self._top) self._init_feedback(self._top) self._init_grammar(self._top) self._init_canvas(self._top) # Initialize the parser. self._parser.initialize(self._sent) # Resize callback self._canvas.bind('<Configure>', self._configure) ######################################### ## Initialization Helpers ######################################### def _init_fonts(self, root): # See: <http://www.astro.washington.edu/owen/ROTKFolklore.html> self._sysfont = tkinter.font.Font(font=Button()["font"]) root.option_add("*Font", self._sysfont) # TWhat's our font size (default=same as sysfont) self._size = IntVar(root) self._size.set(self._sysfont.cget('size')) self._boldfont = tkinter.font.Font(family='helvetica', weight='bold', size=self._size.get()) self._font = tkinter.font.Font(family='helvetica', size=self._size.get()) if self._size.get() < 0: big = self._size.get() - 2 else: big = self._size.get() + 2 self._bigfont = tkinter.font.Font(family='helvetica', weight='bold', size=big) def _init_grammar(self, parent): # Grammar view. self._prodframe = listframe = Frame(parent) self._prodframe.pack(fill='both', side='left', padx=2) self._prodlist_label = Label(self._prodframe, font=self._boldfont, text='Available Expansions') self._prodlist_label.pack() self._prodlist = Listbox(self._prodframe, selectmode='single', relief='groove', background='white', foreground='#909090', font=self._font, selectforeground='#004040', selectbackground='#c0f0c0') self._prodlist.pack(side='right', fill='both', expand=1) self._productions = list(self._parser.grammar().productions()) for production in self._productions: self._prodlist.insert('end', (' %s' % production)) self._prodlist.config(height=min(len(self._productions), 25)) # Add a scrollbar if there are more than 25 productions. if len(self._productions) > 25: listscroll = Scrollbar(self._prodframe, orient='vertical') self._prodlist.config(yscrollcommand=listscroll.set) listscroll.config(command=self._prodlist.yview) listscroll.pack(side='left', fill='y') # If they select a production, apply it. self._prodlist.bind('<<ListboxSelect>>', self._prodlist_select) def _init_bindings(self): # Key bindings are a good thing. self._top.bind('<Control-q>', self.destroy) self._top.bind('<Control-x>', self.destroy) self._top.bind('<Escape>', self.destroy) self._top.bind('e', self.expand) #self._top.bind('<Alt-e>', self.expand) #self._top.bind('<Control-e>', self.expand) self._top.bind('m', self.match) self._top.bind('<Alt-m>', self.match) self._top.bind('<Control-m>', self.match) self._top.bind('b', self.backtrack) self._top.bind('<Alt-b>', self.backtrack) self._top.bind('<Control-b>', self.backtrack) self._top.bind('<Control-z>', self.backtrack) self._top.bind('<BackSpace>', self.backtrack) self._top.bind('a', self.autostep) #self._top.bind('<Control-a>', self.autostep) self._top.bind('<Control-space>', self.autostep) self._top.bind('<Control-c>', self.cancel_autostep) self._top.bind('<space>', self.step) self._top.bind('<Delete>', self.reset) self._top.bind('<Control-p>', self.postscript) #self._top.bind('<h>', self.help) #self._top.bind('<Alt-h>', self.help) self._top.bind('<Control-h>', self.help) self._top.bind('<F1>', self.help) #self._top.bind('<g>', self.toggle_grammar) #self._top.bind('<Alt-g>', self.toggle_grammar) #self._top.bind('<Control-g>', self.toggle_grammar) self._top.bind('<Control-g>', self.edit_grammar) self._top.bind('<Control-t>', self.edit_sentence) def _init_buttons(self, parent): # Set up the frames. self._buttonframe = buttonframe = Frame(parent) buttonframe.pack(fill='none', side='bottom', padx=3, pady=2) Button( buttonframe, text='Step', background='#90c0d0', foreground='black', command=self.step, ).pack(side='left') Button( buttonframe, text='Autostep', background='#90c0d0', foreground='black', command=self.autostep, ).pack(side='left') Button(buttonframe, text='Expand', underline=0, background='#90f090', foreground='black', command=self.expand).pack(side='left') Button(buttonframe, text='Match', underline=0, background='#90f090', foreground='black', command=self.match).pack(side='left') Button(buttonframe, text='Backtrack', underline=0, background='#f0a0a0', foreground='black', command=self.backtrack).pack(side='left') # Replace autostep... # self._autostep_button = Button(buttonframe, text='Autostep', # underline=0, command=self.autostep) # self._autostep_button.pack(side='left') def _configure(self, event): self._autostep = 0 (x1, y1, x2, y2) = self._cframe.scrollregion() y2 = event.height - 6 self._canvas['scrollregion'] = '%d %d %d %d' % (x1, y1, x2, y2) self._redraw() def _init_feedback(self, parent): self._feedbackframe = feedbackframe = Frame(parent) feedbackframe.pack(fill='x', side='bottom', padx=3, pady=3) self._lastoper_label = Label(feedbackframe, text='Last Operation:', font=self._font) self._lastoper_label.pack(side='left') lastoperframe = Frame(feedbackframe, relief='sunken', border=1) lastoperframe.pack(fill='x', side='right', expand=1, padx=5) self._lastoper1 = Label(lastoperframe, foreground='#007070', background='#f0f0f0', font=self._font) self._lastoper2 = Label(lastoperframe, anchor='w', width=30, foreground='#004040', background='#f0f0f0', font=self._font) self._lastoper1.pack(side='left') self._lastoper2.pack(side='left', fill='x', expand=1) def _init_canvas(self, parent): self._cframe = CanvasFrame( parent, background='white', #width=525, height=250, closeenough=10, border=2, relief='sunken') self._cframe.pack(expand=1, fill='both', side='top', pady=2) canvas = self._canvas = self._cframe.canvas() # Initially, there's no tree or text self._tree = None self._textwidgets = [] self._textline = None def _init_menubar(self, parent): menubar = Menu(parent) filemenu = Menu(menubar, tearoff=0) filemenu.add_command(label='Reset Parser', underline=0, command=self.reset, accelerator='Del') filemenu.add_command(label='Print to Postscript', underline=0, command=self.postscript, accelerator='Ctrl-p') filemenu.add_command(label='Exit', underline=1, command=self.destroy, accelerator='Ctrl-x') menubar.add_cascade(label='File', underline=0, menu=filemenu) editmenu = Menu(menubar, tearoff=0) editmenu.add_command(label='Edit Grammar', underline=5, command=self.edit_grammar, accelerator='Ctrl-g') editmenu.add_command(label='Edit Text', underline=5, command=self.edit_sentence, accelerator='Ctrl-t') menubar.add_cascade(label='Edit', underline=0, menu=editmenu) rulemenu = Menu(menubar, tearoff=0) rulemenu.add_command(label='Step', underline=1, command=self.step, accelerator='Space') rulemenu.add_separator() rulemenu.add_command(label='Match', underline=0, command=self.match, accelerator='Ctrl-m') rulemenu.add_command(label='Expand', underline=0, command=self.expand, accelerator='Ctrl-e') rulemenu.add_separator() rulemenu.add_command(label='Backtrack', underline=0, command=self.backtrack, accelerator='Ctrl-b') menubar.add_cascade(label='Apply', underline=0, menu=rulemenu) viewmenu = Menu(menubar, tearoff=0) viewmenu.add_checkbutton(label="Show Grammar", underline=0, variable=self._show_grammar, command=self._toggle_grammar) viewmenu.add_separator() viewmenu.add_radiobutton(label='Tiny', variable=self._size, underline=0, value=10, command=self.resize) viewmenu.add_radiobutton(label='Small', variable=self._size, underline=0, value=12, command=self.resize) viewmenu.add_radiobutton(label='Medium', variable=self._size, underline=0, value=14, command=self.resize) viewmenu.add_radiobutton(label='Large', variable=self._size, underline=0, value=18, command=self.resize) viewmenu.add_radiobutton(label='Huge', variable=self._size, underline=0, value=24, command=self.resize) menubar.add_cascade(label='View', underline=0, menu=viewmenu) animatemenu = Menu(menubar, tearoff=0) animatemenu.add_radiobutton(label="No Animation", underline=0, variable=self._animation_frames, value=0) animatemenu.add_radiobutton(label="Slow Animation", underline=0, variable=self._animation_frames, value=10, accelerator='-') animatemenu.add_radiobutton(label="Normal Animation", underline=0, variable=self._animation_frames, value=5, accelerator='=') animatemenu.add_radiobutton(label="Fast Animation", underline=0, variable=self._animation_frames, value=2, accelerator='+') menubar.add_cascade(label="Animate", underline=1, menu=animatemenu) helpmenu = Menu(menubar, tearoff=0) helpmenu.add_command(label='About', underline=0, command=self.about) helpmenu.add_command(label='Instructions', underline=0, command=self.help, accelerator='F1') menubar.add_cascade(label='Help', underline=0, menu=helpmenu) parent.config(menu=menubar) ######################################### ## Helper ######################################### def _get(self, widget, treeloc): for i in treeloc: widget = widget.subtrees()[i] if isinstance(widget, TreeSegmentWidget): widget = widget.label() return widget ######################################### ## Main draw procedure ######################################### def _redraw(self): canvas = self._canvas # Delete the old tree, widgets, etc. if self._tree is not None: self._cframe.destroy_widget(self._tree) for twidget in self._textwidgets: self._cframe.destroy_widget(twidget) if self._textline is not None: self._canvas.delete(self._textline) # Draw the tree. helv = ('helvetica', -self._size.get()) bold = ('helvetica', -self._size.get(), 'bold') attribs = { 'tree_color': '#000000', 'tree_width': 2, 'node_font': bold, 'leaf_font': helv, } tree = self._parser.tree() self._tree = tree_to_treesegment(canvas, tree, **attribs) self._cframe.add_widget(self._tree, 30, 5) # Draw the text. helv = ('helvetica', -self._size.get()) bottom = y = self._cframe.scrollregion()[3] self._textwidgets = [ TextWidget(canvas, word, font=self._font) for word in self._sent ] for twidget in self._textwidgets: self._cframe.add_widget(twidget, 0, 0) twidget.move(0, bottom - twidget.bbox()[3] - 5) y = min(y, twidget.bbox()[1]) # Draw a line over the text, to separate it from the tree. self._textline = canvas.create_line(-5000, y - 5, 5000, y - 5, dash='.') # Highlight appropriate nodes. self._highlight_nodes() self._highlight_prodlist() # Make sure the text lines up. self._position_text() def _redraw_quick(self): # This should be more-or-less sufficient after an animation. self._highlight_nodes() self._highlight_prodlist() self._position_text() def _highlight_nodes(self): # Highlight the list of nodes to be checked. bold = ('helvetica', -self._size.get(), 'bold') for treeloc in self._parser.frontier()[:1]: self._get(self._tree, treeloc)['color'] = '#20a050' self._get(self._tree, treeloc)['font'] = bold for treeloc in self._parser.frontier()[1:]: self._get(self._tree, treeloc)['color'] = '#008080' def _highlight_prodlist(self): # Highlight the productions that can be expanded. # Boy, too bad tkinter doesn't implement Listbox.itemconfig; # that would be pretty useful here. self._prodlist.delete(0, 'end') expandable = self._parser.expandable_productions() untried = self._parser.untried_expandable_productions() productions = self._productions for index in range(len(productions)): if productions[index] in expandable: if productions[index] in untried: self._prodlist.insert(index, ' %s' % productions[index]) else: self._prodlist.insert(index, ' %s (TRIED)' % productions[index]) self._prodlist.selection_set(index) else: self._prodlist.insert(index, ' %s' % productions[index]) def _position_text(self): # Line up the text widgets that are matched against the tree numwords = len(self._sent) num_matched = numwords - len(self._parser.remaining_text()) leaves = self._tree_leaves()[:num_matched] xmax = self._tree.bbox()[0] for i in range(0, len(leaves)): widget = self._textwidgets[i] leaf = leaves[i] widget['color'] = '#006040' leaf['color'] = '#006040' widget.move(leaf.bbox()[0] - widget.bbox()[0], 0) xmax = widget.bbox()[2] + 10 # Line up the text widgets that are not matched against the tree. for i in range(len(leaves), numwords): widget = self._textwidgets[i] widget['color'] = '#a0a0a0' widget.move(xmax - widget.bbox()[0], 0) xmax = widget.bbox()[2] + 10 # If we have a complete parse, make everything green :) if self._parser.currently_complete(): for twidget in self._textwidgets: twidget['color'] = '#00a000' # Move the matched leaves down to the text. for i in range(0, len(leaves)): widget = self._textwidgets[i] leaf = leaves[i] dy = widget.bbox()[1] - leaf.bbox()[3] - 10.0 dy = max(dy, leaf.parent().label().bbox()[3] - leaf.bbox()[3] + 10) leaf.move(0, dy) def _tree_leaves(self, tree=None): if tree is None: tree = self._tree if isinstance(tree, TreeSegmentWidget): leaves = [] for child in tree.subtrees(): leaves += self._tree_leaves(child) return leaves else: return [tree] ######################################### ## Button Callbacks ######################################### def destroy(self, *e): self._autostep = 0 if self._top is None: return self._top.destroy() self._top = None def reset(self, *e): self._autostep = 0 self._parser.initialize(self._sent) self._lastoper1['text'] = 'Reset Application' self._lastoper2['text'] = '' self._redraw() def autostep(self, *e): if self._animation_frames.get() == 0: self._animation_frames.set(2) if self._autostep: self._autostep = 0 else: self._autostep = 1 self._step() def cancel_autostep(self, *e): #self._autostep_button['text'] = 'Autostep' self._autostep = 0 # Make sure to stop auto-stepping if we get any user input. def step(self, *e): self._autostep = 0 self._step() def match(self, *e): self._autostep = 0 self._match() def expand(self, *e): self._autostep = 0 self._expand() def backtrack(self, *e): self._autostep = 0 self._backtrack() def _step(self): if self._animating_lock: return # Try expanding, matching, and backtracking (in that order) if self._expand(): pass elif self._parser.untried_match() and self._match(): pass elif self._backtrack(): pass else: self._lastoper1['text'] = 'Finished' self._lastoper2['text'] = '' self._autostep = 0 # Check if we just completed a parse. if self._parser.currently_complete(): self._autostep = 0 self._lastoper2['text'] += ' [COMPLETE PARSE]' def _expand(self, *e): if self._animating_lock: return old_frontier = self._parser.frontier() rv = self._parser.expand() if rv is not None: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = rv self._prodlist.selection_clear(0, 'end') index = self._productions.index(rv) self._prodlist.selection_set(index) self._animate_expand(old_frontier[0]) return True else: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = '(all expansions tried)' return False def _match(self, *e): if self._animating_lock: return old_frontier = self._parser.frontier() rv = self._parser.match() if rv is not None: self._lastoper1['text'] = 'Match:' self._lastoper2['text'] = rv self._animate_match(old_frontier[0]) return True else: self._lastoper1['text'] = 'Match:' self._lastoper2['text'] = '(failed)' return False def _backtrack(self, *e): if self._animating_lock: return if self._parser.backtrack(): elt = self._parser.tree() for i in self._parser.frontier()[0]: elt = elt[i] self._lastoper1['text'] = 'Backtrack' self._lastoper2['text'] = '' if isinstance(elt, Tree): self._animate_backtrack(self._parser.frontier()[0]) else: self._animate_match_backtrack(self._parser.frontier()[0]) return True else: self._autostep = 0 self._lastoper1['text'] = 'Finished' self._lastoper2['text'] = '' return False def about(self, *e): ABOUT = ("NLTK Recursive Descent Parser Application\n" + "Written by Edward Loper") TITLE = 'About: Recursive Descent Parser Application' try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT) def help(self, *e): self._autostep = 0 # The default font's not very legible; try using 'fixed' instead. try: ShowText(self._top, 'Help: Recursive Descent Parser Application', (__doc__ or '').strip(), width=75, font='fixed') except: ShowText(self._top, 'Help: Recursive Descent Parser Application', (__doc__ or '').strip(), width=75) def postscript(self, *e): self._autostep = 0 self._cframe.print_to_file() def mainloop(self, *args, **kwargs): """ Enter the Tkinter mainloop. This function must be called if this demo is created from a non-interactive program (e.g. from a secript); otherwise, the demo will close as soon as the script completes. """ if in_idle(): return self._top.mainloop(*args, **kwargs) def resize(self, size=None): if size is not None: self._size.set(size) size = self._size.get() self._font.configure(size=-(abs(size))) self._boldfont.configure(size=-(abs(size))) self._sysfont.configure(size=-(abs(size))) self._bigfont.configure(size=-(abs(size + 2))) self._redraw() ######################################### ## Expand Production Selection ######################################### def _toggle_grammar(self, *e): if self._show_grammar.get(): self._prodframe.pack(fill='both', side='left', padx=2, after=self._feedbackframe) self._lastoper1['text'] = 'Show Grammar' else: self._prodframe.pack_forget() self._lastoper1['text'] = 'Hide Grammar' self._lastoper2['text'] = '' # def toggle_grammar(self, *e): # self._show_grammar = not self._show_grammar # if self._show_grammar: # self._prodframe.pack(fill='both', expand='y', side='left', # after=self._feedbackframe) # self._lastoper1['text'] = 'Show Grammar' # else: # self._prodframe.pack_forget() # self._lastoper1['text'] = 'Hide Grammar' # self._lastoper2['text'] = '' def _prodlist_select(self, event): selection = self._prodlist.curselection() if len(selection) != 1: return index = int(selection[0]) old_frontier = self._parser.frontier() production = self._parser.expand(self._productions[index]) if production: self._lastoper1['text'] = 'Expand:' self._lastoper2['text'] = production self._prodlist.selection_clear(0, 'end') self._prodlist.selection_set(index) self._animate_expand(old_frontier[0]) else: # Reset the production selections. self._prodlist.selection_clear(0, 'end') for prod in self._parser.expandable_productions(): index = self._productions.index(prod) self._prodlist.selection_set(index) ######################################### ## Animation ######################################### def _animate_expand(self, treeloc): oldwidget = self._get(self._tree, treeloc) oldtree = oldwidget.parent() top = not isinstance(oldtree.parent(), TreeSegmentWidget) tree = self._parser.tree() for i in treeloc: tree = tree[i] widget = tree_to_treesegment(self._canvas, tree, node_font=self._boldfont, leaf_color='white', tree_width=2, tree_color='white', node_color='white', leaf_font=self._font) widget.label()['color'] = '#20a050' (oldx, oldy) = oldtree.label().bbox()[:2] (newx, newy) = widget.label().bbox()[:2] widget.move(oldx - newx, oldy - newy) if top: self._cframe.add_widget(widget, 0, 5) widget.move(30 - widget.label().bbox()[0], 0) self._tree = widget else: oldtree.parent().replace_child(oldtree, widget) # Move the children over so they don't overlap. # Line the children up in a strange way. if widget.subtrees(): dx = (oldx + widget.label().width() / 2 - widget.subtrees()[0].bbox()[0] / 2 - widget.subtrees()[0].bbox()[2] / 2) for subtree in widget.subtrees(): subtree.move(dx, 0) self._makeroom(widget) if top: self._cframe.destroy_widget(oldtree) else: oldtree.destroy() colors = [ 'gray%d' % (10 * int(10 * x / self._animation_frames.get())) for x in range(self._animation_frames.get(), 0, -1) ] # Move the text string down, if necessary. dy = widget.bbox()[3] + 30 - self._canvas.coords(self._textline)[1] if dy > 0: for twidget in self._textwidgets: twidget.move(0, dy) self._canvas.move(self._textline, 0, dy) self._animate_expand_frame(widget, colors) def _makeroom(self, treeseg): """ Make sure that no sibling tree bbox's overlap. """ parent = treeseg.parent() if not isinstance(parent, TreeSegmentWidget): return index = parent.subtrees().index(treeseg) # Handle siblings to the right rsiblings = parent.subtrees()[index + 1:] if rsiblings: dx = treeseg.bbox()[2] - rsiblings[0].bbox()[0] + 10 for sibling in rsiblings: sibling.move(dx, 0) # Handle siblings to the left if index > 0: lsibling = parent.subtrees()[index - 1] dx = max(0, lsibling.bbox()[2] - treeseg.bbox()[0] + 10) treeseg.move(dx, 0) # Keep working up the tree. self._makeroom(parent) def _animate_expand_frame(self, widget, colors): if len(colors) > 0: self._animating_lock = 1 widget['color'] = colors[0] for subtree in widget.subtrees(): if isinstance(subtree, TreeSegmentWidget): subtree.label()['color'] = colors[0] else: subtree['color'] = colors[0] self._top.after(50, self._animate_expand_frame, widget, colors[1:]) else: widget['color'] = 'black' for subtree in widget.subtrees(): if isinstance(subtree, TreeSegmentWidget): subtree.label()['color'] = 'black' else: subtree['color'] = 'black' self._redraw_quick() widget.label()['color'] = 'black' self._animating_lock = 0 if self._autostep: self._step() def _animate_backtrack(self, treeloc): # Flash red first, if we're animating. if self._animation_frames.get() == 0: colors = [] else: colors = ['#a00000', '#000000', '#a00000'] colors += [ 'gray%d' % (10 * int(10 * x / (self._animation_frames.get()))) for x in range(1, self._animation_frames.get() + 1) ] widgets = [self._get(self._tree, treeloc).parent()] for subtree in widgets[0].subtrees(): if isinstance(subtree, TreeSegmentWidget): widgets.append(subtree.label()) else: widgets.append(subtree) self._animate_backtrack_frame(widgets, colors) def _animate_backtrack_frame(self, widgets, colors): if len(colors) > 0: self._animating_lock = 1 for widget in widgets: widget['color'] = colors[0] self._top.after(50, self._animate_backtrack_frame, widgets, colors[1:]) else: for widget in widgets[0].subtrees(): widgets[0].remove_child(widget) widget.destroy() self._redraw_quick() self._animating_lock = 0 if self._autostep: self._step() def _animate_match_backtrack(self, treeloc): widget = self._get(self._tree, treeloc) node = widget.parent().label() dy = (1.0 * (node.bbox()[3] - widget.bbox()[1] + 14) / max(1, self._animation_frames.get())) self._animate_match_backtrack_frame(self._animation_frames.get(), widget, dy) def _animate_match(self, treeloc): widget = self._get(self._tree, treeloc) dy = ((self._textwidgets[0].bbox()[1] - widget.bbox()[3] - 10.0) / max(1, self._animation_frames.get())) self._animate_match_frame(self._animation_frames.get(), widget, dy) def _animate_match_frame(self, frame, widget, dy): if frame > 0: self._animating_lock = 1 widget.move(0, dy) self._top.after(10, self._animate_match_frame, frame - 1, widget, dy) else: widget['color'] = '#006040' self._redraw_quick() self._animating_lock = 0 if self._autostep: self._step() def _animate_match_backtrack_frame(self, frame, widget, dy): if frame > 0: self._animating_lock = 1 widget.move(0, dy) self._top.after(10, self._animate_match_backtrack_frame, frame - 1, widget, dy) else: widget.parent().remove_child(widget) widget.destroy() self._animating_lock = 0 if self._autostep: self._step() def edit_grammar(self, *e): CFGEditor(self._top, self._parser.grammar(), self.set_grammar) def set_grammar(self, grammar): self._parser.set_grammar(grammar) self._productions = list(grammar.productions()) self._prodlist.delete(0, 'end') for production in self._productions: self._prodlist.insert('end', (' %s' % production)) def edit_sentence(self, *e): sentence = " ".join(self._sent) title = 'Edit Text' instr = 'Enter a new sentence to parse.' EntryDialog(self._top, sentence, instr, self.set_sentence, title) def set_sentence(self, sentence): self._sent = sentence.split() #[XX] use tagged? self.reset()
class DataDisplay(Frame): def __init__(self, widget_options): super().__init__() self.widget_options = widget_options self.init_gui() def init_gui(self): self.columnconfigure(0, weight=3) self.columnconfigure(1, weight=2) self.columnconfigure(2, weight=2) self.rowconfigure(1, weight=1) self.names = Listbox(self, **self.widget_options) self.date_added = Listbox(self, **self.widget_options) self.expdate = Listbox(self, **self.widget_options) self.names.bind("<ButtonRelease-1>", self.select_fields) self.date_added.bind("<ButtonRelease-1>", self.select_fields) self.expdate.bind("<ButtonRelease-1>", self.select_fields) self.name_label = Label(self, text="Food Name", **self.widget_options) self.added_label = Label(self, text="Date Added", **self.widget_options) self.exp_label = Label(self, text="Expiration Date", **self.widget_options) self.name_label.grid(row=0, column=0, sticky=N + S + E + W) self.added_label.grid(row=0, column=1, sticky=N + S + E + W) self.exp_label.grid(row=0, column=2, sticky=N + S + E + W) self.names.grid(row=1, column=0, sticky=N + S + E + W) self.date_added.grid(row=1, column=1, sticky=N + S + E + W) self.expdate.grid(row=1, column=2, sticky=N + S + E + W) def delete(self, *args): self.names.delete(*args) self.date_added.delete(*args) self.expdate.delete(*args) def insert(self, where, item): self.names.insert(where, item[0]) self.date_added.insert(where, item[1]) self.expdate.insert(where, item[2]) def get(self, *args): name_select = self.names.curselection() date_added_select = self.date_added.curselection() expdate_select = self.expdate.curselection() item = (self.names.get(*args), self.date_added.get(*args), self.expdate.get(*args)) return "{} {} {}".format(*item) def select_fields(self, event=None): instance = event.widget sel_ind = instance.curselection()[0] if instance.curselection() else 0 self.names.selection_clear(0, END) self.date_added.selection_clear(0, END) self.expdate.selection_clear(0, END) self.date_added.selection_set(sel_ind) self.expdate.selection_set(sel_ind) self.names.selection_set(sel_ind)