Exemple #1
1
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()
Exemple #2
0
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()]
Exemple #3
0
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)
Exemple #6
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
Exemple #7
0
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))
Exemple #9
0
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())
Exemple #12
0
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()
Exemple #13
0
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()
Exemple #14
0
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)
Exemple #15
0
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()
Exemple #16
0
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']
Exemple #17
0
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 = []
Exemple #18
0
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)
        ]
Exemple #21
0
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)
Exemple #23
0
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
Exemple #24
0
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
Exemple #25
0
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)
Exemple #26
0
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
Exemple #30
0
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')
Exemple #31
0
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'
Exemple #33
0
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()
Exemple #34
0
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)
Exemple #35
0
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()
Exemple #36
0
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 = '{}&reg={}'.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&notams=0&firnot=0&maps=none'.format(url)
        
        # For show Simbiref.
        else:
            url = '{}&planformat=LIDO&units=KGS&navlog=1&etops=1&stepclimbs=0&tlr=0&notams=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)]
Exemple #39
0
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()
Exemple #41
0
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)