Source code for nltk.sem.drt_glue_demo

# Natural Language Toolkit: GUI Demo for Glue Semantics with Discourse
#                           Representation Theory (DRT) as meaning language
#
# Author: Dan Garrette <dhgarrette@gmail.com>
#
# Copyright (C) 2001-2023 NLTK Project
# URL: <https://www.nltk.org/>
# For license information, see LICENSE.TXT

try:
    from tkinter import Button, Frame, IntVar, Label, Listbox, Menu, Scrollbar, Tk
    from tkinter.font import Font

    from nltk.draw.util import CanvasFrame, ShowText

except ImportError:
    """Ignore ImportError because tkinter might not be available."""

from nltk.parse import MaltParser
from nltk.sem.drt import DrsDrawer, DrtVariableExpression
from nltk.sem.glue import DrtGlue
from nltk.sem.logic import Variable
from nltk.tag import RegexpTagger
from nltk.util import in_idle


[docs]class DrtGlueDemo:
[docs] 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 #########################################
[docs] def destroy(self, *e): self._autostep = 0 if self._top is None: return self._top.destroy() self._top = None
[docs] 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)
[docs] 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)
[docs] def about(self, *e): ABOUT = ( "NLTK Discourse Representation Theory (DRT) Glue Semantics Demo\n" + "Written by Daniel H. Garrette" ) TITLE = "About: NLTK DRT Glue Demo" try: from tkinter.messagebox import Message Message(message=ABOUT, title=TITLE).show() except: ShowText(self._top, TITLE, ABOUT)
[docs] def postscript(self, *e): self._autostep = 0 self._cframe.print_to_file()
[docs] 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)
[docs] 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()
[docs]class DrsWidget:
[docs] def __init__(self, canvas, drs, **attribs): self._drs = drs self._canvas = canvas canvas.font = Font( font=canvas.itemcget(canvas.create_text(0, 0, text=""), "font") ) canvas._BUFFER = 3 self.bbox = (0, 0, 0, 0)
[docs] def draw(self): (right, bottom) = DrsDrawer(self._drs, canvas=self._canvas).draw() self.bbox = (0, 0, right + 1, bottom + 1)
[docs] def clear(self): self._canvas.create_rectangle(self.bbox, fill="white", width="0")
[docs]def demo(): examples = [ "John walks", "David sees Mary", "David eats a sandwich", "every man chases a dog", # 'every man believes a dog yawns', # 'John gives David a sandwich', "John chases himself", # 'John persuades David to order a pizza', # 'John tries to go', # 'John tries to find a unicorn', # 'John seems to vanish', # 'a unicorn seems to approach', # 'every big cat leaves', # 'every gray cat leaves', # 'every big gray cat leaves', # 'a former senator leaves', # 'John likes a cat', # 'John likes every cat', # 'he walks', # 'John walks and he leaves' ] DrtGlueDemo(examples).mainloop()
if __name__ == "__main__": demo()