Home | Trees | Indices | Help |
|
---|
|
1 # Natural Language Toolkit: Graphical Representations for Trees 2 # 3 # Copyright (C) 2001-2007 University of Pennsylvania 4 # Author: Edward Loper <edloper@gradient.cis.upenn.edu> 5 # URL: <http://nltk.sf.net> 6 # For license information, see LICENSE.TXT 7 # 8 # $Id: tree.py 4107 2007-02-01 00:07:42Z stevenbird $ 9 10 """ 11 Graphically display a C{Tree}. 12 """ 13 from Tkinter import * 14 from nltk_lite.parse import tree 15 from nltk_lite.draw import * 16 import sys 17 18 ##////////////////////////////////////////////////////// 19 ## Tree Segment 20 ##////////////////////////////////////////////////////// 2123 """ 24 A canvas widget that displays a single segment of a hierarchical 25 tree. Each C{TreeSegmentWidget} connects a single X{node widget} 26 to a sequence of zero or more X{subtree widgets}. By default, the 27 bottom of the node is connected to the top of each subtree by a 28 single line. However, if the C{roof} attribute is set, then a 29 single triangular "roof" will connect the node to all of its 30 children. 31 32 Attributes: 33 - C{roof}: What sort of connection to draw between the node and 34 its subtrees. If C{roof} is true, draw a single triangular 35 "roof" over the subtrees. If C{roof} is false, draw a line 36 between each subtree and the node. Default value is false. 37 - C{xspace}: The amount of horizontal space to leave between 38 subtrees when managing this widget. Default value is 10. 39 - C{yspace}: The amount of space to place between the node and 40 its children when managing this widget. Default value is 15. 41 - C{color}: The color of the lines connecting the node to its 42 subtrees; and of the outline of the triangular roof. Default 43 value is C{'#006060'}. 44 - C{fill}: The fill color for the triangular roof. Default 45 value is C{''} (no fill). 46 - C{width}: The width of the lines connecting the node to its 47 subtrees; and of the outline of the triangular roof. Default 48 value is 1. 49 - C{orientation}: Determines whether the tree branches downwards 50 or rightwards. Possible values are C{'horizontal'} and 51 C{'vertical'}. The default value is C{'vertical'} (i.e., 52 branch downwards). 53 - C{draggable}: whether the widget can be dragged by the user. 54 55 The following attributes may also be added in the near future: 56 - C{lineM{n}_color}: The color of the line connecting the node 57 to its C{M{n}}th subtree. 58 - C{lineM{n}_color}: The width of the line connecting the node 59 to its C{M{n}}th subtree. 60 - C{lineM{n}_color}: The dash pattern of the line connecting the 61 node to its C{M{n}}th subtree. 62 63 """39765 """ 66 @type node: 67 @type subtrees: C{list} of C{CanvasWidgetI} 68 """ 69 self._node = node 70 self._subtrees = subtrees 71 72 # Attributes 73 self._horizontal = 0 74 self._roof = 0 75 self._xspace = 10 76 self._yspace = 15 77 self._ordered = False 78 79 # Create canvas objects. 80 self._lines = [canvas.create_line(0,0,0,0, fill='#006060') 81 for c in subtrees] 82 self._polygon = canvas.create_polygon(0,0, fill='', state='hidden', 83 outline='#006060') 84 85 # Register child widgets (node + subtrees) 86 self._add_child_widget(node) 87 for subtree in subtrees: 88 self._add_child_widget(subtree) 89 90 # Are we currently managing? 91 self._managing = False 92 93 CanvasWidget.__init__(self, canvas, **attribs)9496 canvas = self.canvas() 97 if attr is 'roof': 98 self._roof = value 99 if self._roof: 100 for l in self._lines: canvas.itemconfig(l, state='hidden') 101 canvas.itemconfig(self._polygon, state='normal') 102 else: 103 for l in self._lines: canvas.itemconfig(l, state='normal') 104 canvas.itemconfig(self._polygon, state='hidden') 105 elif attr == 'orientation': 106 if value == 'horizontal': self._horizontal = 1 107 elif value == 'vertical': self._horizontal = 0 108 else: 109 raise ValueError('orientation must be horizontal or vertical') 110 elif attr == 'color': 111 for l in self._lines: canvas.itemconfig(l, fill=value) 112 canvas.itemconfig(self._polygon, outline=value) 113 elif isinstance(attr, tuple) and attr[0] == 'color': 114 # Set the color of an individual line. 115 l = self._lines[int(attr[1])] 116 canvas.itemconfig(l, fill=value) 117 elif attr == 'fill': 118 canvas.itemconfig(self._polygon, fill=value) 119 elif attr == 'width': 120 canvas.itemconfig(self._polygon, {attr:value}) 121 for l in self._lines: canvas.itemconfig(l, {attr:value}) 122 elif attr in ('xspace', 'yspace'): 123 if attr == 'xspace': self._xspace = value 124 elif attr == 'yspace': self._yspace = value 125 self.update(self._node) 126 elif attr == 'ordered': 127 self._ordered = value 128 else: 129 CanvasWidget.__setitem__(self, attr, value)130132 if attr == 'roof': return self._roof 133 elif attr == 'width': 134 return self.canvas().itemcget(self._polygon, attr) 135 elif attr == 'color': 136 return self.canvas().itemcget(self._polygon, 'outline') 137 elif isinstance(attr, tuple) and attr[0] == 'color': 138 l = self._lines[int(attr[1])] 139 return self.canvas().itemcget(l, 'fill') 140 elif attr == 'xspace': return self._xspace 141 elif attr == 'yspace': return self._yspace 142 elif attr == 'orientation': 143 if self._horizontal: return 'horizontal' 144 else: return 'vertical' 145 elif attr == 'ordered': 146 return self._ordered 147 else: 148 return CanvasWidget.__getitem__(self, attr)149 152 155157 """ 158 Set the node to C{node}. 159 """ 160 self._remove_child_widget(self._node) 161 self._add_child_widget(node) 162 self._node = node 163 self.update(self._node)164166 """ 167 Replace the child C{oldchild} with C{newchild}. 168 """ 169 index = self._subtrees.index(oldchild) 170 self._subtrees[index] = newchild 171 self._remove_child_widget(oldchild) 172 self._add_child_widget(newchild) 173 self.update(newchild)174176 index = self._subtrees.index(child) 177 del self._subtrees[index] 178 self._remove_child_widget(child) 179 self.canvas().delete(self._lines.pop()) 180 self.update(self._node)181183 self._subtrees.insert(index, child) 184 self._add_child_widget(child) 185 self._lines.append(canvas.create_line(0,0,0,0, fill='#006060')) 186 self.update(self._node)187 188 # but.. lines??? 189 195197 if isinstance(child, TreeSegmentWidget): 198 bbox = child.node().bbox() 199 else: 200 bbox = child.bbox() 201 if self._horizontal: 202 return (bbox[0], (bbox[1]+bbox[3])/2.0) 203 else: 204 return ((bbox[0]+bbox[2])/2.0, bbox[1])205207 bbox = self._node.bbox() 208 if self._horizontal: 209 return (bbox[2], (bbox[1]+bbox[3])/2.0) 210 else: 211 return ((bbox[0]+bbox[2])/2.0, bbox[3])212214 if len(self._subtrees) == 0: return 215 if self._node.bbox() is None: return # [XX] ??? 216 217 # Which lines need to be redrawn? 218 if child is self._node: need_update = self._subtrees 219 else: need_update = [child] 220 221 if self._ordered and not self._managing: 222 need_update = self._maintain_order(child) 223 224 # Update the polygon. 225 (nodex, nodey) = self._node_bottom() 226 (xmin, ymin, xmax, ymax) = self._subtrees[0].bbox() 227 for subtree in self._subtrees[1:]: 228 bbox = subtree.bbox() 229 xmin = min(xmin, bbox[0]) 230 ymin = min(ymin, bbox[1]) 231 xmax = max(xmax, bbox[2]) 232 ymax = max(ymax, bbox[3]) 233 234 if self._horizontal: 235 self.canvas().coords(self._polygon, nodex, nodey, xmin, 236 ymin, xmin, ymax, nodex, nodey) 237 else: 238 self.canvas().coords(self._polygon, nodex, nodey, xmin, 239 ymin, xmax, ymin, nodex, nodey) 240 241 # Redraw all lines that need it. 242 for subtree in need_update: 243 (nodex, nodey) = self._node_bottom() 244 line = self._lines[self._subtrees.index(subtree)] 245 (subtreex, subtreey) = self._subtree_top(subtree) 246 self.canvas().coords(line, nodex, nodey, subtreex, subtreey)247249 if self._horizontal: 250 return self._maintain_order_horizontal(child) 251 else: 252 return self._maintain_order_vertical(child)253255 (left, top, right, bot) = child.bbox() 256 257 if child is self._node: 258 # Check all the leaves 259 for subtree in self._subtrees: 260 (x1, y1, x2, y2) = subtree.bbox() 261 if bot+self._yspace > y1: 262 subtree.move(0,bot+self._yspace-y1) 263 264 return self._subtrees 265 else: 266 moved = [child] 267 index = self._subtrees.index(child) 268 269 # Check leaves to our right. 270 x = right + self._xspace 271 for i in range(index+1, len(self._subtrees)): 272 (x1, y1, x2, y2) = self._subtrees[i].bbox() 273 if x > x1: 274 self._subtrees[i].move(x-x1, 0) 275 x += x2-x1 + self._xspace 276 moved.append(self._subtrees[i]) 277 278 # Check leaves to our left. 279 x = left - self._xspace 280 for i in range(index-1, -1, -1): 281 (x1, y1, x2, y2) = self._subtrees[i].bbox() 282 if x < x2: 283 self._subtrees[i].move(x-x2, 0) 284 x -= x2-x1 + self._xspace 285 moved.append(self._subtrees[i]) 286 287 # Check the node 288 (x1, y1, x2, y2) = self._node.bbox() 289 if y2 > top-self._yspace: 290 self._node.move(0, top-self._yspace-y2) 291 moved = self._subtrees 292 293 # Return a list of the nodes we moved 294 return moved295297 (left, top, right, bot) = child.bbox() 298 299 if child is self._node: 300 # Check all the leaves 301 for subtree in self._subtrees: 302 (x1, y1, x2, y2) = subtree.bbox() 303 if right+self._xspace > x1: 304 subtree.move(right+self._xspace-x1) 305 306 return self._subtrees 307 else: 308 moved = [child] 309 index = self._subtrees.index(child) 310 311 # Check leaves below us. 312 y = bot + self._yspace 313 for i in range(index+1, len(self._subtrees)): 314 (x1, y1, x2, y2) = self._subtrees[i].bbox() 315 if y > y1: 316 self._subtrees[i].move(0, y-y1) 317 y += y2-y1 + self._yspace 318 moved.append(self._subtrees[i]) 319 320 # Check leaves above us 321 y = top - self._yspace 322 for i in range(index-1, -1, -1): 323 (x1, y1, x2, y2) = self._subtrees[i].bbox() 324 if y < y2: 325 self._subtrees[i].move(0, y-y2) 326 y -= y2-y1 + self._yspace 327 moved.append(self._subtrees[i]) 328 329 # Check the node 330 (x1, y1, x2, y2) = self._node.bbox() 331 if x2 > left-self._xspace: 332 self._node.move(left-self._xspace-x2, 0) 333 moved = self._subtrees 334 335 # Return a list of the nodes we moved 336 return moved337339 (nodex, nodey) = self._node_bottom() 340 341 # Put the subtrees in a line. 342 y = 20 343 for subtree in self._subtrees: 344 subtree_bbox = subtree.bbox() 345 dx = nodex - subtree_bbox[0] + self._xspace 346 dy = y - subtree_bbox[1] 347 subtree.move(dx, dy) 348 y += subtree_bbox[3] - subtree_bbox[1] + self._yspace 349 350 # Find the center of their tops. 351 center = 0.0 352 for subtree in self._subtrees: 353 center += self._subtree_top(subtree)[1] 354 center /= len(self._subtrees) 355 356 # Center the subtrees with the node. 357 for subtree in self._subtrees: 358 subtree.move(0, nodey-center)359361 (nodex, nodey) = self._node_bottom() 362 363 # Put the subtrees in a line. 364 x = 0 365 for subtree in self._subtrees: 366 subtree_bbox = subtree.bbox() 367 dy = nodey - subtree_bbox[1] + self._yspace 368 dx = x - subtree_bbox[0] 369 subtree.move(dx, dy) 370 x += subtree_bbox[2] - subtree_bbox[0] + self._xspace 371 372 # Find the center of their tops. 373 center = 0.0 374 for subtree in self._subtrees: 375 center += self._subtree_top(subtree)[0]/len(self._subtrees) 376 377 # Center the subtrees with the node. 378 for subtree in self._subtrees: 379 subtree.move(nodex-center, 0)380382 self._managing = True 383 (nodex, nodey) = self._node_bottom() 384 if len(self._subtrees) == 0: return 385 386 if self._horizontal: self._manage_horizontal() 387 else: self._manage_vertical() 388 389 # Update lines to subtrees. 390 for subtree in self._subtrees: 391 self._update(subtree) 392 393 self._managing = False394398 -def _tree_to_treeseg(canvas, t, make_node, make_leaf, 399 tree_attribs, node_attribs, 400 leaf_attribs, loc_attribs):401 if isinstance(t, tree.Tree): 402 node = make_node(canvas, t.node, **node_attribs) 403 subtrees = [_tree_to_treeseg(canvas, child, make_node, make_leaf, 404 tree_attribs, node_attribs, 405 leaf_attribs, loc_attribs) 406 for child in t] 407 return TreeSegmentWidget(canvas, node, subtrees, **tree_attribs) 408 else: 409 return make_leaf(canvas, t, **leaf_attribs)410413 """ 414 Convert a C{Tree} into a C{TreeSegmentWidget}. 415 416 @param make_node: A C{CanvasWidget} constructor or a function that 417 creates C{CanvasWidgets}. C{make_node} is used to convert 418 the C{Tree}'s nodes into C{CanvasWidgets}. If no constructor 419 is specified, then C{TextWidget} will be used. 420 @param make_leaf: A C{CanvasWidget} constructor or a function that 421 creates C{CanvasWidgets}. C{make_leaf} is used to convert 422 the C{Tree}'s leafs into C{CanvasWidgets}. If no constructor 423 is specified, then C{TextWidget} will be used. 424 @param attribs: Attributes for the canvas widgets that make up the 425 returned C{TreeSegmentWidget}. Any attribute beginning with 426 C{'tree_'} will be passed to all C{TreeSegmentWidget}s (with 427 the C{'tree_'} prefix removed. Any attribute beginning with 428 C{'node_'} will be passed to all nodes. Any attribute 429 beginning with C{'leaf_'} will be passed to all leaves. And 430 any attribute beginning with C{'loc_'} will be passed to all 431 text locations (for C{Tree}s). 432 """ 433 # Process attribs. 434 tree_attribs = {} 435 node_attribs = {} 436 leaf_attribs = {} 437 loc_attribs = {} 438 439 for (key, value) in attribs.items(): 440 if key[:5] == 'tree_': tree_attribs[key[5:]] = value 441 elif key[:5] == 'node_': node_attribs[key[5:]] = value 442 elif key[:5] == 'leaf_': leaf_attribs[key[5:]] = value 443 elif key[:4] == 'loc_': loc_attribs[key[4:]] = value 444 else: raise ValueError('Bad attribute: %s' % key) 445 return _tree_to_treeseg(canvas, t, make_node, make_leaf, 446 tree_attribs, node_attribs, 447 leaf_attribs, loc_attribs)448 449 ##////////////////////////////////////////////////////// 450 ## Tree Widget 451 ##////////////////////////////////////////////////////// 452454 """ 455 A canvas widget that displays a single C{Tree}. 456 C{TreeWidget} manages a group of C{TreeSegmentWidget}s that are 457 used to display a C{Tree}. 458 459 Attributes: 460 461 - C{node_M{attr}}: Sets the attribute C{M{attr}} on all of the 462 node widgets for this C{TreeWidget}. 463 - C{node_M{attr}}: Sets the attribute C{M{attr}} on all of the 464 leaf widgets for this C{TreeWidget}. 465 - C{loc_M{attr}}: Sets the attribute C{M{attr}} on all of the 466 location widgets for this C{TreeWidget} (if it was built from 467 a C{Tree}). Note that location widgets are 468 C{TextWidget}s. 469 470 - C{xspace}: The amount of horizontal space to leave between 471 subtrees when managing this widget. Default value is 10. 472 - C{yspace}: The amount of space to place between the node and 473 its children when managing this widget. Default value is 15. 474 475 - C{line_color}: The color of the lines connecting each expanded 476 node to its subtrees. 477 - C{roof_color}: The color of the outline of the triangular roof 478 for collapsed trees. 479 - C{roof_fill}: The fill color for the triangular roof for 480 collapsed trees. 481 - C{width} 482 483 - C{orientation}: Determines whether the tree branches downwards 484 or rightwards. Possible values are C{'horizontal'} and 485 C{'vertical'}. The default value is C{'vertical'} (i.e., 486 branch downwards). 487 488 - C{shapeable}: whether the subtrees can be independantly 489 dragged by the user. THIS property simply sets the 490 C{DRAGGABLE} property on all of the C{TreeWidget}'s tree 491 segments. 492 - C{draggable}: whether the widget can be dragged by the user. 493 """753 754 ##////////////////////////////////////////////////////// 755 ## draw_trees 756 ##////////////////////////////////////////////////////// 757496 # Node & leaf canvas widget constructors 497 self._make_node = make_node 498 self._make_leaf = make_leaf 499 self._tree = t 500 501 # Attributes. 502 self._nodeattribs = {} 503 self._leafattribs = {} 504 self._locattribs = {'color': '#008000'} 505 self._line_color = '#008080' 506 self._line_width = 1 507 self._roof_color = '#008080' 508 self._roof_fill = '#c0c0c0' 509 self._shapeable = False 510 self._xspace = 10 511 self._yspace = 10 512 self._orientation = 'vertical' 513 self._ordered = False 514 515 # Build trees. 516 self._keys = {} # treeseg -> key 517 self._expanded_trees = {} 518 self._collapsed_trees = {} 519 self._nodes = [] 520 self._leaves = [] 521 #self._locs = [] 522 self._make_collapsed_trees(canvas, t, ()) 523 self._treeseg = self._make_expanded_tree(canvas, t, ()) 524 self._add_child_widget(self._treeseg) 525 526 CanvasWidget.__init__(self, canvas, **attribs)527529 """ 530 Return the C{TreeSegmentWidget} for the specified subtree. 531 532 @param path_to_tree: A list of indices i1, i2, ..., in, where 533 the desired widget is the widget corresponding to 534 C{tree.children()[i1].children()[i2]....children()[in]}. 535 For the root, the path is C{()}. 536 """ 537 return self._expanded_trees[path_to_tree]538540 """ 541 Return the C{TreeSegmentWidget} for the specified subtree. 542 543 @param path_to_tree: A list of indices i1, i2, ..., in, where 544 the desired widget is the widget corresponding to 545 C{tree.children()[i1].children()[i2]....children()[in]}. 546 For the root, the path is C{()}. 547 """ 548 return self._collapsed_trees[path_to_tree]549551 """ 552 Add a binding to all tree segments. 553 """ 554 for tseg in self._expanded_trees.values(): 555 tseg.bind_click(callback, button) 556 for tseg in self._collapsed_trees.values(): 557 tseg.bind_click(callback, button)558560 """ 561 Add a binding to all tree segments. 562 """ 563 for tseg in self._expanded_trees.values(): 564 tseg.bind_drag(callback, button) 565 for tseg in self._collapsed_trees.values(): 566 tseg.bind_drag(callback, button)567569 """ 570 Add a binding to all leaves. 571 """ 572 for leaf in self._leaves: leaf.bind_click(callback, button) 573 for leaf in self._leaves: leaf.bind_click(callback, button)574576 """ 577 Add a binding to all leaves. 578 """ 579 for leaf in self._leaves: leaf.bind_drag(callback, button) 580 for leaf in self._leaves: leaf.bind_drag(callback, button)581583 """ 584 Add a binding to all nodes. 585 """ 586 for node in self._nodes: node.bind_click(callback, button) 587 for node in self._nodes: node.bind_click(callback, button)588590 """ 591 Add a binding to all nodes. 592 """ 593 for node in self._nodes: node.bind_drag(callback, button) 594 for node in self._nodes: node.bind_drag(callback, button)595597 if not isinstance(t, tree.Tree): return 598 make_node = self._make_node 599 make_leaf = self._make_leaf 600 601 node = make_node(canvas, t.node, **self._nodeattribs) 602 self._nodes.append(node) 603 leaves = [make_leaf(canvas, l, **self._leafattribs) 604 for l in t.leaves()] 605 self._leaves += leaves 606 treeseg = TreeSegmentWidget(canvas, node, leaves, roof=1, 607 color=self._roof_color, 608 fill=self._roof_fill, 609 width=self._line_width) 610 611 self._collapsed_trees[key] = treeseg 612 self._keys[treeseg] = key 613 #self._add_child_widget(treeseg) 614 treeseg.hide() 615 616 # Build trees for children. 617 for i in range(len(t)): 618 child = t[i] 619 self._make_collapsed_trees(canvas, child, key + (i,))620622 make_node = self._make_node 623 make_leaf = self._make_leaf 624 625 if isinstance(t, tree.Tree): 626 node = make_node(canvas, t.node, **self._nodeattribs) 627 self._nodes.append(node) 628 children = t 629 subtrees = [self._make_expanded_tree(canvas, children[i], key+(i,)) 630 for i in range(len(children))] 631 treeseg = TreeSegmentWidget(canvas, node, subtrees, 632 color=self._line_color, 633 width=self._line_width) 634 self._expanded_trees[key] = treeseg 635 self._keys[treeseg] = key 636 return treeseg 637 else: 638 leaf = make_leaf(canvas, t, **self._leafattribs) 639 self._leaves.append(leaf) 640 return leaf641643 if attr[:5] == 'node_': 644 for node in self._nodes: node[attr[5:]] = value 645 elif attr[:5] == 'leaf_': 646 for leaf in self._leaves: leaf[attr[5:]] = value 647 elif attr == 'line_color': 648 self._line_color = value 649 for tseg in self._expanded_trees.values(): tseg['color'] = value 650 elif attr == 'line_width': 651 self._line_width = value 652 for tseg in self._expanded_trees.values(): tseg['width'] = value 653 for tseg in self._collapsed_trees.values(): tseg['width'] = value 654 elif attr == 'roof_color': 655 self._roof_color = value 656 for tseg in self._collapsed_trees.values(): tseg['color'] = value 657 elif attr == 'roof_fill': 658 self._roof_fill = value 659 for tseg in self._collapsed_trees.values(): tseg['fill'] = value 660 elif attr == 'shapeable': 661 self._shapeable = value 662 for tseg in self._expanded_trees.values(): 663 tseg['draggable'] = value 664 for tseg in self._collapsed_trees.values(): 665 tseg['draggable'] = value 666 for leaf in self._leaves: leaf['draggable'] = value 667 elif attr == 'xspace': 668 self._xspace = value 669 for tseg in self._expanded_trees.values(): 670 tseg['xspace'] = value 671 for tseg in self._collapsed_trees.values(): 672 tseg['xspace'] = value 673 self.manage() 674 elif attr == 'yspace': 675 self._yspace = value 676 for tseg in self._expanded_trees.values(): 677 tseg['yspace'] = value 678 for tseg in self._collapsed_trees.values(): 679 tseg['yspace'] = value 680 self.manage() 681 elif attr == 'orientation': 682 self._orientation = value 683 for tseg in self._expanded_trees.values(): 684 tseg['orientation'] = value 685 for tseg in self._collapsed_trees.values(): 686 tseg['orientation'] = value 687 self.manage() 688 elif attr == 'ordered': 689 self._ordered = value 690 for tseg in self._expanded_trees.values(): 691 tseg['ordered'] = value 692 for tseg in self._collapsed_trees.values(): 693 tseg['ordered'] = value 694 else: CanvasWidget.__setitem__(self, attr, value)695697 if attr[:5] == 'node_': 698 return self._nodeattribs.get(attr[5:], None) 699 elif attr[:5] == 'leaf_': 700 return self._leafattribs.get(attr[5:], None) 701 elif attr[:4] == 'loc_': 702 return self._locattribs.get(attr[4:], None) 703 elif attr == 'line_color': return self._line_color 704 elif attr == 'line_width': return self._line_width 705 elif attr == 'roof_color': return self._roof_color 706 elif attr == 'roof_fill': return self._roof_fill 707 elif attr == 'shapeable': return self._shapeable 708 elif attr == 'xspace': return self._xspace 709 elif attr == 'yspace': return self._yspace 710 elif attr == 'orientation': return self._orientation 711 else: return CanvasWidget.__getitem__(self, attr)712 714716 segs = self._expanded_trees.values() + self._collapsed_trees.values() 717 for tseg in segs: 718 if tseg.hidden(): 719 tseg.show() 720 tseg.manage() 721 tseg.hide()722724 """ 725 Collapse/expand a tree. 726 """ 727 old_treeseg = treeseg 728 if old_treeseg['roof']: 729 new_treeseg = self._expanded_trees[self._keys[old_treeseg]] 730 else: 731 new_treeseg = self._collapsed_trees[self._keys[old_treeseg]] 732 733 # Replace the old tree with the new tree. 734 if old_treeseg.parent() is self: 735 self._remove_child_widget(old_treeseg) 736 self._add_child_widget(new_treeseg) 737 self._treeseg = new_treeseg 738 else: 739 old_treeseg.parent().replace_child(old_treeseg, new_treeseg) 740 741 # Move the new tree to where the old tree was. Show it first, 742 # so we can find its bounding box. 743 new_treeseg.show() 744 (newx, newy) = new_treeseg.node().bbox()[:2] 745 (oldx, oldy) = old_treeseg.node().bbox()[:2] 746 new_treeseg.move(oldx-newx, oldy-newy) 747 748 # Hide the old tree 749 old_treeseg.hide() 750 751 # We could do parent.manage() here instead, if we wanted. 752 new_treeseg.parent().update(new_treeseg)864 874 875 ##////////////////////////////////////////////////////// 876 ## Demo Code 877 ##////////////////////////////////////////////////////// 878 883 884 cf = CanvasFrame(width=550, height=450, closeenough=2) 885 886 t = tree.bracket_parse(''' 887 (S (NP the very big cat) 888 (VP (Adv sorta) (V saw) (NP (Det the) (N dog))))''') 889 890 tc = TreeWidget(cf.canvas(), t, draggable=1, 891 node_font=('helvetica', -14, 'bold'), 892 leaf_font=('helvetica', -12, 'italic'), 893 roof_fill='white', roof_color='black', 894 leaf_color='green4', node_color='blue2') 895 cf.add_widget(tc,10,10) 896 897 def boxit(canvas, text): 898 big = ('helvetica', -16, 'bold') 899 return BoxWidget(canvas, TextWidget(canvas, text, 900 font=big), fill='green') 901 def ovalit(canvas, text): 902 return OvalWidget(canvas, TextWidget(canvas, text), 903 fill='cyan') 904 905 treetok = tree.bracket_parse('(S (NP this tree) (VP (V is) (AdjP shapeable)))') 906 tc2 = TreeWidget(cf.canvas(), treetok, boxit, ovalit, shapeable=1) 907 908 def color(node): 909 node['color'] = '#%04d00' % random.randint(0,9999) 910 def color2(treeseg): 911 treeseg.node()['fill'] = '#%06d' % random.randint(0,9999) 912 treeseg.node().child()['color'] = 'white' 913 914 tc.bind_click_trees(tc.toggle_collapsed) 915 tc2.bind_click_trees(tc2.toggle_collapsed) 916 tc.bind_click_nodes(color, 3) 917 tc2.expanded_tree(1).bind_click(color2, 3) 918 tc2.expanded_tree().bind_click(color2, 3) 919 920 paren = ParenWidget(cf.canvas(), tc2) 921 cf.add_widget(paren, tc.bbox()[2]+10, 10) 922 923 tree3 = tree.bracket_parse(''' 924 (S (NP this tree) (AUX was) 925 (VP (V built) (PP (P with) (NP (N tree_to_treesegment)))))''') 926 tc3 = tree_to_treesegment(cf.canvas(), tree3, tree_color='green4', 927 tree_xspace=2, tree_width=2) 928 tc3['draggable'] = 1 929 cf.add_widget(tc3, 10, tc.bbox()[3]+10) 930 931 def orientswitch(treewidget): 932 if treewidget['orientation'] == 'horizontal': 933 treewidget.expanded_tree(1,1).subtrees()[0].set_text('vertical') 934 treewidget.collapsed_tree(1,1).subtrees()[0].set_text('vertical') 935 treewidget.collapsed_tree(1).subtrees()[1].set_text('vertical') 936 treewidget.collapsed_tree().subtrees()[3].set_text('vertical') 937 treewidget['orientation'] = 'vertical' 938 else: 939 treewidget.expanded_tree(1,1).subtrees()[0].set_text('horizontal') 940 treewidget.collapsed_tree(1,1).subtrees()[0].set_text('horizontal') 941 treewidget.collapsed_tree(1).subtrees()[1].set_text('horizontal') 942 treewidget.collapsed_tree().subtrees()[3].set_text('horizontal') 943 treewidget['orientation'] = 'horizontal' 944 945 text = """ 946 Try clicking, right clicking, and dragging 947 different elements of each of the trees. 948 The top-left tree is a TreeWidget built from 949 a Tree. The top-right is a TreeWidget built 950 from a Tree, using non-default widget 951 constructors for the nodes & leaves (BoxWidget 952 and OvalWidget). The bottom-left tree is 953 built from tree_to_treesegment.""" 954 twidget = TextWidget(cf.canvas(), text.strip()) 955 textbox = BoxWidget(cf.canvas(), twidget, fill='white', draggable=1) 956 cf.add_widget(textbox, tc3.bbox()[2]+10, tc2.bbox()[3]+10) 957 958 tree4 = tree.bracket_parse('(S (NP this tree) (VP (V is) (Adj horizontal)))') 959 tc4 = TreeWidget(cf.canvas(), tree4, draggable=1, 960 line_color='brown2', roof_color='brown2', 961 node_font=('helvetica', -12, 'bold'), 962 node_color='brown4', orientation='horizontal') 963 tc4.manage() 964 cf.add_widget(tc4, tc3.bbox()[2]+10, textbox.bbox()[3]+10) 965 tc4.bind_click(orientswitch) 966 tc4.bind_click_trees(tc4.toggle_collapsed, 3) 967 968 # Run mainloop 969 cf.mainloop() 970 971 if __name__ == '__main__': 972 demo() 973760 from nltk_lite.draw import CanvasFrame 761 from math import sqrt, ceil 762 763 self._trees = trees 764 765 self._top = Tk() 766 self._top.title('NLTK') 767 self._top.bind('<Control-x>', self.destroy) 768 self._top.bind('<Control-q>', self.destroy) 769 770 cf = self._cframe = CanvasFrame(self._top) 771 self._top.bind('<Control-p>', self._cframe.print_to_file) 772 773 # Size is variable. 774 self._size = IntVar(self._top) 775 self._size.set(12) 776 bold = ('helvetica', -self._size.get(), 'bold') 777 helv = ('helvetica', -self._size.get()) 778 779 # Lay the trees out in a square. 780 self._width = int(ceil(sqrt(len(trees)))) 781 self._widgets = [] 782 for i in range(len(trees)): 783 widget = TreeWidget(cf.canvas(), trees[i], node_font=bold, 784 leaf_color='#008040', node_color='#004080', 785 roof_color='#004040', roof_fill='white', 786 line_color='#004040', draggable=1, 787 leaf_font=helv) 788 widget.bind_click_trees(widget.toggle_collapsed) 789 self._widgets.append(widget) 790 cf.add_widget(widget, 0, 0) 791 792 self._layout() 793 self._cframe.pack(expand=1, fill='both') 794 self._init_menubar()795797 i = x = y = ymax = 0 798 width = self._width 799 for i in range(len(self._widgets)): 800 widget = self._widgets[i] 801 (oldx, oldy) = widget.bbox()[:2] 802 if i % width == 0: 803 y = ymax 804 x = 0 805 widget.move(x-oldx, y-oldy) 806 x = widget.bbox()[2] + 10 807 ymax = max(ymax, widget.bbox()[3] + 10)808 834836 bold = ('helvetica', -self._size.get(), 'bold') 837 helv = ('helvetica', -self._size.get()) 838 xspace = self._size.get() 839 yspace = self._size.get() 840 for widget in self._widgets: 841 widget['node_font'] = bold 842 widget['leaf_font'] = helv 843 widget['xspace'] = xspace 844 widget['yspace'] = yspace 845 if self._size.get() < 20: widget['line_width'] = 1 846 elif self._size.get() < 30: widget['line_width'] = 2 847 else: widget['line_width'] = 3 848 self._layout()849 854
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0beta1 on Wed May 16 22:47:43 2007 | http://epydoc.sourceforge.net |