1
2
3
4
5
6
7
8
9
10
11
12 """
13 A graphical tool for exploring chart parsing.
14
15 Chart parsing is a flexible parsing algorithm that uses a data
16 structure called a "chart" to record hypotheses about syntactic
17 constituents. Each hypothesis is represented by a single "edge" on
18 the chart. A set of "chart rules" determine when new edges can be
19 added to the chart. This set of rules controls the overall behavior
20 of the parser (e.g., whether it parses top-down or bottom-up).
21
22 The chart parsing tool demonstrates the process of parsing a single
23 sentence, with a given grammar and lexicon. Its display is divided
24 into three sections: the bottom section displays the chart; the middle
25 section displays the sentence; and the top section displays the
26 partial syntax tree corresponding to the selected edge. Buttons along
27 the bottom of the window are used to control the execution of the
28 algorithm.
29
30 The chart parsing tool allows for flexible control of the parsing
31 algorithm. At each step of the algorithm, you can select which rule
32 or strategy you wish to apply. This allows you to experiment with
33 mixing different strategies (e.g., top-down and bottom-up). You can
34 exercise fine-grained control over the algorithm by selecting which
35 edge you wish to apply a rule to.
36 """
37
38
39
40
41 import pickle
42 from tkFileDialog import asksaveasfilename, askopenfilename
43 import Tkinter
44 import math
45 import string
46 import os.path
47
48 from nltk.parse.chart import *
49 from nltk.tree import Tree
50 from nltk.grammar import Nonterminal, parse_cfg
51 from nltk.util import in_idle
52 from nltk.draw.util import *
53 from nltk.draw.cfg import CFGEditor
54 from nltk.draw.tree import tree_to_treesegment, TreeSegmentWidget
55
56
57
58
59
60
61
62
87
88
89
90
91
93 """
94 A view of a chart that displays the contents of the corresponding matrix.
95 """
96 - def __init__(self, parent, chart, toplevel=True, title='Chart Matrix',
97 show_numedges=False):
98 self._chart = chart
99 self._cells = []
100 self._marks = []
101
102 self._selected_cell = None
103
104 if toplevel:
105 self._root = Tkinter.Toplevel(parent)
106 self._root.title(title)
107 self._root.bind('<Control-q>', self.destroy)
108 self._init_quit(self._root)
109 else:
110 self._root = Tkinter.Frame(parent)
111
112 self._init_matrix(self._root)
113 self._init_list(self._root)
114 if show_numedges:
115 self._init_numedges(self._root)
116 else:
117 self._numedges_label = None
118
119 self._callbacks = {}
120
121 self._num_edges = 0
122
123 self.draw()
124
128
130 cframe = Tkinter.Frame(root, border=2, relief='sunken')
131 cframe.pack(expand=0, fill='none', padx=1, pady=3, side='top')
132 self._canvas = Tkinter.Canvas(cframe, width=200, height=200,
133 background='white')
134 self._canvas.pack(expand=0, fill='none')
135
137 self._numedges_label = Tkinter.Label(root, text='0 edges')
138 self._numedges_label.pack(expand=0, fill='none', side='top')
139
146
148 if self._root is None: return
149 try: self._root.destroy()
150 except: pass
151 self._root = None
152
154 if chart is not self._chart:
155 self._chart = chart
156 self._num_edges = 0
157 self.draw()
158
160 if self._root is None: return
161
162
163 N = len(self._cells)
164 cell_edges = [[0 for i in range(N)] for j in range(N)]
165 for edge in self._chart:
166 cell_edges[edge.start()][edge.end()] += 1
167
168
169 for i in range(N):
170 for j in range(i, N):
171 if cell_edges[i][j] == 0:
172 color = 'gray20'
173 else:
174 color = ('#00%02x%02x' %
175 (min(255, 50+128*cell_edges[i][j]/10),
176 max(0, 128-128*cell_edges[i][j]/10)))
177 cell_tag = self._cells[i][j]
178 self._canvas.itemconfig(cell_tag, fill=color)
179 if (i,j) == self._selected_cell:
180 self._canvas.itemconfig(cell_tag, outline='#00ffff',
181 width=3)
182 self._canvas.tag_raise(cell_tag)
183 else:
184 self._canvas.itemconfig(cell_tag, outline='black',
185 width=1)
186
187
188 edges = list(self._chart.select(span=self._selected_cell))
189 self._list.set(edges)
190
191
192 self._num_edges = self._chart.num_edges()
193 if self._numedges_label is not None:
194 self._numedges_label['text'] = '%d edges' % self._num_edges
195
197 self._canvas.itemconfig('inactivebox', state='hidden')
198 self.update()
199
201 self._canvas.itemconfig('inactivebox', state='normal')
202 self.update()
203
205 self._callbacks.setdefault(event,{})[func] = 1
206
208 if func is None: del self._callbacks[event]
209 else:
210 try: del self._callbacks[event][func]
211 except: pass
212
214 if not self._callbacks.has_key(event): return
215 for cb_func in self._callbacks[event].keys(): cb_func(*args)
216
218 if self._root is None: return
219
220
221
222 if ((i,j) == self._selected_cell and
223 self._chart.num_edges() == self._num_edges): return
224
225 self._selected_cell = (i,j)
226 self.update()
227
228
229 self._fire_callbacks('select_cell', i, j)
230
232 if self._root is None: return
233 self._selected_cell = None
234 self._list.set([])
235 self.update()
236
242
246
251
253 if self._root is None: return
254 self._list.unmark(edge)
255
260
262 if self._root is None: return
263 LEFT_MARGIN = BOT_MARGIN = 15
264 TOP_MARGIN = 5
265 c = self._canvas
266 c.delete('all')
267 N = self._chart.num_leaves()+1
268 dx = (int(c['width'])-LEFT_MARGIN)/N
269 dy = (int(c['height'])-TOP_MARGIN-BOT_MARGIN)/N
270
271 c.delete('all')
272
273
274 for i in range(N):
275 c.create_text(LEFT_MARGIN-2, i*dy+dy/2+TOP_MARGIN,
276 text=`i`, anchor='e')
277 c.create_text(i*dx+dx/2+LEFT_MARGIN, N*dy+TOP_MARGIN+1,
278 text=`i`, anchor='n')
279 c.create_line(LEFT_MARGIN, dy*(i+1)+TOP_MARGIN,
280 dx*N+LEFT_MARGIN, dy*(i+1)+TOP_MARGIN, dash='.')
281 c.create_line(dx*i+LEFT_MARGIN, TOP_MARGIN,
282 dx*i+LEFT_MARGIN, dy*N+TOP_MARGIN, dash='.')
283
284
285 c.create_rectangle(LEFT_MARGIN, TOP_MARGIN,
286 LEFT_MARGIN+dx*N, dy*N+TOP_MARGIN,
287 width=2)
288
289
290 self._cells = [[None for i in range(N)] for j in range(N)]
291 for i in range(N):
292 for j in range(i, N):
293 t = c.create_rectangle(j*dx+LEFT_MARGIN, i*dy+TOP_MARGIN,
294 (j+1)*dx+LEFT_MARGIN,
295 (i+1)*dy+TOP_MARGIN,
296 fill='gray20')
297 self._cells[i][j] = t
298 def cb(event, self=self, i=i, j=j): self._click_cell(i,j)
299 c.tag_bind(t, '<Button-1>', cb)
300
301
302 xmax, ymax = int(c['width']), int(c['height'])
303 t = c.create_rectangle(-100, -100, xmax+100, ymax+100,
304 fill='gray50', state='hidden',
305 tag='inactivebox')
306 c.tag_lower(t)
307
308
309 self.update()
310
311 - def pack(self, *args, **kwargs):
313
314
315
316
317
319 - def __init__(self, parent, chart, grammar, toplevel=True):
320 self._chart = chart
321 self._grammar = grammar
322 self._trees = []
323 self._y = 10
324 self._treewidgets = []
325 self._selection = None
326 self._selectbox = None
327
328 if toplevel:
329 self._root = Tkinter.Toplevel(parent)
330 self._root.title('Chart Parser Application: Results')
331 self._root.bind('<Control-q>', self.destroy)
332 else:
333 self._root = Tkinter.Frame(parent)
334
335
336 if toplevel:
337 buttons = Tkinter.Frame(self._root)
338 buttons.pack(side='bottom', expand=0, fill='x')
339 Tkinter.Button(buttons, text='Quit',
340 command=self.destroy).pack(side='right')
341 Tkinter.Button(buttons, text='Print All',
342 command=self.print_all).pack(side='left')
343 Tkinter.Button(buttons, text='Print Selection',
344 command=self.print_selection).pack(side='left')
345
346
347 self._cframe = CanvasFrame(self._root, closeenough=20)
348 self._cframe.pack(side='top', expand=1, fill='both')
349
350
351 self.update()
352
354 if self._root is None: return
355
356 if edge is not None:
357 if edge.lhs() != self._grammar.start(): return
358 if edge.span() != (0, self._chart.num_leaves()): return
359
360 for parse in self._chart.parses(self._grammar.start()):
361 if parse not in self._trees:
362 self._add(parse)
363
364 - def _add(self, parse):
381
383 c = self._cframe.canvas()
384 if self._selection is not None:
385 c.delete(self._selectbox)
386 self._selection = widget
387 (x1, y1, x2, y2) = widget.bbox()
388 self._selectbox = c.create_rectangle(x1, y1, x2, y2,
389 width=2, outline='#088')
390
391 - def _color(self, treewidget, color):
398
400 if self._root is None: return
401 self._cframe.print_to_file()
402
404 if self._root is None: return
405 if self._selection is None:
406 tkMessageBox.showerror('Print Error', 'No tree selected')
407 else:
408 c = self._cframe.canvas()
409 for widget in self._treewidgets:
410 if widget is not self._selection:
411 self._cframe.destroy_widget(widget)
412 c.delete(self._selectbox)
413 (x1,y1,x2,y2) = self._selection.bbox()
414 self._selection.move(10-x1,10-y1)
415 c['scrollregion'] = '0 0 %s %s' % (x2-x1+20, y2-y1+20)
416 self._cframe.print_to_file()
417
418
419 self._treewidgets = [self._selection]
420 self.clear()
421 self.update()
422
424 if self._root is None: return
425 for treewidget in self._treewidgets:
426 self._cframe.destroy_widget(treewidget)
427 self._trees = []
428 self._treewidgets = []
429 if self._selection is not None:
430 self._cframe.canvas().delete(self._selectbox)
431 self._selection = None
432 self._y = 10
433
438
443
445 if self._root is None: return
446 try: self._root.destroy()
447 except: pass
448 self._root = None
449
450 - def pack(self, *args, **kwargs):
452
453
454
455
456
458 """
459
460 @ivar _root: The root window
461
462 @ivar _charts: A dictionary mapping names to charts. When
463 charts are loaded, they are added to this dictionary.
464
465 @ivar _left_chart: The left L{Chart}.
466 @ivar _left_name: The name C{_left_chart} (derived from filename)
467 @ivar _left_matrix: The L{ChartMatrixView} for C{_left_chart}
468 @ivar _left_selector: The drop-down C{MutableOptionsMenu} used
469 to select C{_left_chart}.
470
471 @ivar _right_chart: The right L{Chart}.
472 @ivar _right_name: The name C{_right_chart} (derived from filename)
473 @ivar _right_matrix: The L{ChartMatrixView} for C{_right_chart}
474 @ivar _right_selector: The drop-down C{MutableOptionsMenu} used
475 to select C{_right_chart}.
476
477 @ivar _out_chart: The out L{Chart}.
478 @ivar _out_name: The name C{_out_chart} (derived from filename)
479 @ivar _out_matrix: The L{ChartMatrixView} for C{_out_chart}
480 @ivar _out_label: The label for C{_out_chart}.
481
482 @ivar _op_label: A Label containing the most recent operation.
483 """
484
485 _OPSYMBOL = {'-': '-',
486 'and': SymbolWidget.SYMBOLS['intersection'],
487 'or': SymbolWidget.SYMBOLS['union']}
488
490
491
492 faketok = [''] * 8
493 self._emptychart = Chart(faketok)
494
495
496 self._left_name = 'None'
497 self._right_name = 'None'
498 self._left_chart = self._emptychart
499 self._right_chart = self._emptychart
500
501
502 self._charts = {'None': self._emptychart}
503
504
505 self._out_chart = self._emptychart
506
507
508 self._operator = None
509
510
511 self._root = Tkinter.Tk()
512 self._root.title('Chart Comparison')
513 self._root.bind('<Control-q>', self.destroy)
514 self._root.bind('<Control-x>', self.destroy)
515
516
517 self._init_menubar(self._root)
518 self._init_chartviews(self._root)
519 self._init_divider(self._root)
520 self._init_buttons(self._root)
521 self._init_bindings(self._root)
522
523
524 for filename in chart_filenames:
525 self.load_chart(filename)
526
528 if self._root is None: return
529 try: self._root.destroy()
530 except: pass
531 self._root = None
532
533 - def mainloop(self, *args, **kwargs):
534 return
535 self._root.mainloop(*args, **kwargs)
536
537
538
539
540
542 menubar = Tkinter.Menu(root)
543
544
545 filemenu = Tkinter.Menu(menubar, tearoff=0)
546 filemenu.add_command(label='Load Chart', accelerator='Ctrl-o',
547 underline=0, command=self.load_chart_dialog)
548 filemenu.add_command(label='Save Output', accelerator='Ctrl-s',
549 underline=0, command=self.save_chart_dialog)
550 filemenu.add_separator()
551 filemenu.add_command(label='Exit', underline=1,
552 command=self.destroy, accelerator='Ctrl-x')
553 menubar.add_cascade(label='File', underline=0, menu=filemenu)
554
555
556 opmenu = Tkinter.Menu(menubar, tearoff=0)
557 opmenu.add_command(label='Intersection',
558 command=self._intersection,
559 accelerator='+')
560 opmenu.add_command(label='Union',
561 command=self._union,
562 accelerator='*')
563 opmenu.add_command(label='Difference',
564 command=self._difference,
565 accelerator='-')
566 opmenu.add_separator()
567 opmenu.add_command(label='Swap Charts',
568 command=self._swapcharts)
569 menubar.add_cascade(label='Compare', underline=0, menu=opmenu)
570
571
572 self._root.config(menu=menubar)
573
575 divider = Tkinter.Frame(root, border=2, relief='sunken')
576 divider.pack(side='top', fill='x', ipady=2)
577
579 opfont=('symbol', -36)
580 eqfont=('helvetica', -36)
581
582 frame = Tkinter.Frame(root, background='#c0c0c0')
583 frame.pack(side='top', expand=1, fill='both')
584
585
586 cv1_frame = Tkinter.Frame(frame, border=3, relief='groove')
587 cv1_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
588 self._left_selector = MutableOptionMenu(
589 cv1_frame, self._charts.keys(), command=self._select_left)
590 self._left_selector.pack(side='top', pady=5, fill='x')
591 self._left_matrix = ChartMatrixView(cv1_frame, self._emptychart,
592 toplevel=False,
593 show_numedges=True)
594 self._left_matrix.pack(side='bottom', padx=5, pady=5,
595 expand=1, fill='both')
596 self._left_matrix.add_callback('select', self.select_edge)
597 self._left_matrix.add_callback('select_cell', self.select_cell)
598 self._left_matrix.inactivate()
599
600
601 self._op_label = Tkinter.Label(frame, text=' ', width=3,
602 background='#c0c0c0', font=opfont)
603 self._op_label.pack(side='left', padx=5, pady=5)
604
605
606 cv2_frame = Tkinter.Frame(frame, border=3, relief='groove')
607 cv2_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
608 self._right_selector = MutableOptionMenu(
609 cv2_frame, self._charts.keys(), command=self._select_right)
610 self._right_selector.pack(side='top', pady=5, fill='x')
611 self._right_matrix = ChartMatrixView(cv2_frame, self._emptychart,
612 toplevel=False,
613 show_numedges=True)
614 self._right_matrix.pack(side='bottom', padx=5, pady=5,
615 expand=1, fill='both')
616 self._right_matrix.add_callback('select', self.select_edge)
617 self._right_matrix.add_callback('select_cell', self.select_cell)
618 self._right_matrix.inactivate()
619
620
621 Tkinter.Label(frame, text='=', width=3, background='#c0c0c0',
622 font=eqfont).pack(side='left', padx=5, pady=5)
623
624
625 out_frame = Tkinter.Frame(frame, border=3, relief='groove')
626 out_frame.pack(side='left', padx=8, pady=7, expand=1, fill='both')
627 self._out_label = Tkinter.Label(out_frame, text='Output')
628 self._out_label.pack(side='top', pady=9)
629 self._out_matrix = ChartMatrixView(out_frame, self._emptychart,
630 toplevel=False,
631 show_numedges=True)
632 self._out_matrix.pack(side='bottom', padx=5, pady=5,
633 expand=1, fill='both')
634 self._out_matrix.add_callback('select', self.select_edge)
635 self._out_matrix.add_callback('select_cell', self.select_cell)
636 self._out_matrix.inactivate()
637
653
657
658
659
660
661
662
669
676
678 if self._operator == '-': self._difference()
679 elif self._operator == 'or': self._union()
680 elif self._operator == 'and': self._intersection()
681
682
683
684
685
686 CHART_FILE_TYPES = [('Pickle file', '.pickle'),
687 ('All files', '*')]
688
690 filename = asksaveasfilename(filetypes=self.CHART_FILE_TYPES,
691 defaultextension='.pickle')
692 if not filename: return
693 try: pickle.dump((self._out_chart), open(filename, 'w'))
694 except Exception, e:
695 tkMessageBox.showerror('Error Saving Chart',
696 'Unable to open file: %r\n%s' %
697 (filename, e))
698
708
724
726 self._left_matrix.update()
727 self._right_matrix.update()
728 self._out_matrix.update()
729
730
731
732
733
735 if edge in self._left_chart:
736 self._left_matrix.markonly_edge(edge)
737 else:
738 self._left_matrix.unmark_edge()
739 if edge in self._right_chart:
740 self._right_matrix.markonly_edge(edge)
741 else:
742 self._right_matrix.unmark_edge()
743 if edge in self._out_chart:
744 self._out_matrix.markonly_edge(edge)
745 else:
746 self._out_matrix.unmark_edge()
747
752
753
754
755
756
758 if not self._checkcompat(): return
759
760 out_chart = Chart(self._left_chart.tokens())
761 for edge in self._left_chart:
762 if edge not in self._right_chart:
763 out_chart.insert(edge, [])
764
765 self._update('-', out_chart)
766
768 if not self._checkcompat(): return
769
770 out_chart = Chart(self._left_chart.tokens())
771 for edge in self._left_chart:
772 if edge in self._right_chart:
773 out_chart.insert(edge, [])
774
775 self._update('and', out_chart)
776
778 if not self._checkcompat(): return
779
780 out_chart = Chart(self._left_chart.tokens())
781 for edge in self._left_chart:
782 out_chart.insert(edge, [])
783 for edge in self._right_chart:
784 out_chart.insert(edge, [])
785
786 self._update('or', out_chart)
787
789 left, right = self._left_name, self._right_name
790 self._left_selector.set(right)
791 self._right_selector.set(left)
792
794 if (self._left_chart.tokens() != self._right_chart.tokens() or
795 self._left_chart.property_names() !=
796 self._right_chart.property_names() or
797 self._left_chart == self._emptychart or
798 self._right_chart == self._emptychart):
799
800 self._out_chart = self._emptychart
801 self._out_matrix.set_chart(self._out_chart)
802 self._out_matrix.inactivate()
803 self._out_label['text'] = 'Output'
804
805 return False
806 else:
807 return True
808
809 - def _update(self, operator, out_chart):
810 self._operator = operator
811 self._op_label['text'] = self._OPSYMBOL[operator]
812 self._out_chart = out_chart
813 self._out_matrix.set_chart(out_chart)
814 self._out_label['text'] = '%s %s %s' % (self._left_name,
815 self._operator,
816 self._right_name)
817
819 self._out_chart = self._emptychart
820 self._out_matrix.set_chart(self._out_chart)
821 self._op_label['text'] = ' '
822 self._out_matrix.inactivate()
823
827
828
829
830
831
832
833
834
835
836
837
838
840 """
841 A component for viewing charts. This is used by C{ChartParserApp} to
842 allow students to interactively experiment with various chart
843 parsing techniques. It is also used by C{Chart.draw()}.
844
845 @ivar _chart: The chart that we are giving a view of. This chart
846 may be modified; after it is modified, you should call
847 C{update}.
848 @ivar _sentence: The list of tokens that the chart spans.
849
850 @ivar _root: The root window.
851 @ivar _chart_canvas: The canvas we're using to display the chart
852 itself.
853 @ivar _tree_canvas: The canvas we're using to display the tree
854 that each edge spans. May be None, if we're not displaying
855 trees.
856 @ivar _sentence_canvas: The canvas we're using to display the sentence
857 text. May be None, if we're not displaying the sentence text.
858 @ivar _edgetags: A dictionary mapping from edges to the tags of
859 the canvas elements (lines, etc) used to display that edge.
860 The values of this dictionary have the form
861 C{(linetag, rhstag1, dottag, rhstag2, lhstag)}.
862 @ivar _treetags: A list of all the tags that make up the tree;
863 used to erase the tree (without erasing the loclines).
864 @ivar _chart_height: The height of the chart canvas.
865 @ivar _sentence_height: The height of the sentence canvas.
866 @ivar _tree_height: The height of the tree
867
868 @ivar _text_height: The height of a text string (in the normal
869 font).
870
871 @ivar _edgelevels: A list of edges at each level of the chart (the
872 top level is the 0th element). This list is used to remember
873 where edges should be drawn; and to make sure that no edges
874 are overlapping on the chart view.
875
876 @ivar _unitsize: Pixel size of one unit (from the location). This
877 is determined by the span of the chart's location, and the
878 width of the chart display canvas.
879
880 @ivar _fontsize: The current font size
881
882 @ivar _marks: A dictionary from edges to marks. Marks are
883 strings, specifying colors (e.g. 'green').
884 """
885
886 _LEAF_SPACING = 10
887 _MARGIN = 10
888 _TREE_LEVEL_SIZE = 12
889 _CHART_LEVEL_SIZE = 40
890
891 - def __init__(self, chart, root=None, **kw):
892 """
893 Construct a new C{Chart} display.
894 """
895
896 draw_tree = kw.get('draw_tree', 0)
897 draw_sentence = kw.get('draw_sentence', 1)
898 self._fontsize = kw.get('fontsize', -12)
899
900
901 self._chart = chart
902
903
904 self._callbacks = {}
905
906
907 self._edgelevels = []
908 self._edgetags = {}
909
910
911 self._marks = {}
912
913
914
915 self._treetoks = []
916 self._treetoks_edge = None
917 self._treetoks_index = 0
918
919
920 self._tree_tags = []
921
922
923 self._compact = 0
924
925
926 if root is None:
927 top = Tkinter.Tk()
928 top.title('Chart View')
929 def destroy1(e, top=top): top.destroy()
930 def destroy2(top=top): top.destroy()
931 top.bind('q', destroy1)
932 b = Tkinter.Button(top, text='Done', command=destroy2)
933 b.pack(side='bottom')
934 self._root = top
935 else:
936 self._root = root
937
938
939 self._init_fonts(root)
940
941
942 (self._chart_sb, self._chart_canvas) = self._sb_canvas(self._root)
943 self._chart_canvas['height'] = 300
944 self._chart_canvas['closeenough'] = 15
945
946
947 if draw_sentence:
948 cframe = Tkinter.Frame(self._root, relief='sunk', border=2)
949 cframe.pack(fill='both', side='bottom')
950 self._sentence_canvas = Tkinter.Canvas(cframe, height=50)
951 self._sentence_canvas['background'] = '#e0e0e0'
952 self._sentence_canvas.pack(fill='both')
953
954 else:
955 self._sentence_canvas = None
956
957
958 if draw_tree:
959 (sb, canvas) = self._sb_canvas(self._root, 'n', 'x')
960 (self._tree_sb, self._tree_canvas) = (sb, canvas)
961 self._tree_canvas['height'] = 200
962 else:
963 self._tree_canvas = None
964
965
966 self._analyze()
967 self.draw()
968 self._resize()
969 self._grow()
970
971
972
973 self._chart_canvas.bind('<Configure>', self._configure)
974
976 self._boldfont = tkFont.Font(family='helvetica', weight='bold',
977 size=self._fontsize)
978 self._font = tkFont.Font(family='helvetica',
979 size=self._fontsize)
980
981 self._sysfont = tkFont.Font(font=Tkinter.Button()["font"])
982 root.option_add("*Font", self._sysfont)
983
984 - def _sb_canvas(self, root, expand='y',
985 fill='both', side='bottom'):
986 """
987 Helper for __init__: construct a canvas with a scrollbar.
988 """
989 cframe =Tkinter.Frame(root, relief='sunk', border=2)
990 cframe.pack(fill=fill, expand=expand, side=side)
991 canvas = Tkinter.Canvas(cframe, background='#e0e0e0')
992
993
994 sb = Tkinter.Scrollbar(cframe, orient='vertical')
995 sb.pack(side='right', fill='y')
996 canvas.pack(side='left', fill=fill, expand='yes')
997
998
999 sb['command']= canvas.yview
1000 canvas['yscrollcommand'] = sb.set
1001
1002 return (sb, canvas)
1003
1006
1009
1010 - def page_up(self, *e):
1011 self._chart_canvas.yview('scroll', -1, 'pages')
1012
1013 - def page_down(self, *e):
1014 self._chart_canvas.yview('scroll', 1, 'pages')
1015
1017 """
1018 Grow the window, if necessary
1019 """
1020
1021 N = self._chart.num_leaves()
1022 width = max(int(self._chart_canvas['width']),
1023 N * self._unitsize + ChartView._MARGIN * 2 )
1024
1025
1026
1027 self._chart_canvas.configure(width=width)
1028 self._chart_canvas.configure(height=self._chart_canvas['height'])
1029
1030 self._unitsize = (width - 2*ChartView._MARGIN) / N
1031
1032
1033 if self._sentence_canvas is not None:
1034 self._sentence_canvas['height'] = self._sentence_height
1035
1043
1045 return abs(self._fontsize)
1046
1057
1058 - def update(self, chart=None):
1059 """
1060 Draw any edges that have not been drawn. This is typically
1061 called when a after modifies the canvas that a CanvasView is
1062 displaying. C{update} will cause any edges that have been
1063 added to the chart to be drawn.
1064
1065 If update is given a C{chart} argument, then it will replace
1066 the current chart with the given chart.
1067 """
1068 if chart is not None:
1069 self._chart = chart
1070 self._edgelevels = []
1071 self._marks = {}
1072 self._analyze()
1073 self._grow()
1074 self.draw()
1075 self.erase_tree()
1076 self._resize()
1077 else:
1078 for edge in self._chart:
1079 if not self._edgetags.has_key(edge):
1080 self._add_edge(edge)
1081 self._resize()
1082
1083
1085 """
1086 Return 1 if the given edge overlaps with any edge on the given
1087 level. This is used by _add_edge to figure out what level a
1088 new edge should be added to.
1089 """
1090 (s1, e1) = edge.span()
1091 for otheredge in self._edgelevels[lvl]:
1092 (s2, e2) = otheredge.span()
1093 if (s1 <= s2 < e1) or (s2 <= s1 < e2) or (s1==s2==e1==e2):
1094 return 1
1095 return 0
1096
1098 """
1099 Given a new edge, recalculate:
1100
1101 - _text_height
1102 - _unitsize (if the edge text is too big for the current
1103 _unitsize, then increase _unitsize)
1104 """
1105 c = self._chart_canvas
1106
1107 if isinstance(edge, TreeEdge):
1108 lhs = edge.lhs()
1109 rhselts = []
1110 for elt in edge.rhs():
1111 if isinstance(elt, Nonterminal):
1112 rhselts.append(str(elt.symbol()))
1113 else:
1114 rhselts.append(repr(elt))
1115 rhs = string.join(rhselts)
1116 else:
1117 lhs = edge.lhs()
1118 rhs = ''
1119
1120 for s in (lhs, rhs):
1121 tag = c.create_text(0,0, text=s,
1122 font=self._boldfont,
1123 anchor='nw', justify='left')
1124 bbox = c.bbox(tag)
1125 c.delete(tag)
1126 width = bbox[2]
1127 edgelen = max(edge.length(), 1)
1128 self._unitsize = max(self._unitsize, width/edgelen)
1129 self._text_height = max(self._text_height, bbox[3] - bbox[1])
1130
1132 """
1133 Add a single edge to the ChartView:
1134
1135 - Call analyze_edge to recalculate display parameters
1136 - Find an available level
1137 - Call _draw_edge
1138 """
1139
1140 if isinstance(edge, LeafEdge): return
1141
1142 if self._edgetags.has_key(edge): return
1143 self._analyze_edge(edge)
1144 self._grow()
1145
1146 if not self._compact:
1147 self._edgelevels.append([edge])
1148 lvl = len(self._edgelevels)-1
1149 self._draw_edge(edge, lvl)
1150 self._resize()
1151 return
1152
1153
1154 lvl = 0
1155 while 1:
1156
1157 while lvl >= len(self._edgelevels):
1158 self._edgelevels.append([])
1159 self._resize()
1160
1161
1162 if lvl>=minlvl and not self._edge_conflict(edge, lvl):
1163
1164 self._edgelevels[lvl].append(edge)
1165 break
1166
1167
1168 lvl += 1
1169
1170 self._draw_edge(edge, lvl)
1171
1173 level = None
1174 for i in range(len(self._edgelevels)):
1175 if edge in self._edgelevels[i]:
1176 level = i
1177 break
1178 if level == None: return
1179
1180 y = (level+1) * self._chart_level_size
1181 dy = self._text_height + 10
1182 self._chart_canvas.yview('moveto', 1.0)
1183 if self._chart_height != 0:
1184 self._chart_canvas.yview('moveto',
1185 float(y-dy)/self._chart_height)
1186
1188 """
1189 Draw a single edge on the ChartView.
1190 """
1191 c = self._chart_canvas
1192
1193
1194 x1 = (edge.start() * self._unitsize + ChartView._MARGIN)
1195 x2 = (edge.end() * self._unitsize + ChartView._MARGIN)
1196 if x2 == x1: x2 += max(4, self._unitsize/5)
1197 y = (lvl+1) * self._chart_level_size
1198 linetag = c.create_line(x1, y, x2, y, arrow='last', width=3)
1199
1200
1201 if isinstance(edge, TreeEdge):
1202 rhs = []
1203 for elt in edge.rhs():
1204 if isinstance(elt, Nonterminal):
1205 rhs.append(str(elt.symbol()))
1206 else:
1207 rhs.append(repr(elt))
1208 pos = edge.dot()
1209 else:
1210 rhs = []
1211 pos = 0
1212
1213 rhs1 = string.join(rhs[:pos])
1214 rhs2 = string.join(rhs[pos:])
1215 rhstag1 = c.create_text(x1+3, y, text=rhs1,
1216 font=self._font,
1217 anchor='nw')
1218 dotx = c.bbox(rhstag1)[2] + 6
1219 doty = (c.bbox(rhstag1)[1]+c.bbox(rhstag1)[3])/2
1220 dottag = c.create_oval(dotx-2, doty-2, dotx+2, doty+2)
1221 rhstag2 = c.create_text(dotx+6, y, text=rhs2,
1222 font=self._font,
1223 anchor='nw')
1224 lhstag = c.create_text((x1+x2)/2, y, text=str(edge.lhs()),
1225 anchor='s',
1226 font=self._boldfont)
1227
1228
1229 self._edgetags[edge] = (linetag, rhstag1,
1230 dottag, rhstag2, lhstag)
1231
1232
1233 def cb(event, self=self, edge=edge):
1234 self._fire_callbacks('select', edge)
1235 c.tag_bind(rhstag1, '<Button-1>', cb)
1236 c.tag_bind(rhstag2, '<Button-1>', cb)
1237 c.tag_bind(linetag, '<Button-1>', cb)
1238 c.tag_bind(dottag, '<Button-1>', cb)
1239 c.tag_bind(lhstag, '<Button-1>', cb)
1240
1241 self._color_edge(edge)
1242
1243 - def _color_edge(self, edge, linecolor=None, textcolor=None):
1244 """
1245 Color in an edge with the given colors.
1246 If no colors are specified, use intelligent defaults
1247 (dependant on selection, etc.)
1248 """
1249 if not self._edgetags.has_key(edge): return
1250 c = self._chart_canvas
1251
1252 if linecolor is not None and textcolor is not None:
1253 if self._marks.has_key(edge):
1254 linecolor = self._marks[edge]
1255 tags = self._edgetags[edge]
1256 c.itemconfig(tags[0], fill=linecolor)
1257 c.itemconfig(tags[1], fill=textcolor)
1258 c.itemconfig(tags[2], fill=textcolor,
1259 outline=textcolor)
1260 c.itemconfig(tags[3], fill=textcolor)
1261 c.itemconfig(tags[4], fill=textcolor)
1262 return
1263 else:
1264 N = self._chart.num_leaves()
1265 if self._marks.has_key(edge):
1266 self._color_edge(self._marks[edge])
1267 if (edge.is_complete() and edge.span() == (0, N)):
1268 self._color_edge(edge, '#084', '#042')
1269 elif isinstance(edge, LeafEdge):
1270 self._color_edge(edge, '#48c', '#246')
1271 else:
1272 self._color_edge(edge, '#00f', '#008')
1273
1275 """
1276 Mark an edge
1277 """
1278 self._marks[edge] = mark
1279 self._color_edge(edge)
1280
1282 """
1283 Unmark an edge (or all edges)
1284 """
1285 if edge == None:
1286 old_marked_edges = self._marks.keys()
1287 self._marks = {}
1288 for edge in old_marked_edges:
1289 self._color_edge(edge)
1290 else:
1291 del self._marks[edge]
1292 self._color_edge(edge)
1293
1297
1299 """
1300 Analyze the sentence string, to figure out how big a unit needs
1301 to be, How big the tree should be, etc.
1302 """
1303
1304 unitsize = 70
1305 text_height = 0
1306 c = self._chart_canvas
1307
1308
1309 for leaf in self._chart.leaves():
1310 tag = c.create_text(0,0, text=repr(leaf),
1311 font=self._font,
1312 anchor='nw', justify='left')
1313 bbox = c.bbox(tag)
1314 c.delete(tag)
1315 width = bbox[2] + ChartView._LEAF_SPACING
1316 unitsize = max(width, unitsize)
1317 text_height = max(text_height, bbox[3] - bbox[1])
1318
1319 self._unitsize = unitsize
1320 self._text_height = text_height
1321 self._sentence_height = (self._text_height +
1322 2*ChartView._MARGIN)
1323
1324
1325 for edge in self._chart.edges():
1326 self._analyze_edge(edge)
1327
1328
1329 self._chart_level_size = self._text_height * 2
1330
1331
1332 self._tree_height = (3 * (ChartView._TREE_LEVEL_SIZE +
1333 self._text_height))
1334
1335
1336 self._resize()
1337
1339 """
1340 Update the scroll-regions for each canvas. This ensures that
1341 everything is within a scroll-region, so the user can use the
1342 scrollbars to view the entire display. This does I{not}
1343 resize the window.
1344 """
1345 c = self._chart_canvas
1346
1347
1348 width = ( self._chart.num_leaves() * self._unitsize +
1349 ChartView._MARGIN * 2 )
1350
1351 levels = len(self._edgelevels)
1352 self._chart_height = (levels+2)*self._chart_level_size
1353 c['scrollregion']=(0,0,width,self._chart_height)
1354
1355
1356 if self._tree_canvas:
1357 self._tree_canvas['scrollregion'] = (0, 0, width,
1358 self._tree_height)
1359
1361 """
1362 Draw location lines. These are vertical gridlines used to
1363 show where each location unit is.
1364 """
1365 BOTTOM = 50000
1366 c1 = self._tree_canvas
1367 c2 = self._sentence_canvas
1368 c3 = self._chart_canvas
1369 margin = ChartView._MARGIN
1370 self._loclines = []
1371 for i in range(0, self._chart.num_leaves()+1):
1372 x = i*self._unitsize + margin
1373
1374 if c1:
1375 t1=c1.create_line(x, 0, x, BOTTOM)
1376 c1.tag_lower(t1)
1377 if c2:
1378 t2=c2.create_line(x, 0, x, self._sentence_height)
1379 c2.tag_lower(t2)
1380 t3=c3.create_line(x, 0, x, BOTTOM)
1381 c3.tag_lower(t3)
1382 t4=c3.create_text(x+2, 0, text=`i`, anchor='nw',
1383 font=self._font)
1384 c3.tag_lower(t4)
1385
1386
1387
1388
1389 if i % 2 == 0:
1390 if c1: c1.itemconfig(t1, fill='gray60')
1391 if c2: c2.itemconfig(t2, fill='gray60')
1392 c3.itemconfig(t3, fill='gray60')
1393 else:
1394 if c1: c1.itemconfig(t1, fill='gray80')
1395 if c2: c2.itemconfig(t2, fill='gray80')
1396 c3.itemconfig(t3, fill='gray80')
1397
1399 """Draw the sentence string."""
1400 if self._chart.num_leaves() == 0: return
1401 c = self._sentence_canvas
1402 margin = ChartView._MARGIN
1403 y = ChartView._MARGIN
1404
1405 for i, leaf in enumerate(self._chart.leaves()):
1406 x1 = i * self._unitsize + margin
1407 x2 = x1 + self._unitsize
1408 x = (x1+x2)/2
1409 tag = c.create_text(x, y, text=repr(leaf),
1410 font=self._font,
1411 anchor='n', justify='left')
1412 bbox = c.bbox(tag)
1413 rt=c.create_rectangle(x1+2, bbox[1]-(ChartView._LEAF_SPACING/2),
1414 x2-2, bbox[3]+(ChartView._LEAF_SPACING/2),
1415 fill='#f0f0f0', outline='#f0f0f0')
1416 c.tag_lower(rt)
1417
1419 for tag in self._tree_tags: self._tree_canvas.delete(tag)
1420 self._treetoks = []
1421 self._treetoks_edge = None
1422 self._treetoks_index = 0
1423
1425 if edge is None and self._treetoks_edge is None: return
1426 if edge is None: edge = self._treetoks_edge
1427
1428
1429 if self._treetoks_edge != edge:
1430 self._treetoks = [t for t in self._chart.trees(edge)
1431 if isinstance(t, Tree)]
1432 self._treetoks_edge = edge
1433 self._treetoks_index = 0
1434
1435
1436 if len(self._treetoks) == 0: return
1437
1438
1439 for tag in self._tree_tags: self._tree_canvas.delete(tag)
1440
1441
1442 tree = self._treetoks[self._treetoks_index]
1443 self._draw_treetok(tree, edge.start())
1444
1445
1446 self._draw_treecycle()
1447
1448
1449 w = self._chart.num_leaves()*self._unitsize+2*ChartView._MARGIN
1450 h = tree.height() * (ChartView._TREE_LEVEL_SIZE+self._text_height)
1451 self._tree_canvas['scrollregion'] = (0, 0, w, h)
1452
1454 self._treetoks_index = (self._treetoks_index+1)%len(self._treetoks)
1455 self.draw_tree(self._treetoks_edge)
1456
1458 if len(self._treetoks) <= 1: return
1459
1460
1461 label = '%d Trees' % len(self._treetoks)
1462 c = self._tree_canvas
1463 margin = ChartView._MARGIN
1464 right = self._chart.num_leaves()*self._unitsize+margin-2
1465 tag = c.create_text(right, 2, anchor='ne', text=label,
1466 font=self._boldfont)
1467 self._tree_tags.append(tag)
1468 _, _, _, y = c.bbox(tag)
1469
1470
1471 for i in range(len(self._treetoks)):
1472 x = right - 20*(len(self._treetoks)-i-1)
1473 if i == self._treetoks_index: fill = '#084'
1474 else: fill = '#fff'
1475 tag = c.create_polygon(x, y+10, x-5, y, x-10, y+10,
1476 fill=fill, outline='black')
1477 self._tree_tags.append(tag)
1478
1479
1480
1481 def cb(event, self=self, i=i):
1482 self._treetoks_index = i
1483 self.draw_tree()
1484 c.tag_bind(tag, '<Button-1>', cb)
1485
1487 """
1488 @param index: The index of the first leaf in the tree.
1489 @return: The index of the first leaf after the tree.
1490 """
1491 c = self._tree_canvas
1492 margin = ChartView._MARGIN
1493
1494
1495 child_xs = []
1496 for child in treetok:
1497 if isinstance(child, Tree):
1498 child_x, index = self._draw_treetok(child, index, depth+1)
1499 child_xs.append(child_x)
1500 else:
1501 child_xs.append((2*index+1)*self._unitsize/2 + margin)
1502 index += 1
1503
1504
1505
1506 if child_xs:
1507 nodex = sum(child_xs)/len(child_xs)
1508 else:
1509
1510 nodex = (2*index+1)*self._unitsize/2 + margin
1511 index += 1
1512
1513
1514 nodey = depth * (ChartView._TREE_LEVEL_SIZE + self._text_height)
1515 tag = c.create_text(nodex, nodey, anchor='n', justify='center',
1516 text=str(treetok.node), fill='#042',
1517 font=self._boldfont)
1518 self._tree_tags.append(tag)
1519
1520
1521 childy = nodey + ChartView._TREE_LEVEL_SIZE + self._text_height
1522 for childx, child in zip(child_xs, treetok):
1523 if isinstance(child, Tree) and child:
1524
1525 tag = c.create_line(nodex, nodey + self._text_height,
1526 childx, childy, width=2, fill='#084')
1527 self._tree_tags.append(tag)
1528 if isinstance(child, Tree) and not child:
1529
1530 tag = c.create_line(nodex, nodey + self._text_height,
1531 childx, childy, width=2,
1532 fill='#048', dash='2 3')
1533 self._tree_tags.append(tag)
1534 if not isinstance(child, Tree):
1535
1536 tag = c.create_line(nodex, nodey + self._text_height,
1537 childx, 10000, width=2, fill='#084')
1538 self._tree_tags.append(tag)
1539
1540 return nodex, index
1541
1543 """
1544 Draw everything (from scratch).
1545 """
1546 if self._tree_canvas:
1547 self._tree_canvas.delete('all')
1548 self.draw_tree()
1549
1550 if self._sentence_canvas:
1551 self._sentence_canvas.delete('all')
1552 self._draw_sentence()
1553
1554 self._chart_canvas.delete('all')
1555 self._edgetags = {}
1556
1557
1558 for lvl in range(len(self._edgelevels)):
1559 for edge in self._edgelevels[lvl]:
1560 self._draw_edge(edge, lvl)
1561
1562 for edge in self._chart:
1563 self._add_edge(edge)
1564
1565 self._draw_loclines()
1566
1568 self._callbacks.setdefault(event,{})[func] = 1
1569
1571 if func is None: del self._callbacks[event]
1572 else:
1573 try: del self._callbacks[event][func]
1574 except: pass
1575
1577 if not self._callbacks.has_key(event): return
1578 for cb_func in self._callbacks[event].keys(): cb_func(*args)
1579
1580
1581
1582
1583
1584
1585
1587 """
1588 To create an edge rule, make an empty base class that uses
1589 EdgeRule as the first base class, and the basic rule as the
1590 second base class. (Order matters!)
1591 """
1593 super = self.__class__.__bases__[1]
1594 self._edge = edge
1595 self.NUM_EDGES = super.NUM_EDGES-1
1601 super = self.__class__.__bases__[1]
1602 return super.__str__(self)
1603
1605 pass
1607 pass
1609 pass
1611 pass
1612
1613
1614
1615
1616
1618 - def __init__(self, grammar, tokens, title='Chart Parser Application'):
1619
1620 self._init_parser(grammar, tokens)
1621
1622 self._root = None
1623 try:
1624
1625 self._root = Tkinter.Tk()
1626 self._root.title(title)
1627 self._root.bind('<Control-q>', self.destroy)
1628
1629
1630 frame3 = Tkinter.Frame(self._root)
1631 frame2 = Tkinter.Frame(self._root)
1632 frame1 = Tkinter.Frame(self._root)
1633 frame3.pack(side='bottom', fill='none')
1634 frame2.pack(side='bottom', fill='x')
1635 frame1.pack(side='bottom', fill='both', expand=1)
1636
1637 self._init_fonts(self._root)
1638 self._init_animation()
1639 self._init_chartview(frame1)
1640 self._init_rulelabel(frame2)
1641 self._init_buttons(frame3)
1642 self._init_menubar()
1643
1644 self._matrix = None
1645 self._results = None
1646
1647
1648 self._init_bindings()
1649
1650 except:
1651 print 'Error creating Tree View'
1652 self.destroy()
1653 raise
1654
1656 if self._root is None: return
1657 self._root.destroy()
1658 self._root = None
1659
1660 - def mainloop(self, *args, **kwargs):
1661 """
1662 Enter the Tkinter mainloop. This function must be called if
1663 this demo is created from a non-interactive program (e.g.
1664 from a secript); otherwise, the demo will close as soon as
1665 the script completes.
1666 """
1667 if in_idle(): return
1668 self._root.mainloop(*args, **kwargs)
1669
1670
1671
1672
1673
1678
1692
1694
1695 self._sysfont = tkFont.Font(font=Tkinter.Button()["font"])
1696 root.option_add("*Font", self._sysfont)
1697
1698
1699 self._size = Tkinter.IntVar(root)
1700 self._size.set(self._sysfont.cget('size'))
1701
1702 self._boldfont = tkFont.Font(family='helvetica', weight='bold',
1703 size=self._size.get())
1704 self._font = tkFont.Font(family='helvetica',
1705 size=self._size.get())
1706
1708
1709 self._step = Tkinter.IntVar(self._root)
1710 self._step.set(1)
1711
1712
1713 self._animate = Tkinter.IntVar(self._root)
1714 self._animate.set(3)
1715
1716
1717 self._animating = 0
1718
1723
1725 ruletxt = 'Last edge generated by:'
1726
1727 self._rulelabel1 = Tkinter.Label(parent,text=ruletxt,
1728 font=self._boldfont)
1729 self._rulelabel2 = Tkinter.Label(parent, width=40,
1730 relief='groove', anchor='w',
1731 font=self._boldfont)
1732 self._rulelabel1.pack(side='left')
1733 self._rulelabel2.pack(side='left')
1734 step = Tkinter.Checkbutton(parent, variable=self._step,
1735 text='Step')
1736 step.pack(side='right')
1737
1782
1784 self._root.bind('<Up>', self._cv.scroll_up)
1785 self._root.bind('<Down>', self._cv.scroll_down)
1786 self._root.bind('<Prior>', self._cv.page_up)
1787 self._root.bind('<Next>', self._cv.page_down)
1788 self._root.bind('<Control-q>', self.destroy)
1789 self._root.bind('<Control-x>', self.destroy)
1790 self._root.bind('<F1>', self.help)
1791
1792 self._root.bind('<Control-s>', self.save_chart)
1793 self._root.bind('<Control-o>', self.load_chart)
1794 self._root.bind('<Control-r>', self.reset)
1795
1796 self._root.bind('t', self.top_down_strategy)
1797 self._root.bind('b', self.bottom_up_strategy)
1798 self._root.bind('c', self.bottom_up_leftcorner_strategy)
1799 self._root.bind('<space>', self._stop_animation)
1800
1801 self._root.bind('<Control-g>', self.edit_grammar)
1802 self._root.bind('<Control-t>', self.edit_sentence)
1803
1804
1805 self._root.bind('-', lambda e,a=self._animate:a.set(1))
1806 self._root.bind('=', lambda e,a=self._animate:a.set(2))
1807 self._root.bind('+', lambda e,a=self._animate:a.set(3))
1808
1809
1810 self._root.bind('s', lambda e,s=self._step:s.set(not s.get()))
1811
1813 menubar = Tkinter.Menu(self._root)
1814
1815 filemenu = Tkinter.Menu(menubar, tearoff=0)
1816 filemenu.add_command(label='Save Chart', underline=0,
1817 command=self.save_chart, accelerator='Ctrl-s')
1818 filemenu.add_command(label='Load Chart', underline=0,
1819 command=self.load_chart, accelerator='Ctrl-o')
1820 filemenu.add_command(label='Reset Chart', underline=0,
1821 command=self.reset, accelerator='Ctrl-r')
1822 filemenu.add_separator()
1823 filemenu.add_command(label='Save Grammar',
1824 command=self.save_grammar)
1825 filemenu.add_command(label='Load Grammar',
1826 command=self.load_grammar)
1827 filemenu.add_separator()
1828 filemenu.add_command(label='Exit', underline=1,
1829 command=self.destroy, accelerator='Ctrl-x')
1830 menubar.add_cascade(label='File', underline=0, menu=filemenu)
1831
1832 editmenu = Tkinter.Menu(menubar, tearoff=0)
1833 editmenu.add_command(label='Edit Grammar', underline=5,
1834 command=self.edit_grammar,
1835 accelerator='Ctrl-g')
1836 editmenu.add_command(label='Edit Text', underline=5,
1837 command=self.edit_sentence,
1838 accelerator='Ctrl-t')
1839 menubar.add_cascade(label='Edit', underline=0, menu=editmenu)
1840
1841 viewmenu = Tkinter.Menu(menubar, tearoff=0)
1842 viewmenu.add_command(label='Chart Matrix', underline=6,
1843 command=self.view_matrix)
1844 viewmenu.add_command(label='Results', underline=0,
1845 command=self.view_results)
1846 menubar.add_cascade(label='View', underline=0, menu=viewmenu)
1847
1848 rulemenu = Tkinter.Menu(menubar, tearoff=0)
1849 rulemenu.add_command(label='Top Down Strategy', underline=0,
1850 command=self.top_down_strategy,
1851 accelerator='t')
1852 rulemenu.add_command(label='Bottom Up Strategy', underline=0,
1853 command=self.bottom_up_strategy,
1854 accelerator='b')
1855 rulemenu.add_command(label='Bottom Up Left-Corner Strategy', underline=0,
1856 command=self.bottom_up_leftcorner_strategy,
1857 accelerator='c')
1858 rulemenu.add_separator()
1859 rulemenu.add_command(label='Bottom Up Rule',
1860 command=self.bottom_up)
1861 rulemenu.add_command(label='Bottom Up Left-Corner Rule',
1862 command=self.bottom_up_leftcorner)
1863 rulemenu.add_command(label='Top Down Init Rule',
1864 command=self.top_down_init)
1865 rulemenu.add_command(label='Top Down Predict Rule',
1866 command=self.top_down_predict)
1867 rulemenu.add_command(label='Fundamental Rule',
1868 command=self.fundamental)
1869 menubar.add_cascade(label='Apply', underline=0, menu=rulemenu)
1870
1871 animatemenu = Tkinter.Menu(menubar, tearoff=0)
1872 animatemenu.add_checkbutton(label="Step", underline=0,
1873 variable=self._step,
1874 accelerator='s')
1875 animatemenu.add_separator()
1876 animatemenu.add_radiobutton(label="No Animation", underline=0,
1877 variable=self._animate, value=0)
1878 animatemenu.add_radiobutton(label="Slow Animation", underline=0,
1879 variable=self._animate, value=1,
1880 accelerator='-')
1881 animatemenu.add_radiobutton(label="Normal Animation", underline=0,
1882 variable=self._animate, value=2,
1883 accelerator='=')
1884 animatemenu.add_radiobutton(label="Fast Animation", underline=0,
1885 variable=self._animate, value=3,
1886 accelerator='+')
1887 menubar.add_cascade(label="Animate", underline=1, menu=animatemenu)
1888
1889 zoommenu = Tkinter.Menu(menubar, tearoff=0)
1890 zoommenu.add_radiobutton(label='Tiny', variable=self._size,
1891 underline=0, value=10, command=self.resize)
1892 zoommenu.add_radiobutton(label='Small', variable=self._size,
1893 underline=0, value=12, command=self.resize)
1894 zoommenu.add_radiobutton(label='Medium', variable=self._size,
1895 underline=0, value=14, command=self.resize)
1896 zoommenu.add_radiobutton(label='Large', variable=self._size,
1897 underline=0, value=18, command=self.resize)
1898 zoommenu.add_radiobutton(label='Huge', variable=self._size,
1899 underline=0, value=24, command=self.resize)
1900 menubar.add_cascade(label='Zoom', underline=0, menu=zoommenu)
1901
1902 helpmenu = Tkinter.Menu(menubar, tearoff=0)
1903 helpmenu.add_command(label='About', underline=0,
1904 command=self.about)
1905 helpmenu.add_command(label='Instructions', underline=0,
1906 command=self.help, accelerator='F1')
1907 menubar.add_cascade(label='Help', underline=0, menu=helpmenu)
1908
1909 self._root.config(menu=menubar)
1910
1911
1912
1913
1914
1922
1923
1924
1928
1937
1945
1959
1960
1961
1962
1963
1964 - def help(self, *e):
1965 self._animating = 0
1966
1967 try:
1968 ShowText(self._root, 'Help: Chart Parser Application',
1969 (__doc__).strip(), width=75, font='fixed')
1970 except:
1971 ShowText(self._root, 'Help: Chart Parser Application',
1972 (__doc__).strip(), width=75)
1973
1975 ABOUT = ("NLTK Chart Parser Application\n"+
1976 "Written by Edward Loper")
1977 tkMessageBox.showinfo('About: Chart Parser Application', ABOUT)
1978
1979
1980
1981
1982
1983 CHART_FILE_TYPES = [('Pickle file', '.pickle'),
1984 ('All files', '*')]
1985 GRAMMAR_FILE_TYPES = [('Plaintext grammar file', '.cfg'),
1986 ('Pickle file', '.pickle'),
1987 ('All files', '*')]
1988
2006
2008 "Save a chart to a pickle file"
2009 filename = asksaveasfilename(filetypes=self.CHART_FILE_TYPES,
2010 defaultextension='.pickle')
2011 if not filename: return
2012 try:
2013 pickle.dump(self._chart, open(filename, 'w'))
2014 except Exception, e:
2015 raise
2016 tkMessageBox.showerror('Error Saving Chart',
2017 'Unable to open file: %r' % filename)
2018
2033
2035 filename = asksaveasfilename(filetypes=self.GRAMMAR_FILE_TYPES,
2036 defaultextension='.cfg')
2037 if not filename: return
2038 try:
2039 if filename.endswith('.pickle'):
2040 pickle.dump((self._chart, self._tokens), open(filename, 'w'))
2041 else:
2042 file = open(filename, 'w')
2043 prods = self._grammar.productions()
2044 start = [p for p in prods if p.lhs() == self._grammar.start()]
2045 rest = [p for p in prods if p.lhs() != self._grammar.start()]
2046 for prod in start: file.write('%s\n' % prod)
2047 for prod in rest: file.write('%s\n' % prod)
2048 file.close()
2049 except Exception, e:
2050 tkMessageBox.showerror('Error Saving Grammar',
2051 'Unable to open file: %r' % filename)
2052
2053 - def reset(self, *args):
2054 self._animating = 0
2055 self._reset_parser()
2056 self._cv.update(self._chart)
2057 if self._matrix: self._matrix.set_chart(self._chart)
2058 if self._matrix: self._matrix.deselect_cell()
2059 if self._results: self._results.set_chart(self._chart)
2060
2061
2062
2063
2064
2067
2072
2078
2082
2083
2084
2085
2086
2091
2093 if self._results is not None: self._results.destroy()
2094 self._results = ChartResultsView(self._root, self._chart,
2095 self._grammar)
2096
2097
2098
2099
2100
2104
2110
2112 return abs(self._size.get())
2113
2114
2115
2116
2117
2154
2157
2169