Beispiel #1
0
    def __init__(self): 
        '''Create a main controller for the game.
        Create the GUI. Run the game.'''
        
        self._set_cwd()
        self._game_files_setup()

        # create the UI
        app = Tk()
        #self.show_splash(app)
        
        self.game_frame = mock1.Game(app)
        self.game_frame.pack(fill=BOTH, expand=1)
        self.game_frame.grab_set()
        self.game_frame.focus_set()
        
        # fast access to models:
        #TODO models should be moved into this class
        self.my_grid = self.game_frame.my_grid._model
        self.enemy_grid = self.game_frame.their_grid._model
        
        # and the individual controllers
        self.my_controller = PlayerController(self.game_frame.my_grid_frame)
        self.their_controller = PlayerController(self.game_frame.their_grid_frame)
        
        # set initial variables
        self.new_game_callback()

        # add events
        self.create_hooks()
        
        # run the game
        app.mainloop()
def demo():
    from nltk import Nonterminal, CFG
    nonterminals = 'S VP NP PP P N Name V Det'
    (S, VP, NP, PP, P, N, Name, V, Det) = [Nonterminal(s)
                                           for s in nonterminals.split()]

    grammar = CFG.fromstring("""
    S -> NP VP
    PP -> P NP
    NP -> Det N
    NP -> NP PP
    VP -> V NP
    VP -> VP PP
    Det -> 'a'
    Det -> 'the'
    Det -> 'my'
    NP -> 'I'
    N -> 'dog'
    N -> 'man'
    N -> 'park'
    N -> 'statue'
    V -> 'saw'
    P -> 'in'
    P -> 'up'
    P -> 'over'
    P -> 'with'
    """)

    def cb(grammar): print(grammar)
    top = Tk()
    editor = CFGEditor(top, grammar, cb)
    Label(top, text='\nTesting CFG Editor\n').pack()
    Button(top, text='Quit', command=top.destroy).pack()
    top.mainloop()
def app():
    global root, sz, rz, rex0
    root = Tk()
    root.resizable(height=False,width=True)
    root.title(windowTitle)
    root.minsize(width=250,height=0)
    sz = FindZone("find",initialFind,initialText)
    sz.fld.bind("<Button-1>",launchRefresh)
    sz.fld.bind("<ButtonRelease-1>",launchRefresh)
    sz.fld.bind("<B1-Motion>",launchRefresh)
    sz.rexSel = re.compile("")
    rz = ReplaceZone("repl",initialRepl,"")
    rex0 = re.compile(r"(?<!\\)\\([0-9]+)")
    root.bind_all("<Key>",launchRefresh)
    launchRefresh(None)
    root.mainloop()
def demo():
    root = Tk()
    root.bind('<Control-q>', lambda e: root.destroy())

    table = Table(root, 'Word Synset Hypernym Hyponym'.split(),
                  column_weights=[0, 1, 1, 1],
                  reprfunc=(lambda i,j,s: '  %s' % s))
    table.pack(expand=True, fill='both')

    from nltk.corpus import wordnet
    from nltk.corpus import brown
    for word, pos in sorted(set(brown.tagged_words()[:500])):
        if pos[0] != 'N': continue
        word = word.lower()
        for synset in wordnet.synsets(word):
            try:
                hyper_def = synset.hypernyms()[0].definition()
            except:
                hyper_def = '*none*'
            try:
                hypo_def = synset.hypernyms()[0].definition()
            except:
                hypo_def = '*none*'
            table.append([word,
                          synset.definition(),
                          hyper_def,
                          hypo_def])

    table.columnconfig('Word', background='#afa')
    table.columnconfig('Synset', background='#efe')
    table.columnconfig('Hypernym', background='#fee')
    table.columnconfig('Hyponym', background='#ffe')
    for row in range(len(table)):
        for column in ('Hypernym', 'Hyponym'):
            if table[row, column] == '*none*':
                table.itemconfig(row, column, foreground='#666',
                                 selectforeground='#666')
    root.mainloop()
Beispiel #5
0
def demo():
    root = Tk()
    root.bind('<Control-q>', lambda e: root.destroy())

    table = Table(root,
                  'Word Synset Hypernym Hyponym'.split(),
                  column_weights=[0, 1, 1, 1],
                  reprfunc=(lambda i, j, s: '  %s' % s))
    table.pack(expand=True, fill='both')

    from nltk.corpus import wordnet
    from nltk.corpus import brown
    for word, pos in sorted(set(brown.tagged_words()[:500])):
        if pos[0] != 'N': continue
        word = word.lower()
        for synset in wordnet.synsets(word):
            try:
                hyper_def = synset.hypernyms()[0].definition()
            except:
                hyper_def = '*none*'
            try:
                hypo_def = synset.hypernyms()[0].definition()
            except:
                hypo_def = '*none*'
            table.append([word, synset.definition(), hyper_def, hypo_def])

    table.columnconfig('Word', background='#afa')
    table.columnconfig('Synset', background='#efe')
    table.columnconfig('Hypernym', background='#fee')
    table.columnconfig('Hyponym', background='#ffe')
    for row in range(len(table)):
        for column in ('Hypernym', 'Hyponym'):
            if table[row, column] == '*none*':
                table.itemconfig(row,
                                 column,
                                 foreground='#666',
                                 selectforeground='#666')
    root.mainloop()
Beispiel #6
0
def show_qr(path):
    import platform
    try:
        from six.moves.tkinter import Tk, Label
    except ImportError:
        raise SystemError('缺少Tkinter模块, 可使用sudo pip install Tkinter尝试安装')
    try:
        from PIL import ImageTk, Image
    except ImportError:
        raise SystemError('缺少PIL模块, 可使用sudo pip install PIL尝试安装')

    system = platform.system()
    if system == 'Darwin':  # 如果是Mac OS X
        img = Image.open(path)
        img.show()
    else:
        root = Tk()
        img = ImageTk.PhotoImage(
            Image.open(path)
        )
        panel = Label(root, image=img)
        panel.pack(side="bottom", fill="both", expand="yes")
        root.mainloop()
Beispiel #7
0
def demo():
    from nltk import Nonterminal, CFG

    nonterminals = 'S VP NP PP P N Name V Det'
    (S, VP, NP, PP, P, N, Name, V,
     Det) = [Nonterminal(s) for s in nonterminals.split()]

    grammar = CFG.fromstring("""
    S -> NP VP
    PP -> P NP
    NP -> Det N
    NP -> NP PP
    VP -> V NP
    VP -> VP PP
    Det -> 'a'
    Det -> 'the'
    Det -> 'my'
    NP -> 'I'
    N -> 'dog'
    N -> 'man'
    N -> 'park'
    N -> 'statue'
    V -> 'saw'
    P -> 'in'
    P -> 'up'
    P -> 'over'
    P -> 'with'
    """)

    def cb(grammar):
        print(grammar)

    top = Tk()
    editor = CFGEditor(top, grammar, cb)
    Label(top, text='\nTesting CFG Editor\n').pack()
    Button(top, text='Quit', command=top.destroy).pack()
    top.mainloop()
Beispiel #8
0
class TreeView(object):
    def __init__(self, *trees):
        from math import sqrt, ceil

        self._trees = trees

        self._top = Tk()
        self._top.title('NLTK')
        self._top.bind('<Control-x>', self.destroy)
        self._top.bind('<Control-q>', self.destroy)

        cf = self._cframe = CanvasFrame(self._top)
        self._top.bind('<Control-p>', self._cframe.print_to_file)

        # Size is variable.
        self._size = IntVar(self._top)
        self._size.set(12)
        bold = ('helvetica', -self._size.get(), 'bold')
        helv = ('helvetica', -self._size.get())

        # Lay the trees out in a square.
        self._width = int(ceil(sqrt(len(trees))))
        self._widgets = []
        for i in range(len(trees)):
            widget = TreeWidget(cf.canvas(), trees[i], node_font=bold,
                                leaf_color='#008040', node_color='#004080',
                                roof_color='#004040', roof_fill='white',
                                line_color='#004040', draggable=1,
                                leaf_font=helv)
            widget.bind_click_trees(widget.toggle_collapsed)
            self._widgets.append(widget)
            cf.add_widget(widget, 0, 0)

        self._layout()
        self._cframe.pack(expand=1, fill='both')
        self._init_menubar()

    def _layout(self):
        i = x = y = ymax = 0
        width = self._width
        for i in range(len(self._widgets)):
            widget = self._widgets[i]
            (oldx, oldy) = widget.bbox()[:2]
            if i % width == 0:
                y = ymax
                x = 0
            widget.move(x-oldx, y-oldy)
            x = widget.bbox()[2] + 10
            ymax = max(ymax, widget.bbox()[3] + 10)

    def _init_menubar(self):
        menubar = Menu(self._top)

        filemenu = Menu(menubar, tearoff=0)
        filemenu.add_command(label='Print to Postscript', underline=0,
                             command=self._cframe.print_to_file,
                             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)

        zoommenu = Menu(menubar, tearoff=0)
        zoommenu.add_radiobutton(label='Tiny', variable=self._size,
                                 underline=0, value=10, command=self.resize)
        zoommenu.add_radiobutton(label='Small', variable=self._size,
                                 underline=0, value=12, command=self.resize)
        zoommenu.add_radiobutton(label='Medium', variable=self._size,
                                 underline=0, value=14, command=self.resize)
        zoommenu.add_radiobutton(label='Large', variable=self._size,
                                 underline=0, value=28, command=self.resize)
        zoommenu.add_radiobutton(label='Huge', variable=self._size,
                                 underline=0, value=50, command=self.resize)
        menubar.add_cascade(label='Zoom', underline=0, menu=zoommenu)

        self._top.config(menu=menubar)

    def resize(self, *e):
        bold = ('helvetica', -self._size.get(), 'bold')
        helv = ('helvetica', -self._size.get())
        xspace = self._size.get()
        yspace = self._size.get()
        for widget in self._widgets:
            widget['node_font'] = bold
            widget['leaf_font'] = helv
            widget['xspace'] = xspace
            widget['yspace'] = yspace
            if self._size.get() < 20: widget['line_width'] = 1
            elif self._size.get() < 30: widget['line_width'] = 2
            else: widget['line_width'] = 3
        self._layout()

    def destroy(self, *e):
        if self._top is None: return
        self._top.destroy()
        self._top = None

    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)
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 = 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_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 six.moves.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 = (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 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 = 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())

    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 six.moves.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'
Beispiel #11
0
def main():
    root = Tk()

    frame = add_scrollbars(root, Frame, wheel=True)

    if False:

        def event_break(e):
            print("breaking %r" % e)
            return "break"

        frame.bind_all("<Button-4>", event_break, "+")

    lb = Label(frame, text="Label")
    lb.pack(fill=BOTH, side=TOP)

    cb = Combobox(frame, values=("1", "2", "3"))
    cb.pack(fill=BOTH, side=TOP)

    text = Text(frame)
    text.pack(fill=BOTH, expand=True, side=TOP)
    text.insert(END, "A\nMultiline\nMessage")

    for i in range(3, 100):
        text.insert(END, "line %d\n" % i)

    text2 = Text(frame)
    text2.pack(fill=BOTH, expand=True, side=TOP)

    for i in range(1, 200):
        text2.insert(END, "line %d\n" % i)

    bt1 = Button(frame, text="Bt#1")
    bt1.pack(side=LEFT)

    bt2 = Button(frame, text="Bt#2")
    bt2.pack(side=RIGHT)

    root.rowconfigure(2, weight=0)
    Label(root, text="Outer label").grid(row=2,
                                         column=0,
                                         columnspan=2,
                                         sticky="EW")

    if False:

        def event(e):
            print("event %r" % e)

        frame.bind("<Button-4>", event, "+")

    scrollable = set(["TCombobox", "Scrollbar", "Text"])

    def event_all(e):
        w = e.widget

        m = e.widget
        while m is not None:
            if m is frame:
                break
            m = m.master
        else:
            print("Outer widget")
            return

        cls = w.winfo_class()
        print("cls = " + cls)
        if cls in scrollable:
            return
        # scroll here

    bind_all_mouse_wheel(frame, event_all, "+")

    root.mainloop()
            self.ship_labels[short_name] = Label(
                self, 
                text=full_name.title(), 
                fg=self.SHIP_ALIVE,
                justify=LEFT,
            )
            self.ship_labels[short_name].grid(row=i, column=0, sticky=W, ipady=5)
            i += 1
            
    def redraw(self, model):
        '''Redraw the panel based on GridModel.
        <model> is an instance of GridModel.'''
        
        for name, ship in model.get_ships().items():
            if ship.is_sunk():
                self.set_sunk(name)
        
    def set_sunk(self, ship_name):
        '''Given the short name for the ship, set it to sunk.
        <ship_name> is the short name for the ship'''
        
        self.ship_labels[ship_name].config(fg=self.SHIP_SUNK)
    
if __name__ == "__main__":
    app = Tk()
    
    f = EnemyShipPanel(app)
    f.pack()
    
    app.mainloop()
Beispiel #13
0
class CFGDemo(object):
    def __init__(self, grammar, text):
        self._grammar = grammar
        self._text = text

        # Set up the main window.
        self._top = Tk()
        self._top.title('Context Free Grammar Demo')

        # Base font size
        self._size = IntVar(self._top)
        self._size.set(12)  # = medium

        # Set up the key bindings
        self._init_bindings(self._top)

        # Create the basic frames
        frame1 = Frame(self._top)
        frame1.pack(side='left', fill='y', expand=0)
        self._init_menubar(self._top)
        self._init_buttons(self._top)
        self._init_grammar(frame1)
        self._init_treelet(frame1)
        self._init_workspace(self._top)

    # //////////////////////////////////////////////////
    # Initialization
    # //////////////////////////////////////////////////

    def _init_bindings(self, top):
        top.bind('<Control-q>', self.destroy)

    def _init_menubar(self, parent):
        pass

    def _init_buttons(self, parent):
        pass

    def _init_grammar(self, parent):
        self._prodlist = ProductionList(parent, self._grammar, width=20)
        self._prodlist.pack(side='top', fill='both', expand=1)
        self._prodlist.focus()
        self._prodlist.add_callback('select', self._selectprod_cb)
        self._prodlist.add_callback('move', self._selectprod_cb)

    def _init_treelet(self, parent):
        self._treelet_canvas = Canvas(parent, background='white')
        self._treelet_canvas.pack(side='bottom', fill='x')
        self._treelet = None

    def _init_workspace(self, parent):
        self._workspace = CanvasFrame(parent, background='white')
        self._workspace.pack(side='right', fill='both', expand=1)
        self._tree = None
        self.reset_workspace()

    # //////////////////////////////////////////////////
    # Workspace
    # //////////////////////////////////////////////////

    def reset_workspace(self):
        c = self._workspace.canvas()
        fontsize = int(self._size.get())
        node_font = ('helvetica', -(fontsize + 4), 'bold')
        leaf_font = ('helvetica', -(fontsize + 2))

        # Remove the old tree
        if self._tree is not None:
            self._workspace.remove_widget(self._tree)

        # The root of the tree.
        start = self._grammar.start().symbol()
        rootnode = TextWidget(c, start, font=node_font, draggable=1)

        # The leaves of the tree.
        leaves = []
        for word in self._text:
            leaves.append(TextWidget(c, word, font=leaf_font, draggable=1))

        # Put it all together into one tree
        self._tree = TreeSegmentWidget(c, rootnode, leaves, color='white')

        # Add it to the workspace.
        self._workspace.add_widget(self._tree)

        # Move the leaves to the bottom of the workspace.
        for leaf in leaves:
            leaf.move(0, 100)

        # self._nodes = {start:1}
        # self._leaves = dict([(l,1) for l in leaves])

    def workspace_markprod(self, production):
        pass

    def _markproduction(self, prod, tree=None):
        if tree is None:
            tree = self._tree
        for i in range(len(tree.subtrees()) - len(prod.rhs())):
            if tree['color', i] == 'white':
                self._markproduction  # FIXME: Is this necessary at all?

            for j, node in enumerate(prod.rhs()):
                widget = tree.subtrees()[i + j]
                if (
                    isinstance(node, Nonterminal)
                    and isinstance(widget, TreeSegmentWidget)
                    and node.symbol == widget.label().text()
                ):
                    pass  # matching nonterminal
                elif (
                    isinstance(node, string_types)
                    and isinstance(widget, TextWidget)
                    and node == widget.text()
                ):
                    pass  # matching nonterminal
                else:
                    break
            else:
                # Everything matched!
                print('MATCH AT', i)

    # //////////////////////////////////////////////////
    # Grammar
    # //////////////////////////////////////////////////

    def _selectprod_cb(self, production):
        canvas = self._treelet_canvas

        self._prodlist.highlight(production)
        if self._treelet is not None:
            self._treelet.destroy()

        # Convert the production to a tree.
        rhs = production.rhs()
        for (i, elt) in enumerate(rhs):
            if isinstance(elt, Nonterminal):
                elt = Tree(elt)
        tree = Tree(production.lhs().symbol(), *rhs)

        # Draw the tree in the treelet area.
        fontsize = int(self._size.get())
        node_font = ('helvetica', -(fontsize + 4), 'bold')
        leaf_font = ('helvetica', -(fontsize + 2))
        self._treelet = tree_to_treesegment(
            canvas, tree, node_font=node_font, leaf_font=leaf_font
        )
        self._treelet['draggable'] = 1

        # Center the treelet.
        (x1, y1, x2, y2) = self._treelet.bbox()
        w, h = int(canvas['width']), int(canvas['height'])
        self._treelet.move((w - x1 - x2) / 2, (h - y1 - y2) / 2)

        # Mark the places where we can add it to the workspace.
        self._markproduction(production)

    def destroy(self, *args):
        self._top.destroy()

    def mainloop(self, *args, **kwargs):
        self._top.mainloop(*args, **kwargs)
Beispiel #14
0
class TreeView(object):
    def __init__(self, *trees):
        from math import sqrt, ceil

        self._trees = trees

        self._top = Tk()
        self._top.title('NLTK')
        self._top.bind('<Control-x>', self.destroy)
        self._top.bind('<Control-q>', self.destroy)

        cf = self._cframe = CanvasFrame(self._top)
        self._top.bind('<Control-p>', self._cframe.print_to_file)

        # Size is variable.
        self._size = IntVar(self._top)
        self._size.set(12)
        bold = ('helvetica', -self._size.get(), 'bold')
        helv = ('helvetica', -self._size.get())

        # Lay the trees out in a square.
        self._width = int(ceil(sqrt(len(trees))))
        self._widgets = []
        for i in range(len(trees)):
            widget = TreeWidget(
                cf.canvas(),
                trees[i],
                node_font=bold,
                leaf_color='#008040',
                node_color='#004080',
                roof_color='#004040',
                roof_fill='white',
                line_color='#004040',
                draggable=1,
                leaf_font=helv,
            )
            widget.bind_click_trees(widget.toggle_collapsed)
            self._widgets.append(widget)
            cf.add_widget(widget, 0, 0)

        self._layout()
        self._cframe.pack(expand=1, fill='both')
        self._init_menubar()

    def _layout(self):
        i = x = y = ymax = 0
        width = self._width
        for i in range(len(self._widgets)):
            widget = self._widgets[i]
            (oldx, oldy) = widget.bbox()[:2]
            if i % width == 0:
                y = ymax
                x = 0
            widget.move(x - oldx, y - oldy)
            x = widget.bbox()[2] + 10
            ymax = max(ymax, widget.bbox()[3] + 10)

    def _init_menubar(self):
        menubar = Menu(self._top)

        filemenu = Menu(menubar, tearoff=0)
        filemenu.add_command(
            label='Print to Postscript',
            underline=0,
            command=self._cframe.print_to_file,
            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)

        zoommenu = Menu(menubar, tearoff=0)
        zoommenu.add_radiobutton(
            label='Tiny',
            variable=self._size,
            underline=0,
            value=10,
            command=self.resize,
        )
        zoommenu.add_radiobutton(
            label='Small',
            variable=self._size,
            underline=0,
            value=12,
            command=self.resize,
        )
        zoommenu.add_radiobutton(
            label='Medium',
            variable=self._size,
            underline=0,
            value=14,
            command=self.resize,
        )
        zoommenu.add_radiobutton(
            label='Large',
            variable=self._size,
            underline=0,
            value=28,
            command=self.resize,
        )
        zoommenu.add_radiobutton(
            label='Huge',
            variable=self._size,
            underline=0,
            value=50,
            command=self.resize,
        )
        menubar.add_cascade(label='Zoom', underline=0, menu=zoommenu)

        self._top.config(menu=menubar)

    def resize(self, *e):
        bold = ('helvetica', -self._size.get(), 'bold')
        helv = ('helvetica', -self._size.get())
        xspace = self._size.get()
        yspace = self._size.get()
        for widget in self._widgets:
            widget['node_font'] = bold
            widget['leaf_font'] = helv
            widget['xspace'] = xspace
            widget['yspace'] = yspace
            if self._size.get() < 20:
                widget['line_width'] = 1
            elif self._size.get() < 30:
                widget['line_width'] = 2
            else:
                widget['line_width'] = 3
        self._layout()

    def destroy(self, *e):
        if self._top is None:
            return
        self._top.destroy()
        self._top = None

    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)
Beispiel #15
0
class CFGDemo(object):
    def __init__(self, grammar, text):
        self._grammar = grammar
        self._text = text

        # Set up the main window.
        self._top = Tk()
        self._top.title('Context Free Grammar Demo')

        # Base font size
        self._size = IntVar(self._top)
        self._size.set(12) # = medium

        # Set up the key bindings
        self._init_bindings(self._top)

        # Create the basic frames
        frame1 = Frame(self._top)
        frame1.pack(side='left', fill='y', expand=0)
        self._init_menubar(self._top)
        self._init_buttons(self._top)
        self._init_grammar(frame1)
        self._init_treelet(frame1)
        self._init_workspace(self._top)

    #//////////////////////////////////////////////////
    # Initialization
    #//////////////////////////////////////////////////

    def _init_bindings(self, top):
        top.bind('<Control-q>', self.destroy)

    def _init_menubar(self, parent): pass

    def _init_buttons(self, parent): pass

    def _init_grammar(self, parent):
        self._prodlist = ProductionList(parent, self._grammar, width=20)
        self._prodlist.pack(side='top', fill='both', expand=1)
        self._prodlist.focus()
        self._prodlist.add_callback('select', self._selectprod_cb)
        self._prodlist.add_callback('move', self._selectprod_cb)

    def _init_treelet(self, parent):
        self._treelet_canvas = Canvas(parent, background='white')
        self._treelet_canvas.pack(side='bottom', fill='x')
        self._treelet = None

    def _init_workspace(self, parent):
        self._workspace = CanvasFrame(parent, background='white')
        self._workspace.pack(side='right', fill='both', expand=1)
        self._tree = None
        self.reset_workspace()

    #//////////////////////////////////////////////////
    # Workspace
    #//////////////////////////////////////////////////

    def reset_workspace(self):
        c = self._workspace.canvas()
        fontsize = int(self._size.get())
        node_font = ('helvetica', -(fontsize+4), 'bold')
        leaf_font = ('helvetica', -(fontsize+2))

        # Remove the old tree
        if self._tree is not None:
            self._workspace.remove_widget(self._tree)

        # The root of the tree.
        start = self._grammar.start().symbol()
        rootnode = TextWidget(c, start, font=node_font, draggable=1)

        # The leaves of the tree.
        leaves = []
        for word in self._text:
            leaves.append(TextWidget(c, word, font=leaf_font, draggable=1))

        # Put it all together into one tree
        self._tree = TreeSegmentWidget(c, rootnode, leaves,
                                       color='white')

        # Add it to the workspace.
        self._workspace.add_widget(self._tree)

        # Move the leaves to the bottom of the workspace.
        for leaf in leaves: leaf.move(0,100)

        #self._nodes = {start:1}
        #self._leaves = dict([(l,1) for l in leaves])

    def workspace_markprod(self, production):
        pass

    def _markproduction(self, prod, tree=None):
        if tree is None: tree = self._tree
        for i in range(len(tree.subtrees())-len(prod.rhs())):
            if tree['color', i] == 'white':
                self._markproduction

            for j, node in enumerate(prod.rhs()):
                widget = tree.subtrees()[i+j]
                if (isinstance(node, Nonterminal) and
                    isinstance(widget, TreeSegmentWidget) and
                    node.symbol == widget.label().text()):
                    pass # matching nonterminal
                elif (isinstance(node, string_types) and
                      isinstance(widget, TextWidget) and
                      node == widget.text()):
                    pass # matching nonterminal
                else: break
            else:
                # Everything matched!
                print('MATCH AT', i)

    #//////////////////////////////////////////////////
    # Grammar
    #//////////////////////////////////////////////////

    def _selectprod_cb(self, production):
        canvas = self._treelet_canvas

        self._prodlist.highlight(production)
        if self._treelet is not None: self._treelet.destroy()

        # Convert the production to a tree.
        rhs = production.rhs()
        for (i, elt) in enumerate(rhs):
            if isinstance(elt, Nonterminal): elt = Tree(elt)
        tree = Tree(production.lhs().symbol(), *rhs)

        # Draw the tree in the treelet area.
        fontsize = int(self._size.get())
        node_font = ('helvetica', -(fontsize+4), 'bold')
        leaf_font = ('helvetica', -(fontsize+2))
        self._treelet = tree_to_treesegment(canvas, tree,
                                            node_font=node_font,
                                            leaf_font=leaf_font)
        self._treelet['draggable'] = 1

        # Center the treelet.
        (x1, y1, x2, y2) = self._treelet.bbox()
        w, h = int(canvas['width']), int(canvas['height'])
        self._treelet.move((w-x1-x2)/2, (h-y1-y2)/2)

        # Mark the places where we can add it to the workspace.
        self._markproduction(production)

    def destroy(self, *args):
        self._top.destroy()

    def mainloop(self, *args, **kwargs):
        self._top.mainloop(*args, **kwargs)
Beispiel #16
0
class CollocationsView:
    _BACKGROUND_COLOUR = "#FFF"  # white

    def __init__(self):
        self.queue = q.Queue()
        self.model = CollocationsModel(self.queue)
        self.top = Tk()
        self._init_top(self.top)
        self._init_menubar()
        self._init_widgets(self.top)
        self.load_corpus(self.model.DEFAULT_CORPUS)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def _init_top(self, top):
        top.geometry("550x650+50+50")
        top.title("NLTK Collocations List")
        top.bind("<Control-q>", self.destroy)
        top.protocol("WM_DELETE_WINDOW", self.destroy)
        top.minsize(550, 650)

    def _init_widgets(self, parent):
        self.main_frame = Frame(
            parent,
            dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1))
        self._init_corpus_select(self.main_frame)
        self._init_results_box(self.main_frame)
        self._init_paging(self.main_frame)
        self._init_status(self.main_frame)
        self.main_frame.pack(fill="both", expand=True)

    def _init_corpus_select(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.var = StringVar(innerframe)
        self.var.set(self.model.DEFAULT_CORPUS)
        Label(
            innerframe,
            justify=LEFT,
            text=" Corpus: ",
            background=self._BACKGROUND_COLOUR,
            padx=2,
            pady=1,
            border=0,
        ).pack(side="left")

        other_corpora = list(self.model.CORPORA.keys()).remove(
            self.model.DEFAULT_CORPUS)
        om = OptionMenu(innerframe,
                        self.var,
                        self.model.DEFAULT_CORPUS,
                        command=self.corpus_selected,
                        *self.model.non_default_corpora())
        om["borderwidth"] = 0
        om["highlightthickness"] = 1
        om.pack(side="left")
        innerframe.pack(side="top", fill="x", anchor="n")

    def _init_status(self, parent):
        self.status = Label(
            parent,
            justify=LEFT,
            relief=SUNKEN,
            background=self._BACKGROUND_COLOUR,
            border=0,
            padx=1,
            pady=0,
        )
        self.status.pack(side="top", anchor="sw")

    def _init_menubar(self):
        self._result_size = IntVar(self.top)
        menubar = Menu(self.top)

        filemenu = Menu(menubar, tearoff=0, borderwidth=0)
        filemenu.add_command(label="Exit",
                             underline=1,
                             command=self.destroy,
                             accelerator="Ctrl-q")
        menubar.add_cascade(label="File", underline=0, menu=filemenu)

        editmenu = Menu(menubar, tearoff=0)
        rescntmenu = Menu(editmenu, tearoff=0)
        rescntmenu.add_radiobutton(
            label="20",
            variable=self._result_size,
            underline=0,
            value=20,
            command=self.set_result_size,
        )
        rescntmenu.add_radiobutton(
            label="50",
            variable=self._result_size,
            underline=0,
            value=50,
            command=self.set_result_size,
        )
        rescntmenu.add_radiobutton(
            label="100",
            variable=self._result_size,
            underline=0,
            value=100,
            command=self.set_result_size,
        )
        rescntmenu.invoke(1)
        editmenu.add_cascade(label="Result Count",
                             underline=0,
                             menu=rescntmenu)

        menubar.add_cascade(label="Edit", underline=0, menu=editmenu)
        self.top.config(menu=menubar)

    def set_result_size(self, **kwargs):
        self.model.result_count = self._result_size.get()

    def _init_results_box(self, parent):
        innerframe = Frame(parent)
        i1 = Frame(innerframe)
        i2 = Frame(innerframe)
        vscrollbar = Scrollbar(i1, borderwidth=1)
        hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz")
        self.results_box = Text(
            i1,
            font=Font(family="courier", size="16"),
            state="disabled",
            borderwidth=1,
            yscrollcommand=vscrollbar.set,
            xscrollcommand=hscrollbar.set,
            wrap="none",
            width="40",
            height="20",
            exportselection=1,
        )
        self.results_box.pack(side="left", fill="both", expand=True)
        vscrollbar.pack(side="left", fill="y", anchor="e")
        vscrollbar.config(command=self.results_box.yview)
        hscrollbar.pack(side="left", fill="x", expand=True, anchor="w")
        hscrollbar.config(command=self.results_box.xview)
        # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!!
        Label(i2, text="   ",
              background=self._BACKGROUND_COLOUR).pack(side="left", anchor="e")
        i1.pack(side="top", fill="both", expand=True, anchor="n")
        i2.pack(side="bottom", fill="x", anchor="s")
        innerframe.pack(side="top", fill="both", expand=True)

    def _init_paging(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.prev = prev = Button(
            innerframe,
            text="Previous",
            command=self.previous,
            width="10",
            borderwidth=1,
            highlightthickness=1,
            state="disabled",
        )
        prev.pack(side="left", anchor="center")
        self.next = next = Button(
            innerframe,
            text="Next",
            command=self.__next__,
            width="10",
            borderwidth=1,
            highlightthickness=1,
            state="disabled",
        )
        next.pack(side="right", anchor="center")
        innerframe.pack(side="top", fill="y")
        self.reset_current_page()

    def reset_current_page(self):
        self.current_page = -1

    def _poll(self):
        try:
            event = self.queue.get(block=False)
        except q.Empty:
            pass
        else:
            if event == CORPUS_LOADED_EVENT:
                self.handle_corpus_loaded(event)
            elif event == ERROR_LOADING_CORPUS_EVENT:
                self.handle_error_loading_corpus(event)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def handle_error_loading_corpus(self, event):
        self.status["text"] = "Error in loading " + self.var.get()
        self.unfreeze_editable()
        self.clear_results_box()
        self.freeze_editable()
        self.reset_current_page()

    def handle_corpus_loaded(self, event):
        self.status["text"] = self.var.get() + " is loaded"
        self.unfreeze_editable()
        self.clear_results_box()
        self.reset_current_page()
        # self.next()
        collocations = self.model.next(self.current_page + 1)
        self.write_results(collocations)
        self.current_page += 1

    def corpus_selected(self, *args):
        new_selection = self.var.get()
        self.load_corpus(new_selection)

    def previous(self):
        self.freeze_editable()
        collocations = self.model.prev(self.current_page - 1)
        self.current_page = self.current_page - 1
        self.clear_results_box()
        self.write_results(collocations)
        self.unfreeze_editable()

    def __next__(self):
        self.freeze_editable()
        collocations = self.model.next(self.current_page + 1)
        self.clear_results_box()
        self.write_results(collocations)
        self.current_page += 1
        self.unfreeze_editable()

    def load_corpus(self, selection):
        if self.model.selected_corpus != selection:
            self.status["text"] = "Loading " + selection + "..."
            self.freeze_editable()
            self.model.load_corpus(selection)

    def freeze_editable(self):
        self.prev["state"] = "disabled"
        self.next["state"] = "disabled"

    def clear_results_box(self):
        self.results_box["state"] = "normal"
        self.results_box.delete("1.0", END)
        self.results_box["state"] = "disabled"

    def fire_event(self, event):
        # Firing an event so that rendering of widgets happen in the mainloop thread
        self.top.event_generate(event, when="tail")

    def destroy(self, *e):
        if self.top is None:
            return
        self.top.after_cancel(self.after)
        self.top.destroy()
        self.top = None

    def mainloop(self, *args, **kwargs):
        if in_idle():
            return
        self.top.mainloop(*args, **kwargs)

    def unfreeze_editable(self):
        self.set_paging_button_states()

    def set_paging_button_states(self):
        if self.current_page == -1 or self.current_page == 0:
            self.prev["state"] = "disabled"
        else:
            self.prev["state"] = "normal"
        if self.model.is_last_page(self.current_page):
            self.next["state"] = "disabled"
        else:
            self.next["state"] = "normal"

    def write_results(self, results):
        self.results_box["state"] = "normal"
        row = 1
        for each in results:
            self.results_box.insert(
                str(row) + ".0", each[0] + " " + each[1] + "\n")
            row += 1
        self.results_box["state"] = "disabled"
Beispiel #17
0
class CollocationsView:
    _BACKGROUND_COLOUR = '#FFF'  # white

    def __init__(self):
        self.queue = q.Queue()
        self.model = CollocationsModel(self.queue)
        self.top = Tk()
        self._init_top(self.top)
        self._init_menubar()
        self._init_widgets(self.top)
        self.load_corpus(self.model.DEFAULT_CORPUS)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def _init_top(self, top):
        top.geometry('550x650+50+50')
        top.title('NLTK Collocations List')
        top.bind('<Control-q>', self.destroy)
        top.protocol('WM_DELETE_WINDOW', self.destroy)
        top.minsize(550, 650)

    def _init_widgets(self, parent):
        self.main_frame = Frame(
            parent,
            dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1))
        self._init_corpus_select(self.main_frame)
        self._init_results_box(self.main_frame)
        self._init_paging(self.main_frame)
        self._init_status(self.main_frame)
        self.main_frame.pack(fill='both', expand=True)

    def _init_corpus_select(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.var = StringVar(innerframe)
        self.var.set(self.model.DEFAULT_CORPUS)
        Label(innerframe,
              justify=LEFT,
              text=' Corpus: ',
              background=self._BACKGROUND_COLOUR,
              padx=2,
              pady=1,
              border=0).pack(side='left')

        other_corpora = list(self.model.CORPORA.keys()).remove(
            self.model.DEFAULT_CORPUS)
        om = OptionMenu(innerframe,
                        self.var,
                        self.model.DEFAULT_CORPUS,
                        command=self.corpus_selected,
                        *self.model.non_default_corpora())
        om['borderwidth'] = 0
        om['highlightthickness'] = 1
        om.pack(side='left')
        innerframe.pack(side='top', fill='x', anchor='n')

    def _init_status(self, parent):
        self.status = Label(parent,
                            justify=LEFT,
                            relief=SUNKEN,
                            background=self._BACKGROUND_COLOUR,
                            border=0,
                            padx=1,
                            pady=0)
        self.status.pack(side='top', anchor='sw')

    def _init_menubar(self):
        self._result_size = IntVar(self.top)
        menubar = Menu(self.top)

        filemenu = Menu(menubar, tearoff=0, borderwidth=0)
        filemenu.add_command(label='Exit',
                             underline=1,
                             command=self.destroy,
                             accelerator='Ctrl-q')
        menubar.add_cascade(label='File', underline=0, menu=filemenu)

        editmenu = Menu(menubar, tearoff=0)
        rescntmenu = Menu(editmenu, tearoff=0)
        rescntmenu.add_radiobutton(label='20',
                                   variable=self._result_size,
                                   underline=0,
                                   value=20,
                                   command=self.set_result_size)
        rescntmenu.add_radiobutton(label='50',
                                   variable=self._result_size,
                                   underline=0,
                                   value=50,
                                   command=self.set_result_size)
        rescntmenu.add_radiobutton(label='100',
                                   variable=self._result_size,
                                   underline=0,
                                   value=100,
                                   command=self.set_result_size)
        rescntmenu.invoke(1)
        editmenu.add_cascade(label='Result Count',
                             underline=0,
                             menu=rescntmenu)

        menubar.add_cascade(label='Edit', underline=0, menu=editmenu)
        self.top.config(menu=menubar)

    def set_result_size(self, **kwargs):
        self.model.result_count = self._result_size.get()

    def _init_results_box(self, parent):
        innerframe = Frame(parent)
        i1 = Frame(innerframe)
        i2 = Frame(innerframe)
        vscrollbar = Scrollbar(i1, borderwidth=1)
        hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz')
        self.results_box = Text(i1,
                                font=Font(family='courier', size='16'),
                                state='disabled',
                                borderwidth=1,
                                yscrollcommand=vscrollbar.set,
                                xscrollcommand=hscrollbar.set,
                                wrap='none',
                                width='40',
                                height='20',
                                exportselection=1)
        self.results_box.pack(side='left', fill='both', expand=True)
        vscrollbar.pack(side='left', fill='y', anchor='e')
        vscrollbar.config(command=self.results_box.yview)
        hscrollbar.pack(side='left', fill='x', expand=True, anchor='w')
        hscrollbar.config(command=self.results_box.xview)
        # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!!
        Label(i2, text='   ',
              background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e')
        i1.pack(side='top', fill='both', expand=True, anchor='n')
        i2.pack(side='bottom', fill='x', anchor='s')
        innerframe.pack(side='top', fill='both', expand=True)

    def _init_paging(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.prev = prev = Button(innerframe,
                                  text='Previous',
                                  command=self.previous,
                                  width='10',
                                  borderwidth=1,
                                  highlightthickness=1,
                                  state='disabled')
        prev.pack(side='left', anchor='center')
        self.next = next = Button(innerframe,
                                  text='Next',
                                  command=self.__next__,
                                  width='10',
                                  borderwidth=1,
                                  highlightthickness=1,
                                  state='disabled')
        next.pack(side='right', anchor='center')
        innerframe.pack(side='top', fill='y')
        self.reset_current_page()

    def reset_current_page(self):
        self.current_page = -1

    def _poll(self):
        try:
            event = self.queue.get(block=False)
        except q.Empty:
            pass
        else:
            if event == CORPUS_LOADED_EVENT:
                self.handle_corpus_loaded(event)
            elif event == ERROR_LOADING_CORPUS_EVENT:
                self.handle_error_loading_corpus(event)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def handle_error_loading_corpus(self, event):
        self.status['text'] = 'Error in loading ' + self.var.get()
        self.unfreeze_editable()
        self.clear_results_box()
        self.freeze_editable()
        self.reset_current_page()

    def handle_corpus_loaded(self, event):
        self.status['text'] = self.var.get() + ' is loaded'
        self.unfreeze_editable()
        self.clear_results_box()
        self.reset_current_page()
        # self.next()
        collocations = self.model.next(self.current_page + 1)
        self.write_results(collocations)
        self.current_page += 1

    def corpus_selected(self, *args):
        new_selection = self.var.get()
        self.load_corpus(new_selection)

    def previous(self):
        self.freeze_editable()
        collocations = self.model.prev(self.current_page - 1)
        self.current_page = self.current_page - 1
        self.clear_results_box()
        self.write_results(collocations)
        self.unfreeze_editable()

    def __next__(self):
        self.freeze_editable()
        collocations = self.model.next(self.current_page + 1)
        self.clear_results_box()
        self.write_results(collocations)
        self.current_page += 1
        self.unfreeze_editable()

    def load_corpus(self, selection):
        if self.model.selected_corpus != selection:
            self.status['text'] = 'Loading ' + selection + '...'
            self.freeze_editable()
            self.model.load_corpus(selection)

    def freeze_editable(self):
        self.prev['state'] = 'disabled'
        self.next['state'] = 'disabled'

    def clear_results_box(self):
        self.results_box['state'] = 'normal'
        self.results_box.delete("1.0", END)
        self.results_box['state'] = 'disabled'

    def fire_event(self, event):
        # Firing an event so that rendering of widgets happen in the mainloop thread
        self.top.event_generate(event, when='tail')

    def destroy(self, *e):
        if self.top is None:
            return
        self.top.after_cancel(self.after)
        self.top.destroy()
        self.top = None

    def mainloop(self, *args, **kwargs):
        if in_idle():
            return
        self.top.mainloop(*args, **kwargs)

    def unfreeze_editable(self):
        self.set_paging_button_states()

    def set_paging_button_states(self):
        if self.current_page == -1 or self.current_page == 0:
            self.prev['state'] = 'disabled'
        else:
            self.prev['state'] = 'normal'
        if self.model.is_last_page(self.current_page):
            self.next['state'] = 'disabled'
        else:
            self.next['state'] = 'normal'

    def write_results(self, results):
        self.results_box['state'] = 'normal'
        row = 1
        for each in results:
            self.results_box.insert(
                str(row) + '.0', each[0] + " " + each[1] + "\n")
            row += 1
        self.results_box['state'] = 'disabled'
Beispiel #18
0
from six.moves.tkinter import (Tk, RAISED, Canvas, BOTH, HORIZONTAL, VERTICAL)
from widgets import (AutoPanedWindow)

if __name__ == "__main__":
    r = Tk()
    r.title("AutoPanedWindow example")

    a = AutoPanedWindow(r, sashrelief=RAISED, orient=HORIZONTAL)
    a.pack(fill=BOTH, expand=True)

    a.add(Canvas(a, bg="red"), sticky="NESW")

    a1 = AutoPanedWindow(a, sashrelief=RAISED, orient=VERTICAL)
    a.add(a1, sticky="NESW")

    blue = Canvas(a1, bg="blue")
    a1.add(blue, sticky="NESW")

    green = Canvas(a1, bg="green")
    a1.add(green, sticky="NESW")

    a1.add(Canvas(a1, bg="brown"), after=blue, sticky="NESW")

    yellow = Canvas(a, bg="yellow")
    a.add(yellow, sticky="NESW")

    a.add(Canvas(a, bg="pink"), before=yellow, sticky="NESW")

    r.geometry("800x600")
    r.mainloop()
Beispiel #19
0
class LoginDialog(object):

    def __init__(self, host):
        self.__interna_init()
        self.__host = host
        self.__create_content()

    def __interna_init(self):
        self.__rootFrame = None
        self.__top = None
        self.__usrEntry = None
        self.__pwdEntry = None
        self.__accepted = False
        self.__host = None
        self.__usr = None
        self.__pwd = None

    def __cancel_action(self):
        self.__accepted = False
        self.__rootFrame.destroy()

    def __login_action(self):
        self.__accepted = True
        self.__usr = self.__usrEntry.get()
        self.__pwd = self.__pwdEntry.get()
        self.__rootFrame.destroy()

    def __enter_action(self, event):
        self.__login_action()

    def __create_content(self):
        self.__rootFrame = TKTk()
        self.__rootFrame.withdraw()

        self.__top = TKToplevel(self.__rootFrame)
        self.__top.title("Login")
        self.__top.protocol("WM_DELETE_WINDOW", self.__rootFrame.destroy)

        self.__top.bind('<Return>', self.__enter_action)

        self.__top.update_idletasks()
        width = self.__top.winfo_width()
        height = self.__top.winfo_height()
        x = (self.__top.winfo_screenwidth() // 2) - (width // 2)
        y = (self.__top.winfo_screenheight() // 2) - (height // 2)

        self.__top.geometry("+%d+%d" % (x, y))

        row = 0
        expLabel = TKLabel(self.__top, text='Login to host:')
        expLabel.grid(row=row, column=0, columnspan=4, padx=5, pady=2)

        row = row+1
        urlLabel = TKLabel(self.__top, text=self.__host)
        urlLabel.grid(row=row, column=0, columnspan=4, padx=5, pady=2)

        row = row+1
        usrLabel = TKLabel(self.__top, text='User')
        usrLabel.grid(row=row, column=0, columnspan=2, padx=20, pady=5)
        self.__usrEntry = TKEntry(self.__top, width=20)
        self.__usrEntry.grid(row=row, column=2, columnspan=2, padx=5, pady=5)

        row = row+1
        pwdLabel = TKLabel(self.__top, text='Password')
        pwdLabel.grid(row=row, column=0, columnspan=2, padx=20, pady=5)
        self.__pwdEntry = TKEntry(self.__top, width=20, show="*")
        self.__pwdEntry.grid(row=row, column=2, columnspan=2, padx=5, pady=5)

        row = row+1
        cancelButton = TKButton(self.__top, text='Cancel', command=self.__cancel_action)
        cancelButton.grid(row=row, column=1, padx=5, pady=5)
        loginButton = TKButton(self.__top, text='Login', command=self.__login_action)
        loginButton.grid(row=row, column=2, padx=5, pady=5)

    def show_login(self):
        self.__usrEntry.focus_set()
        self.__rootFrame.mainloop()

    def is_accepted(self):
        return self.__accepted

    def get_user(self):
        return self.__usr

    def get_password(self):
        return self.__pwd
Beispiel #20
0
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 = 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())

    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 six.moves.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 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 = 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_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 six.moves.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 = ((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()
Beispiel #22
0
class ConcordanceSearchView(object):
    _BACKGROUND_COLOUR = "#FFF"  # white

    # Colour of highlighted results
    _HIGHLIGHT_WORD_COLOUR = "#F00"  # red
    _HIGHLIGHT_WORD_TAG = "HL_WRD_TAG"

    _HIGHLIGHT_LABEL_COLOUR = "#C0C0C0"  # dark grey
    _HIGHLIGHT_LABEL_TAG = "HL_LBL_TAG"

    # Percentage of text left of the scrollbar position
    _FRACTION_LEFT_TEXT = 0.30

    def __init__(self):
        self.queue = q.Queue()
        self.model = ConcordanceSearchModel(self.queue)
        self.top = Tk()
        self._init_top(self.top)
        self._init_menubar()
        self._init_widgets(self.top)
        self.load_corpus(self.model.DEFAULT_CORPUS)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def _init_top(self, top):
        top.geometry("950x680+50+50")
        top.title("NLTK Concordance Search")
        top.bind("<Control-q>", self.destroy)
        top.protocol("WM_DELETE_WINDOW", self.destroy)
        top.minsize(950, 680)

    def _init_widgets(self, parent):
        self.main_frame = Frame(
            parent,
            dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1))
        self._init_corpus_select(self.main_frame)
        self._init_query_box(self.main_frame)
        self._init_results_box(self.main_frame)
        self._init_paging(self.main_frame)
        self._init_status(self.main_frame)
        self.main_frame.pack(fill="both", expand=True)

    def _init_menubar(self):
        self._result_size = IntVar(self.top)
        self._cntx_bf_len = IntVar(self.top)
        self._cntx_af_len = IntVar(self.top)
        menubar = Menu(self.top)

        filemenu = Menu(menubar, tearoff=0, borderwidth=0)
        filemenu.add_command(label="Exit",
                             underline=1,
                             command=self.destroy,
                             accelerator="Ctrl-q")
        menubar.add_cascade(label="File", underline=0, menu=filemenu)

        editmenu = Menu(menubar, tearoff=0)
        rescntmenu = Menu(editmenu, tearoff=0)
        rescntmenu.add_radiobutton(
            label="20",
            variable=self._result_size,
            underline=0,
            value=20,
            command=self.set_result_size,
        )
        rescntmenu.add_radiobutton(
            label="50",
            variable=self._result_size,
            underline=0,
            value=50,
            command=self.set_result_size,
        )
        rescntmenu.add_radiobutton(
            label="100",
            variable=self._result_size,
            underline=0,
            value=100,
            command=self.set_result_size,
        )
        rescntmenu.invoke(1)
        editmenu.add_cascade(label="Result Count",
                             underline=0,
                             menu=rescntmenu)

        cntxmenu = Menu(editmenu, tearoff=0)
        cntxbfmenu = Menu(cntxmenu, tearoff=0)
        cntxbfmenu.add_radiobutton(
            label="60 characters",
            variable=self._cntx_bf_len,
            underline=0,
            value=60,
            command=self.set_cntx_bf_len,
        )
        cntxbfmenu.add_radiobutton(
            label="80 characters",
            variable=self._cntx_bf_len,
            underline=0,
            value=80,
            command=self.set_cntx_bf_len,
        )
        cntxbfmenu.add_radiobutton(
            label="100 characters",
            variable=self._cntx_bf_len,
            underline=0,
            value=100,
            command=self.set_cntx_bf_len,
        )
        cntxbfmenu.invoke(1)
        cntxmenu.add_cascade(label="Before", underline=0, menu=cntxbfmenu)

        cntxafmenu = Menu(cntxmenu, tearoff=0)
        cntxafmenu.add_radiobutton(
            label="70 characters",
            variable=self._cntx_af_len,
            underline=0,
            value=70,
            command=self.set_cntx_af_len,
        )
        cntxafmenu.add_radiobutton(
            label="90 characters",
            variable=self._cntx_af_len,
            underline=0,
            value=90,
            command=self.set_cntx_af_len,
        )
        cntxafmenu.add_radiobutton(
            label="110 characters",
            variable=self._cntx_af_len,
            underline=0,
            value=110,
            command=self.set_cntx_af_len,
        )
        cntxafmenu.invoke(1)
        cntxmenu.add_cascade(label="After", underline=0, menu=cntxafmenu)

        editmenu.add_cascade(label="Context", underline=0, menu=cntxmenu)

        menubar.add_cascade(label="Edit", underline=0, menu=editmenu)

        self.top.config(menu=menubar)

    def set_result_size(self, **kwargs):
        self.model.result_count = self._result_size.get()

    def set_cntx_af_len(self, **kwargs):
        self._char_after = self._cntx_af_len.get()

    def set_cntx_bf_len(self, **kwargs):
        self._char_before = self._cntx_bf_len.get()

    def _init_corpus_select(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.var = StringVar(innerframe)
        self.var.set(self.model.DEFAULT_CORPUS)
        Label(
            innerframe,
            justify=LEFT,
            text=" Corpus: ",
            background=self._BACKGROUND_COLOUR,
            padx=2,
            pady=1,
            border=0,
        ).pack(side="left")

        other_corpora = list(self.model.CORPORA.keys()).remove(
            self.model.DEFAULT_CORPUS)
        om = OptionMenu(innerframe,
                        self.var,
                        self.model.DEFAULT_CORPUS,
                        command=self.corpus_selected,
                        *self.model.non_default_corpora())
        om["borderwidth"] = 0
        om["highlightthickness"] = 1
        om.pack(side="left")
        innerframe.pack(side="top", fill="x", anchor="n")

    def _init_status(self, parent):
        self.status = Label(
            parent,
            justify=LEFT,
            relief=SUNKEN,
            background=self._BACKGROUND_COLOUR,
            border=0,
            padx=1,
            pady=0,
        )
        self.status.pack(side="top", anchor="sw")

    def _init_query_box(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        another = Frame(innerframe, background=self._BACKGROUND_COLOUR)
        self.query_box = Entry(another, width=60)
        self.query_box.pack(side="left", fill="x", pady=25, anchor="center")
        self.search_button = Button(
            another,
            text="Search",
            command=self.search,
            borderwidth=1,
            highlightthickness=1,
        )
        self.search_button.pack(side="left",
                                fill="x",
                                pady=25,
                                anchor="center")
        self.query_box.bind("<KeyPress-Return>",
                            self.search_enter_keypress_handler)
        another.pack()
        innerframe.pack(side="top", fill="x", anchor="n")

    def search_enter_keypress_handler(self, *event):
        self.search()

    def _init_results_box(self, parent):
        innerframe = Frame(parent)
        i1 = Frame(innerframe)
        i2 = Frame(innerframe)
        vscrollbar = Scrollbar(i1, borderwidth=1)
        hscrollbar = Scrollbar(i2, borderwidth=1, orient="horiz")
        self.results_box = Text(
            i1,
            font=Font(family="courier", size="16"),
            state="disabled",
            borderwidth=1,
            yscrollcommand=vscrollbar.set,
            xscrollcommand=hscrollbar.set,
            wrap="none",
            width="40",
            height="20",
            exportselection=1,
        )
        self.results_box.pack(side="left", fill="both", expand=True)
        self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG,
                                    foreground=self._HIGHLIGHT_WORD_COLOUR)
        self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG,
                                    foreground=self._HIGHLIGHT_LABEL_COLOUR)
        vscrollbar.pack(side="left", fill="y", anchor="e")
        vscrollbar.config(command=self.results_box.yview)
        hscrollbar.pack(side="left", fill="x", expand=True, anchor="w")
        hscrollbar.config(command=self.results_box.xview)
        # there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!!
        Label(i2, text="   ",
              background=self._BACKGROUND_COLOUR).pack(side="left", anchor="e")
        i1.pack(side="top", fill="both", expand=True, anchor="n")
        i2.pack(side="bottom", fill="x", anchor="s")
        innerframe.pack(side="top", fill="both", expand=True)

    def _init_paging(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.prev = prev = Button(
            innerframe,
            text="Previous",
            command=self.previous,
            width="10",
            borderwidth=1,
            highlightthickness=1,
            state="disabled",
        )
        prev.pack(side="left", anchor="center")
        self.next = next = Button(
            innerframe,
            text="Next",
            command=self.__next__,
            width="10",
            borderwidth=1,
            highlightthickness=1,
            state="disabled",
        )
        next.pack(side="right", anchor="center")
        innerframe.pack(side="top", fill="y")
        self.current_page = 0

    def previous(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.prev(self.current_page - 1)

    def __next__(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.next(self.current_page + 1)

    def about(self, *e):
        ABOUT = "NLTK Concordance Search Demo\n"
        TITLE = "About: NLTK Concordance Search Demo"
        try:
            from six.moves.tkinter_messagebox import Message

            Message(message=ABOUT, title=TITLE, parent=self.main_frame).show()
        except:
            ShowText(self.top, TITLE, ABOUT)

    def _bind_event_handlers(self):
        self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded)
        self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated)
        self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error)
        self.top.bind(ERROR_LOADING_CORPUS_EVENT,
                      self.handle_error_loading_corpus)

    def _poll(self):
        try:
            event = self.queue.get(block=False)
        except q.Empty:
            pass
        else:
            if event == CORPUS_LOADED_EVENT:
                self.handle_corpus_loaded(event)
            elif event == SEARCH_TERMINATED_EVENT:
                self.handle_search_terminated(event)
            elif event == SEARCH_ERROR_EVENT:
                self.handle_search_error(event)
            elif event == ERROR_LOADING_CORPUS_EVENT:
                self.handle_error_loading_corpus(event)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def handle_error_loading_corpus(self, event):
        self.status["text"] = "Error in loading " + self.var.get()
        self.unfreeze_editable()
        self.clear_all()
        self.freeze_editable()

    def handle_corpus_loaded(self, event):
        self.status["text"] = self.var.get() + " is loaded"
        self.unfreeze_editable()
        self.clear_all()
        self.query_box.focus_set()

    def handle_search_terminated(self, event):
        # todo: refactor the model such that it is less state sensitive
        results = self.model.get_results()
        self.write_results(results)
        self.status["text"] = ""
        if len(results) == 0:
            self.status["text"] = "No results found for " + self.model.query
        else:
            self.current_page = self.model.last_requested_page
        self.unfreeze_editable()
        self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT)

    def handle_search_error(self, event):
        self.status["text"] = "Error in query " + self.model.query
        self.unfreeze_editable()

    def corpus_selected(self, *args):
        new_selection = self.var.get()
        self.load_corpus(new_selection)

    def load_corpus(self, selection):
        if self.model.selected_corpus != selection:
            self.status["text"] = "Loading " + selection + "..."
            self.freeze_editable()
            self.model.load_corpus(selection)

    def search(self):
        self.current_page = 0
        self.clear_results_box()
        self.model.reset_results()
        query = self.query_box.get()
        if len(query.strip()) == 0:
            return
        self.status["text"] = "Searching for " + query
        self.freeze_editable()
        self.model.search(query, self.current_page + 1)

    def write_results(self, results):
        self.results_box["state"] = "normal"
        row = 1
        for each in results:
            sent, pos1, pos2 = each[0].strip(), each[1], each[2]
            if len(sent) != 0:
                if pos1 < self._char_before:
                    sent, pos1, pos2 = self.pad(sent, pos1, pos2)
                sentence = sent[pos1 - self._char_before:pos1 +
                                self._char_after]
                if not row == len(results):
                    sentence += "\n"
                self.results_box.insert(str(row) + ".0", sentence)
                word_markers, label_markers = self.words_and_labels(
                    sent, pos1, pos2)
                for marker in word_markers:
                    self.results_box.tag_add(
                        self._HIGHLIGHT_WORD_TAG,
                        str(row) + "." + str(marker[0]),
                        str(row) + "." + str(marker[1]),
                    )
                for marker in label_markers:
                    self.results_box.tag_add(
                        self._HIGHLIGHT_LABEL_TAG,
                        str(row) + "." + str(marker[0]),
                        str(row) + "." + str(marker[1]),
                    )
                row += 1
        self.results_box["state"] = "disabled"

    def words_and_labels(self, sentence, pos1, pos2):
        search_exp = sentence[pos1:pos2]
        words, labels = [], []
        labeled_words = search_exp.split(" ")
        index = 0
        for each in labeled_words:
            if each == "":
                index += 1
            else:
                word, label = each.split("/")
                words.append((self._char_before + index,
                              self._char_before + index + len(word)))
                index += len(word) + 1
                labels.append((self._char_before + index,
                               self._char_before + index + len(label)))
                index += len(label)
            index += 1
        return words, labels

    def pad(self, sent, hstart, hend):
        if hstart >= self._char_before:
            return sent, hstart, hend
        d = self._char_before - hstart
        sent = "".join([" "] * d) + sent
        return sent, hstart + d, hend + d

    def destroy(self, *e):
        if self.top is None:
            return
        self.top.after_cancel(self.after)
        self.top.destroy()
        self.top = None

    def clear_all(self):
        self.query_box.delete(0, END)
        self.model.reset_query()
        self.clear_results_box()

    def clear_results_box(self):
        self.results_box["state"] = "normal"
        self.results_box.delete("1.0", END)
        self.results_box["state"] = "disabled"

    def freeze_editable(self):
        self.query_box["state"] = "disabled"
        self.search_button["state"] = "disabled"
        self.prev["state"] = "disabled"
        self.next["state"] = "disabled"

    def unfreeze_editable(self):
        self.query_box["state"] = "normal"
        self.search_button["state"] = "normal"
        self.set_paging_button_states()

    def set_paging_button_states(self):
        if self.current_page == 0 or self.current_page == 1:
            self.prev["state"] = "disabled"
        else:
            self.prev["state"] = "normal"
        if self.model.has_more_pages(self.current_page):
            self.next["state"] = "normal"
        else:
            self.next["state"] = "disabled"

    def fire_event(self, event):
        # Firing an event so that rendering of widgets happen in the mainloop thread
        self.top.event_generate(event, when="tail")

    def mainloop(self, *args, **kwargs):
        if in_idle():
            return
        self.top.mainloop(*args, **kwargs)
Beispiel #23
0
class LoginDialog(object):
    def __init__(self, host):
        self.__interna_init()
        self.__host = host
        self.__initialized = False
        if TKTk is not None:
            self.__create_content()
            self.__initialized = True

    def __interna_init(self):
        self.__rootFrame = None
        self.__top = None
        self.__usrEntry = None
        self.__pwdEntry = None
        self.__accepted = False
        self.__host = None
        self.__usr = None
        self.__pwd = None

    def __cancel_action(self):
        self.__accepted = False
        self.__rootFrame.destroy()

    def __login_action(self):
        self.__accepted = True
        self.__usr = self.__usrEntry.get()
        self.__pwd = self.__pwdEntry.get()
        self.__rootFrame.destroy()

    def __enter_action(self, event):
        self.__login_action()

    def __create_content(self):
        self.__rootFrame = TKTk()
        self.__rootFrame.withdraw()

        self.__top = TKToplevel(self.__rootFrame)
        self.__top.title("Login")
        self.__top.protocol("WM_DELETE_WINDOW", self.__rootFrame.destroy)

        self.__top.bind('<Return>', self.__enter_action)

        self.__top.update_idletasks()
        width = self.__top.winfo_width()
        height = self.__top.winfo_height()
        x = (self.__top.winfo_screenwidth() // 2) - (width // 2)
        y = (self.__top.winfo_screenheight() // 2) - (height // 2)

        self.__top.geometry("+%d+%d" % (x, y))

        row = 0
        expLabel = TKLabel(self.__top, text='Login to host:')
        expLabel.grid(row=row, column=0, columnspan=4, padx=5, pady=2)

        row = row + 1
        urlLabel = TKLabel(self.__top, text=self.__host)
        urlLabel.grid(row=row, column=0, columnspan=4, padx=5, pady=2)

        row = row + 1
        usrLabel = TKLabel(self.__top, text='User')
        usrLabel.grid(row=row, column=0, columnspan=2, padx=20, pady=5)
        self.__usrEntry = TKEntry(self.__top, width=20)
        self.__usrEntry.grid(row=row, column=2, columnspan=2, padx=5, pady=5)

        row = row + 1
        pwdLabel = TKLabel(self.__top, text='Password')
        pwdLabel.grid(row=row, column=0, columnspan=2, padx=20, pady=5)
        self.__pwdEntry = TKEntry(self.__top, width=20, show="*")
        self.__pwdEntry.grid(row=row, column=2, columnspan=2, padx=5, pady=5)

        row = row + 1
        cancelButton = TKButton(self.__top,
                                text='Cancel',
                                command=self.__cancel_action)
        cancelButton.grid(row=row, column=1, padx=5, pady=5)
        loginButton = TKButton(self.__top,
                               text='Login',
                               command=self.__login_action)
        loginButton.grid(row=row, column=2, padx=5, pady=5)

    def show_login(self):
        if self.__initialized:
            self.__usrEntry.focus_set()
            self.__rootFrame.mainloop()
        else:
            print("tkinter python module is not available.\n\
            Please, install tkinter module or use command line login utility.")

    def is_accepted(self):
        return self.__accepted

    def get_user(self):
        return self.__usr

    def get_password(self):
        return self.__pwd
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 six.moves.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()
Beispiel #25
0
            global button_index
            global pressed

            if button_index is None:
                unpress()
                update(message="pressed: %s, %s" % (e.keycode, e.keysym))

                for i, code_data in kbd._codes.items():
                    if code_data is None:
                        continue
                    if code_data[0] != e.keycode:
                        continue
                    iid = b2id[i]
                    cnv.itemconfig(iid, fill=color_pressed)
                    pressed.append(iid)

                return

            key = (e.keycode, e.keysym)

            kbd._codes[button_index] = key

            cnv.itemconfig(b2tid[button_index], text=key_lb_fmt % key)
            update(message=MSG_SET)

        root.bind_all("<Key>", on_key, "+")

        update()

        root.mainloop()
class ConcordanceSearchView(object):
    _BACKGROUND_COLOUR='#FFF' #white

    #Colour of highlighted results
    _HIGHLIGHT_WORD_COLOUR='#F00' #red
    _HIGHLIGHT_WORD_TAG='HL_WRD_TAG'

    _HIGHLIGHT_LABEL_COLOUR='#C0C0C0' # dark grey
    _HIGHLIGHT_LABEL_TAG='HL_LBL_TAG'


    #Percentage of text left of the scrollbar position
    _FRACTION_LEFT_TEXT=0.30

    def __init__(self):
        self.queue = q.Queue()
        self.model = ConcordanceSearchModel(self.queue)
        self.top = Tk()
        self._init_top(self.top)
        self._init_menubar()
        self._init_widgets(self.top)
        self.load_corpus(self.model.DEFAULT_CORPUS)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def _init_top(self, top):
        top.geometry('950x680+50+50')
        top.title('NLTK Concordance Search')
        top.bind('<Control-q>', self.destroy)
        top.protocol('WM_DELETE_WINDOW', self.destroy)
        top.minsize(950,680)

    def _init_widgets(self, parent):
        self.main_frame = Frame(parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1))
        self._init_corpus_select(self.main_frame)
        self._init_query_box(self.main_frame)
        self._init_results_box(self.main_frame)
        self._init_paging(self.main_frame)
        self._init_status(self.main_frame)
        self.main_frame.pack(fill='both', expand=True)

    def _init_menubar(self):
        self._result_size = IntVar(self.top)
        self._cntx_bf_len = IntVar(self.top)
        self._cntx_af_len = IntVar(self.top)
        menubar = Menu(self.top)

        filemenu = Menu(menubar, tearoff=0, borderwidth=0)
        filemenu.add_command(label='Exit', underline=1,
                             command=self.destroy, accelerator='Ctrl-q')
        menubar.add_cascade(label='File', underline=0, menu=filemenu)

        editmenu = Menu(menubar, tearoff=0)
        rescntmenu = Menu(editmenu, tearoff=0)
        rescntmenu.add_radiobutton(label='20', variable=self._result_size,
                                   underline=0, value=20,
                                   command=self.set_result_size)
        rescntmenu.add_radiobutton(label='50', variable=self._result_size,
                                   underline=0, value=50,
                                   command=self.set_result_size)
        rescntmenu.add_radiobutton(label='100', variable=self._result_size,
                                   underline=0, value=100,
                                   command=self.set_result_size)
        rescntmenu.invoke(1)
        editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu)

        cntxmenu = Menu(editmenu, tearoff=0)
        cntxbfmenu = Menu(cntxmenu, tearoff=0)
        cntxbfmenu.add_radiobutton(label='60 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0, value=60,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.add_radiobutton(label='80 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0, value=80,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.add_radiobutton(label='100 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0, value=100,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.invoke(1)
        cntxmenu.add_cascade(label='Before', underline=0, menu=cntxbfmenu)

        cntxafmenu = Menu(cntxmenu, tearoff=0)
        cntxafmenu.add_radiobutton(label='70 characters',
                                   variable=self._cntx_af_len,
                                   underline=0, value=70,
                                   command=self.set_cntx_af_len)
        cntxafmenu.add_radiobutton(label='90 characters',
                                   variable=self._cntx_af_len,
                                   underline=0, value=90,
                                   command=self.set_cntx_af_len)
        cntxafmenu.add_radiobutton(label='110 characters',
                                   variable=self._cntx_af_len,
                                   underline=0, value=110,
                                   command=self.set_cntx_af_len)
        cntxafmenu.invoke(1)
        cntxmenu.add_cascade(label='After', underline=0, menu=cntxafmenu)

        editmenu.add_cascade(label='Context', underline=0, menu=cntxmenu)

        menubar.add_cascade(label='Edit', underline=0, menu=editmenu)

        self.top.config(menu=menubar)

    def set_result_size(self, **kwargs):
        self.model.result_count = self._result_size.get()

    def set_cntx_af_len(self, **kwargs):
        self._char_after = self._cntx_af_len.get()

    def set_cntx_bf_len(self, **kwargs):
        self._char_before = self._cntx_bf_len.get()

    def _init_corpus_select(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.var = StringVar(innerframe)
        self.var.set(self.model.DEFAULT_CORPUS)
        Label(innerframe, justify=LEFT, text=' Corpus: ',
              background=self._BACKGROUND_COLOUR, padx = 2, pady = 1, border = 0).pack(side='left')

        other_corpora = list(self.model.CORPORA.keys()).remove(self.model.DEFAULT_CORPUS)
        om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora())
        om['borderwidth'] = 0
        om['highlightthickness'] = 1
        om.pack(side='left')
        innerframe.pack(side='top', fill='x', anchor='n')

    def _init_status(self, parent):
        self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx = 1, pady = 0)
        self.status.pack(side='top', anchor='sw')

    def _init_query_box(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        another = Frame(innerframe, background=self._BACKGROUND_COLOUR)
        self.query_box = Entry(another, width=60)
        self.query_box.pack(side='left', fill='x', pady=25, anchor='center')
        self.search_button = Button(another, text='Search', command=self.search, borderwidth=1, highlightthickness=1)
        self.search_button.pack(side='left', fill='x', pady=25, anchor='center')
        self.query_box.bind('<KeyPress-Return>', self.search_enter_keypress_handler)
        another.pack()
        innerframe.pack(side='top', fill='x', anchor='n')

    def search_enter_keypress_handler(self, *event):
        self.search()

    def _init_results_box(self, parent):
        innerframe = Frame(parent)
        i1 = Frame(innerframe)
        i2 = Frame(innerframe)
        vscrollbar = Scrollbar(i1, borderwidth=1)
        hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz')
        self.results_box = Text(i1,
                                font=Font(family='courier', size='16'),
                                state='disabled', borderwidth=1,
                                                            yscrollcommand=vscrollbar.set,
                                xscrollcommand=hscrollbar.set, wrap='none', width='40', height = '20', exportselection=1)
        self.results_box.pack(side='left', fill='both', expand=True)
        self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG, foreground=self._HIGHLIGHT_WORD_COLOUR)
        self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG, foreground=self._HIGHLIGHT_LABEL_COLOUR)
        vscrollbar.pack(side='left', fill='y', anchor='e')
        vscrollbar.config(command=self.results_box.yview)
        hscrollbar.pack(side='left', fill='x', expand=True, anchor='w')
        hscrollbar.config(command=self.results_box.xview)
        #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!!
        Label(i2, text='   ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e')
        i1.pack(side='top', fill='both', expand=True, anchor='n')
        i2.pack(side='bottom', fill='x', anchor='s')
        innerframe.pack(side='top', fill='both', expand=True)

    def _init_paging(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled')
        prev.pack(side='left', anchor='center')
        self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled')
        next.pack(side='right', anchor='center')
        innerframe.pack(side='top', fill='y')
        self.current_page = 0

    def previous(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.prev(self.current_page - 1)

    def __next__(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.next(self.current_page + 1)

    def about(self, *e):
        ABOUT = ("NLTK Concordance Search Demo\n")
        TITLE = 'About: NLTK Concordance Search Demo'
        try:
            from six.moves.tkinter_messagebox import Message
            Message(message=ABOUT, title=TITLE, parent=self.main_frame).show()
        except:
            ShowText(self.top, TITLE, ABOUT)

    def _bind_event_handlers(self):
        self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded)
        self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated)
        self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error)
        self.top.bind(ERROR_LOADING_CORPUS_EVENT, self.handle_error_loading_corpus)

    def _poll(self):
        try:
            event = self.queue.get(block=False)
        except q.Empty:
            pass
        else:
            if event == CORPUS_LOADED_EVENT:
                self.handle_corpus_loaded(event)
            elif event == SEARCH_TERMINATED_EVENT:
                self.handle_search_terminated(event)
            elif event == SEARCH_ERROR_EVENT:
                self.handle_search_error(event)
            elif event == ERROR_LOADING_CORPUS_EVENT:
                self.handle_error_loading_corpus(event)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def handle_error_loading_corpus(self, event):
        self.status['text'] = 'Error in loading ' + self.var.get()
        self.unfreeze_editable()
        self.clear_all()
        self.freeze_editable()

    def handle_corpus_loaded(self, event):
        self.status['text'] = self.var.get() + ' is loaded'
        self.unfreeze_editable()
        self.clear_all()
        self.query_box.focus_set()

    def handle_search_terminated(self, event):
        #todo: refactor the model such that it is less state sensitive
        results = self.model.get_results()
        self.write_results(results)
        self.status['text'] = ''
        if len(results) == 0:
            self.status['text'] = 'No results found for ' + self.model.query
        else:
                self.current_page = self.model.last_requested_page
        self.unfreeze_editable()
        self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT)

    def handle_search_error(self, event):
        self.status['text'] = 'Error in query ' + self.model.query
        self.unfreeze_editable()

    def corpus_selected(self, *args):
        new_selection = self.var.get()
        self.load_corpus(new_selection)

    def load_corpus(self, selection):
        if self.model.selected_corpus != selection:
            self.status['text'] = 'Loading ' + selection + '...'
            self.freeze_editable()
            self.model.load_corpus(selection)

    def search(self):
        self.current_page = 0
        self.clear_results_box()
        self.model.reset_results()
        query = self.query_box.get()
        if (len(query.strip()) == 0): return
        self.status['text']  = 'Searching for ' + query
        self.freeze_editable()
        self.model.search(query, self.current_page + 1, )


    def write_results(self, results):
        self.results_box['state'] = 'normal'
        row = 1
        for each in results:
            sent, pos1, pos2 = each[0].strip(), each[1], each[2]
            if len(sent) != 0:
                if (pos1 < self._char_before):
                    sent, pos1, pos2 = self.pad(sent, pos1, pos2)
                sentence = sent[pos1-self._char_before:pos1+self._char_after]
                if not row == len(results):
                    sentence += '\n'
                self.results_box.insert(str(row) + '.0', sentence)
                word_markers, label_markers = self.words_and_labels(sent, pos1, pos2)
                for marker in word_markers: self.results_box.tag_add(self._HIGHLIGHT_WORD_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1]))
                for marker in label_markers: self.results_box.tag_add(self._HIGHLIGHT_LABEL_TAG, str(row) + '.' + str(marker[0]), str(row) + '.' + str(marker[1]))
                row += 1
        self.results_box['state'] = 'disabled'

    def words_and_labels(self, sentence, pos1, pos2):
        search_exp = sentence[pos1:pos2]
        words, labels = [], []
        labeled_words = search_exp.split(' ')
        index = 0
        for each in labeled_words:
            if each == '':
                index += 1
            else:
                word, label = each.split('/')
                words.append((self._char_before + index, self._char_before + index + len(word)))
                index += len(word) + 1
                labels.append((self._char_before + index, self._char_before + index + len(label)))
                index += len(label)
            index += 1
        return words, labels

    def pad(self, sent, hstart, hend):
        if hstart >= self._char_before:
            return sent, hstart, hend
        d = self._char_before - hstart
        sent = ''.join([' '] * d) + sent
        return sent, hstart + d, hend + d

    def destroy(self, *e):
        if self.top is None: return
        self.top.after_cancel(self.after)
        self.top.destroy()
        self.top = None

    def clear_all(self):
        self.query_box.delete(0, END)
        self.model.reset_query()
        self.clear_results_box()

    def clear_results_box(self):
        self.results_box['state'] = 'normal'
        self.results_box.delete("1.0", END)
        self.results_box['state'] = 'disabled'

    def freeze_editable(self):
        self.query_box['state'] = 'disabled'
        self.search_button['state'] = 'disabled'
        self.prev['state'] = 'disabled'
        self.next['state'] = 'disabled'

    def unfreeze_editable(self):
        self.query_box['state'] = 'normal'
        self.search_button['state'] = 'normal'
        self.set_paging_button_states()

    def set_paging_button_states(self):
        if self.current_page == 0 or self.current_page == 1:
            self.prev['state'] = 'disabled'
        else:
            self.prev['state'] = 'normal'
        if self.model.has_more_pages(self.current_page):
            self.next['state'] = 'normal'
        else:
            self.next['state'] = 'disabled'

    def fire_event(self, event):
        #Firing an event so that rendering of widgets happen in the mainloop thread
        self.top.event_generate(event, when='tail')

    def mainloop(self, *args, **kwargs):
        if in_idle(): return
        self.top.mainloop(*args, **kwargs)
Beispiel #27
0
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 six.moves.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()
Beispiel #28
0
        self.their_grid.redraw(grids[1])

        if all([g.has_all_ships()
                for g in grids]) and all([g.all_sunk() for g in grids]):
            #TODO
            # state is game over.
            pass
        elif all([g.has_all_ships() for g in grids]):
            self.process_playing_state()
        else:
            #TODO
            # state is placing
            pass


def hide(w):
    w.config(state=HIDDEN)


if __name__ == "__main__":
    app = Tk()

    game_frame = Game(app)
    game_frame.pack(fill=BOTH, expand=1)
    game_frame.grab_set()
    game_frame.focus_set()

    game_frame.show_warning("cannot place ship there")

    app.mainloop()
Beispiel #29
0
class ConcordanceSearchView(object):
    _BACKGROUND_COLOUR = '#FFF'  #white

    #Colour of highlighted results
    _HIGHLIGHT_WORD_COLOUR = '#F00'  #red
    _HIGHLIGHT_WORD_TAG = 'HL_WRD_TAG'

    _HIGHLIGHT_LABEL_COLOUR = '#C0C0C0'  # dark grey
    _HIGHLIGHT_LABEL_TAG = 'HL_LBL_TAG'

    #Percentage of text left of the scrollbar position
    _FRACTION_LEFT_TEXT = 0.30

    def __init__(self):
        self.queue = q.Queue()
        self.model = ConcordanceSearchModel(self.queue)
        self.top = Tk()
        self._init_top(self.top)
        self._init_menubar()
        self._init_widgets(self.top)
        self.load_corpus(self.model.DEFAULT_CORPUS)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def _init_top(self, top):
        top.geometry('950x680+50+50')
        top.title('NLTK Concordance Search')
        top.bind('<Control-q>', self.destroy)
        top.protocol('WM_DELETE_WINDOW', self.destroy)
        top.minsize(950, 680)

    def _init_widgets(self, parent):
        self.main_frame = Frame(
            parent,
            dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1))
        self._init_corpus_select(self.main_frame)
        self._init_query_box(self.main_frame)
        self._init_results_box(self.main_frame)
        self._init_paging(self.main_frame)
        self._init_status(self.main_frame)
        self.main_frame.pack(fill='both', expand=True)

    def _init_menubar(self):
        self._result_size = IntVar(self.top)
        self._cntx_bf_len = IntVar(self.top)
        self._cntx_af_len = IntVar(self.top)
        menubar = Menu(self.top)

        filemenu = Menu(menubar, tearoff=0, borderwidth=0)
        filemenu.add_command(label='Exit',
                             underline=1,
                             command=self.destroy,
                             accelerator='Ctrl-q')
        menubar.add_cascade(label='File', underline=0, menu=filemenu)

        editmenu = Menu(menubar, tearoff=0)
        rescntmenu = Menu(editmenu, tearoff=0)
        rescntmenu.add_radiobutton(label='20',
                                   variable=self._result_size,
                                   underline=0,
                                   value=20,
                                   command=self.set_result_size)
        rescntmenu.add_radiobutton(label='50',
                                   variable=self._result_size,
                                   underline=0,
                                   value=50,
                                   command=self.set_result_size)
        rescntmenu.add_radiobutton(label='100',
                                   variable=self._result_size,
                                   underline=0,
                                   value=100,
                                   command=self.set_result_size)
        rescntmenu.invoke(1)
        editmenu.add_cascade(label='Result Count',
                             underline=0,
                             menu=rescntmenu)

        cntxmenu = Menu(editmenu, tearoff=0)
        cntxbfmenu = Menu(cntxmenu, tearoff=0)
        cntxbfmenu.add_radiobutton(label='60 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0,
                                   value=60,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.add_radiobutton(label='80 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0,
                                   value=80,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.add_radiobutton(label='100 characters',
                                   variable=self._cntx_bf_len,
                                   underline=0,
                                   value=100,
                                   command=self.set_cntx_bf_len)
        cntxbfmenu.invoke(1)
        cntxmenu.add_cascade(label='Before', underline=0, menu=cntxbfmenu)

        cntxafmenu = Menu(cntxmenu, tearoff=0)
        cntxafmenu.add_radiobutton(label='70 characters',
                                   variable=self._cntx_af_len,
                                   underline=0,
                                   value=70,
                                   command=self.set_cntx_af_len)
        cntxafmenu.add_radiobutton(label='90 characters',
                                   variable=self._cntx_af_len,
                                   underline=0,
                                   value=90,
                                   command=self.set_cntx_af_len)
        cntxafmenu.add_radiobutton(label='110 characters',
                                   variable=self._cntx_af_len,
                                   underline=0,
                                   value=110,
                                   command=self.set_cntx_af_len)
        cntxafmenu.invoke(1)
        cntxmenu.add_cascade(label='After', underline=0, menu=cntxafmenu)

        editmenu.add_cascade(label='Context', underline=0, menu=cntxmenu)

        menubar.add_cascade(label='Edit', underline=0, menu=editmenu)

        self.top.config(menu=menubar)

    def set_result_size(self, **kwargs):
        self.model.result_count = self._result_size.get()

    def set_cntx_af_len(self, **kwargs):
        self._char_after = self._cntx_af_len.get()

    def set_cntx_bf_len(self, **kwargs):
        self._char_before = self._cntx_bf_len.get()

    def _init_corpus_select(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.var = StringVar(innerframe)
        self.var.set(self.model.DEFAULT_CORPUS)
        Label(innerframe,
              justify=LEFT,
              text=' Corpus: ',
              background=self._BACKGROUND_COLOUR,
              padx=2,
              pady=1,
              border=0).pack(side='left')

        other_corpora = list(self.model.CORPORA.keys()).remove(
            self.model.DEFAULT_CORPUS)
        om = OptionMenu(innerframe,
                        self.var,
                        self.model.DEFAULT_CORPUS,
                        command=self.corpus_selected,
                        *self.model.non_default_corpora())
        om['borderwidth'] = 0
        om['highlightthickness'] = 1
        om.pack(side='left')
        innerframe.pack(side='top', fill='x', anchor='n')

    def _init_status(self, parent):
        self.status = Label(parent,
                            justify=LEFT,
                            relief=SUNKEN,
                            background=self._BACKGROUND_COLOUR,
                            border=0,
                            padx=1,
                            pady=0)
        self.status.pack(side='top', anchor='sw')

    def _init_query_box(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        another = Frame(innerframe, background=self._BACKGROUND_COLOUR)
        self.query_box = Entry(another, width=60)
        self.query_box.pack(side='left', fill='x', pady=25, anchor='center')
        self.search_button = Button(another,
                                    text='Search',
                                    command=self.search,
                                    borderwidth=1,
                                    highlightthickness=1)
        self.search_button.pack(side='left',
                                fill='x',
                                pady=25,
                                anchor='center')
        self.query_box.bind('<KeyPress-Return>',
                            self.search_enter_keypress_handler)
        another.pack()
        innerframe.pack(side='top', fill='x', anchor='n')

    def search_enter_keypress_handler(self, *event):
        self.search()

    def _init_results_box(self, parent):
        innerframe = Frame(parent)
        i1 = Frame(innerframe)
        i2 = Frame(innerframe)
        vscrollbar = Scrollbar(i1, borderwidth=1)
        hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz')
        self.results_box = Text(i1,
                                font=Font(family='courier', size='16'),
                                state='disabled',
                                borderwidth=1,
                                yscrollcommand=vscrollbar.set,
                                xscrollcommand=hscrollbar.set,
                                wrap='none',
                                width='40',
                                height='20',
                                exportselection=1)
        self.results_box.pack(side='left', fill='both', expand=True)
        self.results_box.tag_config(self._HIGHLIGHT_WORD_TAG,
                                    foreground=self._HIGHLIGHT_WORD_COLOUR)
        self.results_box.tag_config(self._HIGHLIGHT_LABEL_TAG,
                                    foreground=self._HIGHLIGHT_LABEL_COLOUR)
        vscrollbar.pack(side='left', fill='y', anchor='e')
        vscrollbar.config(command=self.results_box.yview)
        hscrollbar.pack(side='left', fill='x', expand=True, anchor='w')
        hscrollbar.config(command=self.results_box.xview)
        #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!!
        Label(i2, text='   ',
              background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e')
        i1.pack(side='top', fill='both', expand=True, anchor='n')
        i2.pack(side='bottom', fill='x', anchor='s')
        innerframe.pack(side='top', fill='both', expand=True)

    def _init_paging(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.prev = prev = Button(innerframe,
                                  text='Previous',
                                  command=self.previous,
                                  width='10',
                                  borderwidth=1,
                                  highlightthickness=1,
                                  state='disabled')
        prev.pack(side='left', anchor='center')
        self.next = next = Button(innerframe,
                                  text='Next',
                                  command=self.__next__,
                                  width='10',
                                  borderwidth=1,
                                  highlightthickness=1,
                                  state='disabled')
        next.pack(side='right', anchor='center')
        innerframe.pack(side='top', fill='y')
        self.current_page = 0

    def previous(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.prev(self.current_page - 1)

    def __next__(self):
        self.clear_results_box()
        self.freeze_editable()
        self.model.next(self.current_page + 1)

    def about(self, *e):
        ABOUT = ("NLTK Concordance Search Demo\n")
        TITLE = 'About: NLTK Concordance Search Demo'
        try:
            from six.moves.tkinter_messagebox import Message
            Message(message=ABOUT, title=TITLE, parent=self.main_frame).show()
        except:
            ShowText(self.top, TITLE, ABOUT)

    def _bind_event_handlers(self):
        self.top.bind(CORPUS_LOADED_EVENT, self.handle_corpus_loaded)
        self.top.bind(SEARCH_TERMINATED_EVENT, self.handle_search_terminated)
        self.top.bind(SEARCH_ERROR_EVENT, self.handle_search_error)
        self.top.bind(ERROR_LOADING_CORPUS_EVENT,
                      self.handle_error_loading_corpus)

    def _poll(self):
        try:
            event = self.queue.get(block=False)
        except q.Empty:
            pass
        else:
            if event == CORPUS_LOADED_EVENT:
                self.handle_corpus_loaded(event)
            elif event == SEARCH_TERMINATED_EVENT:
                self.handle_search_terminated(event)
            elif event == SEARCH_ERROR_EVENT:
                self.handle_search_error(event)
            elif event == ERROR_LOADING_CORPUS_EVENT:
                self.handle_error_loading_corpus(event)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def handle_error_loading_corpus(self, event):
        self.status['text'] = 'Error in loading ' + self.var.get()
        self.unfreeze_editable()
        self.clear_all()
        self.freeze_editable()

    def handle_corpus_loaded(self, event):
        self.status['text'] = self.var.get() + ' is loaded'
        self.unfreeze_editable()
        self.clear_all()
        self.query_box.focus_set()

    def handle_search_terminated(self, event):
        #todo: refactor the model such that it is less state sensitive
        results = self.model.get_results()
        self.write_results(results)
        self.status['text'] = ''
        if len(results) == 0:
            self.status['text'] = 'No results found for ' + self.model.query
        else:
            self.current_page = self.model.last_requested_page
        self.unfreeze_editable()
        self.results_box.xview_moveto(self._FRACTION_LEFT_TEXT)

    def handle_search_error(self, event):
        self.status['text'] = 'Error in query ' + self.model.query
        self.unfreeze_editable()

    def corpus_selected(self, *args):
        new_selection = self.var.get()
        self.load_corpus(new_selection)

    def load_corpus(self, selection):
        if self.model.selected_corpus != selection:
            self.status['text'] = 'Loading ' + selection + '...'
            self.freeze_editable()
            self.model.load_corpus(selection)

    def search(self):
        self.current_page = 0
        self.clear_results_box()
        self.model.reset_results()
        query = self.query_box.get()
        if (len(query.strip()) == 0): return
        self.status['text'] = 'Searching for ' + query
        self.freeze_editable()
        self.model.search(
            query,
            self.current_page + 1,
        )

    def write_results(self, results):
        self.results_box['state'] = 'normal'
        row = 1
        for each in results:
            sent, pos1, pos2 = each[0].strip(), each[1], each[2]
            if len(sent) != 0:
                if (pos1 < self._char_before):
                    sent, pos1, pos2 = self.pad(sent, pos1, pos2)
                sentence = sent[pos1 - self._char_before:pos1 +
                                self._char_after]
                if not row == len(results):
                    sentence += '\n'
                self.results_box.insert(str(row) + '.0', sentence)
                word_markers, label_markers = self.words_and_labels(
                    sent, pos1, pos2)
                for marker in word_markers:
                    self.results_box.tag_add(self._HIGHLIGHT_WORD_TAG,
                                             str(row) + '.' + str(marker[0]),
                                             str(row) + '.' + str(marker[1]))
                for marker in label_markers:
                    self.results_box.tag_add(self._HIGHLIGHT_LABEL_TAG,
                                             str(row) + '.' + str(marker[0]),
                                             str(row) + '.' + str(marker[1]))
                row += 1
        self.results_box['state'] = 'disabled'

    def words_and_labels(self, sentence, pos1, pos2):
        search_exp = sentence[pos1:pos2]
        words, labels = [], []
        labeled_words = search_exp.split(' ')
        index = 0
        for each in labeled_words:
            if each == '':
                index += 1
            else:
                word, label = each.split('/')
                words.append((self._char_before + index,
                              self._char_before + index + len(word)))
                index += len(word) + 1
                labels.append((self._char_before + index,
                               self._char_before + index + len(label)))
                index += len(label)
            index += 1
        return words, labels

    def pad(self, sent, hstart, hend):
        if hstart >= self._char_before:
            return sent, hstart, hend
        d = self._char_before - hstart
        sent = ''.join([' '] * d) + sent
        return sent, hstart + d, hend + d

    def destroy(self, *e):
        if self.top is None: return
        self.top.after_cancel(self.after)
        self.top.destroy()
        self.top = None

    def clear_all(self):
        self.query_box.delete(0, END)
        self.model.reset_query()
        self.clear_results_box()

    def clear_results_box(self):
        self.results_box['state'] = 'normal'
        self.results_box.delete("1.0", END)
        self.results_box['state'] = 'disabled'

    def freeze_editable(self):
        self.query_box['state'] = 'disabled'
        self.search_button['state'] = 'disabled'
        self.prev['state'] = 'disabled'
        self.next['state'] = 'disabled'

    def unfreeze_editable(self):
        self.query_box['state'] = 'normal'
        self.search_button['state'] = 'normal'
        self.set_paging_button_states()

    def set_paging_button_states(self):
        if self.current_page == 0 or self.current_page == 1:
            self.prev['state'] = 'disabled'
        else:
            self.prev['state'] = 'normal'
        if self.model.has_more_pages(self.current_page):
            self.next['state'] = 'normal'
        else:
            self.next['state'] = 'disabled'

    def fire_event(self, event):
        #Firing an event so that rendering of widgets happen in the mainloop thread
        self.top.event_generate(event, when='tail')

    def mainloop(self, *args, **kwargs):
        if in_idle(): return
        self.top.mainloop(*args, **kwargs)
class CollocationsView:
    _BACKGROUND_COLOUR='#FFF' #white

    def __init__(self):
        self.queue = q.Queue()
        self.model = CollocationsModel(self.queue)
        self.top = Tk()
        self._init_top(self.top)
        self._init_menubar()
        self._init_widgets(self.top)
        self.load_corpus(self.model.DEFAULT_CORPUS)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def _init_top(self, top):
        top.geometry('550x650+50+50')
        top.title('NLTK Collocations List')
        top.bind('<Control-q>', self.destroy)
        top.protocol('WM_DELETE_WINDOW', self.destroy)
        top.minsize(550,650)

    def _init_widgets(self, parent):
        self.main_frame = Frame(parent, dict(background=self._BACKGROUND_COLOUR, padx=1, pady=1, border=1))
        self._init_corpus_select(self.main_frame)
        self._init_results_box(self.main_frame)
        self._init_paging(self.main_frame)
        self._init_status(self.main_frame)
        self.main_frame.pack(fill='both', expand=True)

    def _init_corpus_select(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.var = StringVar(innerframe)
        self.var.set(self.model.DEFAULT_CORPUS)
        Label(innerframe, justify=LEFT, text=' Corpus: ', background=self._BACKGROUND_COLOUR, padx = 2, pady = 1, border = 0).pack(side='left')

        other_corpora = list(self.model.CORPORA.keys()).remove(self.model.DEFAULT_CORPUS)
        om = OptionMenu(innerframe, self.var, self.model.DEFAULT_CORPUS, command=self.corpus_selected, *self.model.non_default_corpora())
        om['borderwidth'] = 0
        om['highlightthickness'] = 1
        om.pack(side='left')
        innerframe.pack(side='top', fill='x', anchor='n')

    def _init_status(self, parent):
        self.status = Label(parent, justify=LEFT, relief=SUNKEN, background=self._BACKGROUND_COLOUR, border=0, padx = 1, pady = 0)
        self.status.pack(side='top', anchor='sw')

    def _init_menubar(self):
        self._result_size = IntVar(self.top)
        menubar = Menu(self.top)

        filemenu = Menu(menubar, tearoff=0, borderwidth=0)
        filemenu.add_command(label='Exit', underline=1,
                   command=self.destroy, accelerator='Ctrl-q')
        menubar.add_cascade(label='File', underline=0, menu=filemenu)

        editmenu = Menu(menubar, tearoff=0)
        rescntmenu = Menu(editmenu, tearoff=0)
        rescntmenu.add_radiobutton(label='20', variable=self._result_size,
                     underline=0, value=20, command=self.set_result_size)
        rescntmenu.add_radiobutton(label='50', variable=self._result_size,
                     underline=0, value=50, command=self.set_result_size)
        rescntmenu.add_radiobutton(label='100', variable=self._result_size,
                     underline=0, value=100, command=self.set_result_size)
        rescntmenu.invoke(1)
        editmenu.add_cascade(label='Result Count', underline=0, menu=rescntmenu)

        menubar.add_cascade(label='Edit', underline=0, menu=editmenu)
        self.top.config(menu=menubar)

    def set_result_size(self, **kwargs):
        self.model.result_count = self._result_size.get()

    def _init_results_box(self, parent):
        innerframe = Frame(parent)
        i1 = Frame(innerframe)
        i2 = Frame(innerframe)
        vscrollbar = Scrollbar(i1, borderwidth=1)
        hscrollbar = Scrollbar(i2, borderwidth=1, orient='horiz')
        self.results_box = Text(i1,
                    font=Font(family='courier', size='16'),
                    state='disabled', borderwidth=1,
                    yscrollcommand=vscrollbar.set,
                    xscrollcommand=hscrollbar.set, wrap='none', width='40', height = '20', exportselection=1)
        self.results_box.pack(side='left', fill='both', expand=True)
        vscrollbar.pack(side='left', fill='y', anchor='e')
        vscrollbar.config(command=self.results_box.yview)
        hscrollbar.pack(side='left', fill='x', expand=True, anchor='w')
        hscrollbar.config(command=self.results_box.xview)
        #there is no other way of avoiding the overlap of scrollbars while using pack layout manager!!!
        Label(i2, text='   ', background=self._BACKGROUND_COLOUR).pack(side='left', anchor='e')
        i1.pack(side='top', fill='both', expand=True, anchor='n')
        i2.pack(side='bottom', fill='x', anchor='s')
        innerframe.pack(side='top', fill='both', expand=True)

    def _init_paging(self, parent):
        innerframe = Frame(parent, background=self._BACKGROUND_COLOUR)
        self.prev = prev = Button(innerframe, text='Previous', command=self.previous, width='10', borderwidth=1, highlightthickness=1, state='disabled')
        prev.pack(side='left', anchor='center')
        self.next = next = Button(innerframe, text='Next', command=self.__next__, width='10', borderwidth=1, highlightthickness=1, state='disabled')
        next.pack(side='right', anchor='center')
        innerframe.pack(side='top', fill='y')
        self.reset_current_page()

    def reset_current_page(self):
        self.current_page = -1

    def _poll(self):
        try:
            event = self.queue.get(block=False)
        except q.Empty:
            pass
        else:
            if event == CORPUS_LOADED_EVENT:
                self.handle_corpus_loaded(event)
            elif event == ERROR_LOADING_CORPUS_EVENT:
                self.handle_error_loading_corpus(event)
        self.after = self.top.after(POLL_INTERVAL, self._poll)

    def handle_error_loading_corpus(self, event):
        self.status['text'] = 'Error in loading ' + self.var.get()
        self.unfreeze_editable()
        self.clear_results_box()
        self.freeze_editable()
        self.reset_current_page()

    def handle_corpus_loaded(self, event):
        self.status['text'] = self.var.get() + ' is loaded'
        self.unfreeze_editable()
        self.clear_results_box()
        self.reset_current_page()
        #self.next()
        collocations = self.model.next(self.current_page + 1)
        self.write_results(collocations)
        self.current_page += 1

    def corpus_selected(self, *args):
        new_selection = self.var.get()
        self.load_corpus(new_selection)

    def previous(self):
        self.freeze_editable()
        collocations = self.model.prev(self.current_page - 1)
        self.current_page= self.current_page - 1
        self.clear_results_box()
        self.write_results(collocations)
        self.unfreeze_editable()

    def __next__(self):
        self.freeze_editable()
        collocations = self.model.next(self.current_page + 1)
        self.clear_results_box()
        self.write_results(collocations)
        self.current_page += 1
        self.unfreeze_editable()

    def load_corpus(self, selection):
        if self.model.selected_corpus != selection:
            self.status['text'] = 'Loading ' + selection + '...'
            self.freeze_editable()
            self.model.load_corpus(selection)

    def freeze_editable(self):
        self.prev['state'] = 'disabled'
        self.next['state'] = 'disabled'

    def clear_results_box(self):
        self.results_box['state'] = 'normal'
        self.results_box.delete("1.0", END)
        self.results_box['state'] = 'disabled'

    def fire_event(self, event):
        #Firing an event so that rendering of widgets happen in the mainloop thread
        self.top.event_generate(event, when='tail')

    def destroy(self, *e):
        if self.top is None: return
        self.top.after_cancel(self.after)
        self.top.destroy()
        self.top = None

    def mainloop(self, *args, **kwargs):
        if in_idle(): return
        self.top.mainloop(*args, **kwargs)

    def unfreeze_editable(self):
        self.set_paging_button_states()

    def set_paging_button_states(self):
        if self.current_page == -1 or self.current_page == 0:
            self.prev['state'] = 'disabled'
        else:
            self.prev['state'] = 'normal'
        if self.model.is_last_page(self.current_page):
            self.next['state'] = 'disabled'
        else:
            self.next['state'] = 'normal'

    def write_results(self, results):
        self.results_box['state'] = 'normal'
        row = 1
        for each in results:
            self.results_box.insert(str(row) + '.0', each[0] + " " + each[1] + "\n")
            row += 1
        self.results_box['state'] = 'disabled'