| Home | Trees | Indices | Help |
|
|---|
|
|
1 # Natural Language Toolkit: graphical representations package
2 #
3 # Copyright (C) 2001 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: bigwidgets.py 3006 2008-06-19 21:16:02Z pynadath $
9
10 """
11 Tools for graphically displaying and interacting with the objects and
12 processing classes defined by the Toolkit. These tools are primarily
13 intended to help students visualize the objects that they create.
14
15 The graphical tools are typically built using X{canvas widgets}, each
16 of which encapsulates the graphical elements and bindings used to
17 display a complex object on a Tkinter C{Canvas}. For example, NLTK
18 defines canvas widgets for displaying trees and directed graphs, as
19 well as a number of simpler widgets. These canvas widgets make it
20 easier to build new graphical tools and demos. See the class
21 documentation for C{CanvasWidget} for more information.
22
23 The C{nltk.draw} module defines the abstract C{CanvasWidget} base
24 class, and a number of simple canvas widgets. The remaining canvas
25 widgets are defined by submodules, such as C{nltk.draw.tree}.
26
27 The C{nltk.draw} module also defines C{CanvasFrame}, which
28 encapsulates a C{Canvas} and its scrollbars. It uses a
29 C{ScrollWatcherWidget} to ensure that all canvas widgets contained on
30 its canvas are within the scroll region.
31
32 Acknowledgements: Many of the ideas behind the canvas widget system
33 are derived from C{CLIG}, a Tk-based grapher for linguistic data
34 structures. For more information, see the CLIG homepage at::
35
36 http://www.ags.uni-sb.de/~konrad/clig.html
37
38 """
39
40 from Tkinter import *
41
42 __all__ = (
43 'CanvasFrame', 'CanvasWidget',
44 'TextWidget', 'SymbolWidget','ImageWidget',
45 'BoxWidget', 'OvalWidget', 'ParenWidget', 'BracketWidget', 'PolygonWidget',
46 'SequenceWidget', 'StackWidget', 'SpaceWidget',
47 'ScrollWatcherWidget', 'AbstractContainerWidget',
48
49 'ShowText')
50
51 # Including these causes circular dependancy trouble???
52 # 'tree', 'chart', 'fas', 'srparser', 'plot',
53 # 'plot_graph', 'tree_edit'
54 # )
55
56 ##//////////////////////////////////////////////////////
57 ## CanvasWidget
58 ##//////////////////////////////////////////////////////
59
61 """
62 A collection of graphical elements and bindings used to display a
63 complex object on a Tkinter C{Canvas}. A canvas widget is
64 responsible for managing the C{Canvas} tags and callback bindings
65 necessary to display and interact with the object. Canvas widgets
66 are often organized into hierarchies, where parent canvas widgets
67 control aspects of their child widgets.
68
69 Each canvas widget is bound to a single C{Canvas}. This C{Canvas}
70 is specified as the first argument to the C{CanvasWidget}'s
71 constructor.
72
73 Attributes
74 ==========
75 Each canvas widget can support a variety of X{attributes}, which
76 control how the canvas widget is displayed. Some typical examples
77 attributes are C{color}, C{font}, and C{radius}. Each attribute
78 has a default value. This default value can be overridden in the
79 constructor, using keyword arguments of the form
80 C{attribute=value}:
81
82 >>> cn = CanvasText(c, 'test', color='red')
83
84 Attribute values can also be changed after a canvas widget has
85 been constructed, using the C{__setitem__} operator:
86
87 >>> cn['font'] = 'times'
88
89 The current value of an attribute value can be queried using the
90 C{__getitem__} operator:
91
92 >>> cn['color']
93 red
94
95 For a list of the attributes supported by a type of canvas widget,
96 see its class documentation.
97
98 Interaction
99 ===========
100 The attribute C{'draggable'} controls whether the user can drag a
101 canvas widget around the canvas. By default, canvas widgets
102 are not draggable.
103
104 C{CanvasWidget} provides callback support for two types of user
105 interaction: clicking and dragging. The method C{bind_click}
106 registers a callback function that is called whenever the canvas
107 widget is clicked. The method C{bind_drag} registers a callback
108 function that is called after the canvas widget is dragged. If
109 the user clicks or drags a canvas widget with no registered
110 callback function, then the interaction event will propagate to
111 its parent. For each canvas widget, only one callback function
112 may be registered for an interaction event. Callback functions
113 can be deregistered with the C{unbind_click} and C{unbind_drag}
114 methods.
115
116 Subclassing
117 ===========
118 C{CanvasWidget} is an abstract class. Subclasses are required to
119 implement the following methods:
120
121 - C{__init__}: Builds a new canvas widget. It must perform the
122 following three tasks (in order):
123 - Create any new graphical elements.
124 - Call C{_add_child_widget} on each child widget.
125 - Call the C{CanvasWidget} constructor.
126 - C{_tags}: Returns a list of the canvas tags for all graphical
127 elements managed by this canvas widget, not including
128 graphical elements managed by its child widgets.
129 - C{_manage}: Arranges the child widgets of this canvas widget.
130 This is typically only called when the canvas widget is
131 created.
132 - C{_update}: Update this canvas widget in response to a
133 change in a single child.
134
135 For C{CanvasWidget}s with no child widgets, the default
136 definitions for C{_manage} and C{_update} may be used.
137
138 If a subclass defines any attributes, then it should implement
139 C{__getitem__} and C{__setitem__}. If either of these methods is
140 called with an unknown attribute, then they should propagate the
141 request to C{CanvasWidget}.
142
143 Most subclasses implement a number of additional methods that
144 modify the C{CanvasWidget} in some way. These methods must call
145 C{parent.update(self)} after making any changes to the canvas
146 widget's graphical elements. The canvas widget must also call
147 C{parent.update(self)} after changing any attribute value that
148 affects the shape or position of the canvas widget's graphical
149 elements.
150
151 @type __canvas: C{Tkinter.Canvas}
152 @ivar __canvas: This C{CanvasWidget}'s canvas.
153
154 @type __parent: C{CanvasWidget} or C{None}
155 @ivar __parent: This C{CanvasWidget}'s hierarchical parent widget.
156 @type __children: C{list} of C{CanvasWidget}
157 @ivar __children: This C{CanvasWidget}'s hierarchical child widgets.
158
159 @type __updating: C{boolean}
160 @ivar __updating: Is this canvas widget currently performing an
161 update? If it is, then it will ignore any new update requests
162 from child widgets.
163
164 @type __draggable: C{boolean}
165 @ivar __draggable: Is this canvas widget draggable?
166 @type __press: C{event}
167 @ivar __press: The ButtonPress event that we're currently handling.
168 @type __drag_x: C{int}
169 @ivar __drag_x: Where it's been moved to (to find dx)
170 @type __drag_y: C{int}
171 @ivar __drag_y: Where it's been moved to (to find dy)
172 @type __callbacks: C{dictionary}
173 @ivar __callbacks: Registered callbacks. Currently, four keys are
174 used: C{1}, C{2}, C{3}, and C{'drag'}. The values are
175 callback functions. Each callback function takes a single
176 argument, which is the C{CanvasWidget} that triggered the
177 callback.
178 """
180 """
181 Create a new canvas widget. This constructor should only be
182 called by subclass constructors; and it should be called only
183 X{after} the subclass has constructed all graphical canvas
184 objects and registered all child widgets.
185
186 @param canvas: This canvas widget's canvas.
187 @type canvas: C{Tkinter.Canvas}
188 @param parent: This canvas widget's hierarchical parent.
189 @type parent: C{CanvasWidget}
190 @param attribs: The new canvas widget's attributes.
191 """
192 if not isinstance(canvas, Canvas):
193 raise TypeError('Expected a canvas!')
194
195 self.__canvas = canvas
196 self.__parent = parent
197 if not hasattr(self, '_CanvasWidget__children'): self.__children = []
198
199 self.__hidden = 0
200
201 # Update control (prevents infinite loops)
202 self.__updating = 0
203
204 # Button-press and drag callback handling.
205 self.__press = None
206 self.__drag_x = self.__drag_y = 0
207 self.__callbacks = {}
208 self.__draggable = 0
209 # dp: double-click callback handling.
210 self.__double = None
211
212 # Set up attributes.
213 for (attr, value) in attribs.items(): self[attr] = value
214
215 # Manage this canvas widget
216 self._manage()
217
218 # Register any new bindings
219 for tag in self._tags():
220 self.__canvas.tag_bind(tag, '<ButtonPress-1>',
221 self.__press_cb)
222 self.__canvas.tag_bind(tag, '<ButtonPress-2>',
223 self.__press_cb)
224 self.__canvas.tag_bind(tag, '<ButtonPress-3>',
225 self.__press_cb)
226
227 ##//////////////////////////////////////////////////////
228 ## Inherited methods.
229 ##//////////////////////////////////////////////////////
230
232 """
233 @return: A bounding box for this C{CanvasWidget}. The bounding
234 box is a tuple of four coordinates, M{(xmin, ymin, xmax,
235 ymax)}, for a rectangle which encloses all of the canvas
236 widget's graphical elements. Bounding box coordinates are
237 specified with respect to the C{Canvas}'s coordinate
238 space.
239 @rtype: C{4-tuple} of C{int}s
240 """
241 if self.__hidden: return (0,0,0,0)
242 if len(self.tags()) == 0: raise ValueError('No tags')
243 return self.__canvas.bbox(*self.tags())
244
246 """
247 @return: The width of this canvas widget's bounding box, in
248 its C{Canvas}'s coordinate space.
249 @rtype: C{int}
250 """
251 if len(self.tags()) == 0: raise ValueError('No tags')
252 bbox = self.__canvas.bbox(*self.tags())
253 return bbox[2]-bbox[0]
254
256 """
257 @return: The height of this canvas widget's bounding box, in
258 its C{Canvas}'s coordinate space.
259 @rtype: C{int}
260 """
261 if len(self.tags()) == 0: raise ValueError('No tags')
262 bbox = self.__canvas.bbox(*self.tags())
263 return bbox[3]-bbox[1]
264
266 """
267 @return: The hierarchical parent of this canvas widget.
268 C{self} is considered a subpart of its parent for
269 purposes of user interaction.
270 @rtype: C{CanvasWidget} or C{None}
271 """
272 return self.__parent
273
275 """
276 @return: A list of the hierarchical children of this canvas
277 widget. These children are considered part of C{self}
278 for purposes of user interaction.
279 @rtype: C{list} of C{CanvasWidget}
280 """
281 return self.__children
282
284 """
285 @return: The canvas that this canvas widget is bound to.
286 @rtype: C{Tkinter.Canvas}
287 """
288 return self.__canvas
289
291 """
292 Move this canvas widget by a given distance. In particular,
293 shift the canvas widget right by C{dx} pixels, and down by
294 C{dy} pixels. Both C{dx} and C{dy} may be negative, resulting
295 in leftward or upward movement.
296
297 @type dx: C{int}
298 @param dx: The number of pixels to move this canvas widget
299 rightwards.
300 @type dy: C{int}
301 @param dy: The number of pixels to move this canvas widget
302 downwards.
303 @rtype: C{None}
304 """
305 if dx == dy == 0: return
306 for tag in self.tags():
307 self.__canvas.move(tag, dx, dy)
308 if self.__parent: self.__parent.update(self)
309
311 """
312 Remove this C{CanvasWidget} from its C{Canvas}. After a
313 C{CanvasWidget} has been destroyed, it should not be accessed.
314
315 Note that you only need to destroy a top-level
316 C{CanvasWidget}; its child widgets will be destroyed
317 automatically. If you destroy a non-top-level
318 C{CanvasWidget}, then the entire top-level widget will be
319 destroyed.
320
321 @raise ValueError: if this C{CanvasWidget} has a parent.
322 @rtype: C{None}
323 """
324 if self.__parent is not None:
325 self.__parent.destroy()
326 return
327
328 for tag in self.tags():
329 self.__canvas.tag_unbind(tag, '<ButtonPress-1>')
330 self.__canvas.tag_unbind(tag, '<ButtonPress-2>')
331 self.__canvas.tag_unbind(tag, '<ButtonPress-3>')
332 self.__canvas.delete(*self.tags())
333 self.__canvas = None
334
336 """
337 Update the graphical display of this canvas widget, and all of
338 its ancestors, in response to a change in one of this canvas
339 widget's children.
340
341 @param child: The child widget that changed.
342 @type child: C{CanvasWidget}
343 """
344 if self.__hidden or child.__hidden: return
345 # If we're already updating, then do nothing. This prevents
346 # infinite loops when _update modifies its children.
347 if self.__updating: return
348 self.__updating = 1
349
350 # Update this CanvasWidget.
351 self._update(child)
352
353 # Propagate update request to the parent.
354 if self.__parent: self.__parent.update(self)
355
356 # We're done updating.
357 self.__updating = 0
358
360 """
361 Arrange this canvas widget and all of its descendants.
362
363 @rtype: C{None}
364 """
365 if self.__hidden: return
366 for child in self.__children: child.manage()
367 self._manage()
368
383
385 """
386 Set the value of the attribute C{attr} to C{value}. See the
387 class documentation for a list of attributes supported by this
388 canvas widget.
389
390 @rtype: C{None}
391 """
392 if attr == 'draggable':
393 self.__draggable = value
394 else:
395 raise ValueError('Unknown attribute %r' % attr)
396
398 """
399 @return: the value of the attribute C{attr}. See the class
400 documentation for a list of attributes supported by this
401 canvas widget.
402 @rtype: (any)
403 """
404 if attr == 'draggable':
405 return self.__draggable
406 else:
407 raise ValueError('Unknown attribute %r' % attr)
408
410 """
411 @return: a string representation of this canvas widget.
412 @rtype: C{string}
413 """
414 return '<%s>' % self.__class__.__name__
415
417 """
418 Temporarily hide this canvas widget.
419
420 @rtype: C{None}
421 """
422 self.__hidden = 1
423 for tag in self.tags():
424 self.__canvas.itemconfig(tag, state='hidden')
425
427 """
428 Show a hidden canvas widget.
429
430 @rtype: C{None}
431 """
432 self.__hidden = 0
433 for tag in self.tags():
434 self.__canvas.itemconfig(tag, state='normal')
435
442
443 ##//////////////////////////////////////////////////////
444 ## Callback interface
445 ##//////////////////////////////////////////////////////
446
448 """
449 Register a new callback that will be called whenever this
450 C{CanvasWidget} is clicked on.
451
452 @type callback: C{function}
453 @param callback: The callback function that will be called
454 whenever this C{CanvasWidget} is clicked. This function
455 will be called with the event and this C{CanvasWidget}
456 as its argument. (dp: added the event as argument)
457 @type button: C{int}
458 @param button: Which button the user should use to click on
459 this C{CanvasWidget}. Typically, this should be 1 (left
460 button), 3 (right button), or 2 (middle button).
461 """
462 self.__callbacks[button] = callback
463
465 """
466 Register a new callback that will be called after this
467 C{CanvasWidget} is dragged. This implicitly makes this
468 C{CanvasWidget} draggable.
469
470 @type callback: C{function}
471 @param callback: The callback function that will be called
472 whenever this C{CanvasWidget} is clicked. This function
473 will be called with this C{CanvasWidget} as its argument.
474 """
475 self.__draggable = 1
476 self.__callbacks['drag'] = callback
477
479 """
480 Remove a callback that was registered with C{bind_click}.
481
482 @type button: C{int}
483 @param button: Which button the user should use to click on
484 this C{CanvasWidget}. Typically, this should be 1 (left
485 button), 3 (right button), or 2 (middle button).
486 """
487 try: del self.__callbacks[button]
488 except: pass
489
491 """
492 Remove a callback that was registered with C{bind_drag}.
493 """
494 try: del self.__callbacks['drag']
495 except: pass
496
497 ##//////////////////////////////////////////////////////
498 ## Callback internals
499 ##//////////////////////////////////////////////////////
500
502 """
503 Handle a button-press event:
504 - record the button press event in C{self.__press}
505 - register a button-release callback.
506 - if this CanvasWidget or any of its ancestors are
507 draggable, then register the appropriate motion callback.
508 """
509 # If we're already waiting for a button release, then ignore
510 # this new button press.
511 if (self.__canvas.bind('<ButtonRelease-1>') or
512 self.__canvas.bind('<ButtonRelease-2>') or
513 self.__canvas.bind('<ButtonRelease-3>')):
514 return
515
516 # Unbind motion (just in case; this shouldn't be necessary)
517 self.__canvas.unbind('<Motion>')
518
519 # dp: check for double-click
520 if self.__press:
521 if abs(event.time-self.__press.time) < 1000:
522 self.__double = self.__press
523 else:
524 self.__double = None
525
526 # Record the button press event.
527 self.__press = event
528
529 # If any ancestor is draggable, set up a motion callback.
530 # (Only if they pressed button number 1)
531 if event.num == 1:
532 widget = self
533 while widget is not None:
534 if widget['draggable']:
535 widget.__start_drag(event)
536 break
537 widget = widget.parent()
538
539 # Set up the button release callback.
540 self.__canvas.bind('<ButtonRelease-%d>' % event.num,
541 self.__release_cb)
542
544 """
545 Begin dragging this object:
546 - register a motion callback
547 - record the drag coordinates
548 """
549 self.__canvas.bind('<Motion>', self.__motion_cb)
550 self.__drag_x = event.x
551 self.__drag_y = event.y
552
554 """
555 Handle a motion event:
556 - move this object to the new location
557 - record the new drag coordinates
558 """
559 self.move(event.x-self.__drag_x, event.y-self.__drag_y)
560 self.__drag_x = event.x
561 self.__drag_y = event.y
562
564 """
565 Handle a release callback:
566 - unregister motion & button release callbacks.
567 - decide whether they clicked, dragged, or cancelled
568 - call the appropriate handler.
569 """
570 # Unbind the button release & motion callbacks.
571 self.__canvas.unbind('<ButtonRelease-%d>' % event.num)
572 self.__canvas.unbind('<Motion>')
573 # Is it a click or a drag?
574 if (event.time - self.__press.time < 1000 and
575 abs(event.x-self.__press.x) + abs(event.y-self.__press.y) < 5):
576 # Move it back, if we were dragging.
577 if self.__draggable and event.num == 1:
578 self.move(self.__press.x - self.__drag_x,
579 self.__press.y - self.__drag_y)
580 self.__click(event)
581 elif event.num == 1:
582 self.__drag()
583
584 ## self.__press = None
585
587 """
588 If this C{CanvasWidget} has a drag callback, then call it;
589 otherwise, find the closest ancestor with a drag callback, and
590 call it. If no ancestors have a drag callback, do nothing.
591 """
592 if self.__draggable:
593 if self.__callbacks.has_key('drag'):
594 cb = self.__callbacks['drag']
595 ## try:
596 cb(self)
597 ## except:
598 ## print 'Error in drag callback for %r' % self
599 elif self.__parent is not None:
600 self.__parent.__drag()
601
603 """
604 If this C{CanvasWidget} has a click callback, then call it;
605 otherwise, find the closest ancestor with a click callback, and
606 call it. If no ancestors have a click callback, do nothing.
607 """
608 if self.__double:
609 if self.__callbacks.has_key('double'):
610 self.__callbacks['double'](event,self)
611 self.__double = None
612 self.__press = None
613 else:
614 self.__parent.__double = self.__double
615 self.__parent.__click(event)
616 elif self.__callbacks.has_key(event.num):
617 cb = self.__callbacks[event.num]
618 #try:
619 cb(event,self)
620 #except:
621 # print 'Error in click callback for %r' % self
622 # raise
623 elif self.__parent is not None:
624 self.__parent.__click(event)
625
626 ##//////////////////////////////////////////////////////
627 ## Child/parent Handling
628 ##//////////////////////////////////////////////////////
629
631 """
632 Register a hierarchical child widget. The child will be
633 considered part of this canvas widget for purposes of user
634 interaction. C{_add_child_widget} has two direct effects:
635 - It sets C{child}'s parent to this canvas widget.
636 - It adds C{child} to the list of canvas widgets returned by
637 the C{child_widgets} member function.
638
639 @param child: The new child widget. C{child} must not already
640 have a parent.
641 @type child: C{CanvasWidget}
642 """
643 if not hasattr(self, '_CanvasWidget__children'): self.__children = []
644 if child.__parent is not None:
645 raise ValueError('%s already has a parent', child)
646 child.__parent = self
647 self.__children.append(child)
648
650 """
651 Remove a hierarchical child widget. This child will no longer
652 be considered part of this canvas widget for purposes of user
653 interaction. C{_add_child_widget} has two direct effects:
654 - It sets C{child}'s parent to C{None}.
655 - It removes C{child} from the list of canvas widgets
656 returned by the C{child_widgets} member function.
657
658 @param child: The child widget to remove. C{child} must be a
659 child of this canvas widget.
660 @type child: C{CanvasWidget}
661 """
662 self.__children.remove(child)
663 child.__parent = None
664
665 ##//////////////////////////////////////////////////////
666 ## Defined by subclass
667 ##//////////////////////////////////////////////////////
668
677
679 """
680 Arrange the child widgets of this canvas widget. This method
681 is called when the canvas widget is initially created. It is
682 also called if the user calls the C{manage} method on this
683 canvas widget or any of its ancestors.
684
685 @rtype: C{None}
686 """
687 pass
688
699
700 ##//////////////////////////////////////////////////////
701 ## Basic widgets.
702 ##//////////////////////////////////////////////////////
703
705 """
706 A canvas widget that displays a single string of text.
707
708 Attributes:
709 - C{color}: the color of the text.
710 - C{font}: the font used to display the text.
711 - C{justify}: justification for multi-line texts. Valid values
712 are C{left}, C{center}, and C{right}.
713 - C{width}: the width of the text. If the text is wider than
714 this width, it will be line-wrapped at whitespace.
715 - C{draggable}: whether the text can be dragged by the user.
716 """
718 """
719 Create a new text widget.
720
721 @type canvas: C{Tkinter.Canvas}
722 @param canvas: This canvas widget's canvas.
723 @type text: C{string}
724 @param text: The string of text to display.
725 @param attribs: The new canvas widget's attributes.
726 """
727 self._text = text
728 self._tag = canvas.create_text(1, 1, text=text)
729 CanvasWidget.__init__(self, canvas, **attribs)
730
732 if attr in ('color', 'font', 'justify', 'width'):
733 if attr == 'color': attr = 'fill'
734 self.canvas().itemconfig(self._tag, {attr:value})
735 else:
736 CanvasWidget.__setitem__(self, attr, value)
737
739 if attr == 'width':
740 return int(self.canvas().itemcget(self._tag, attr))
741 elif attr in ('color', 'font', 'justify'):
742 if attr == 'color': attr = 'fill'
743 return self.canvas().itemcget(self._tag, attr)
744 else:
745 return CanvasWidget.__getitem__(self, attr)
746
748
750 """
751 @return: The text displayed by this text widget.
752 @rtype: C{string}
753 """
754 return self.canvas().itemcget(self._tag, 'text')
755
757 """
758 Change the text that is displayed by this text widget.
759
760 @type text: C{string}
761 @param text: The string of text to display.
762 @rtype: C{None}
763 """
764 self.canvas().itemconfig(self._tag, text=text)
765 if self.parent() is not None:
766 self.parent().update(self)
767
770
772 """
773 A canvas widget that displays special symbols, such as the
774 negation sign and the exists operator. Symbols are specified by
775 name. Currently, the following symbol names are defined: C{neg},
776 C{disj}, C{conj}, C{lambda}, C{merge}, C{forall}, C{exists},
777 C{subseteq}, C{subset}, C{notsubset}, C{emptyset}, C{imp},
778 C{rightarrow}, C{equal}, C{notequal}, C{epsilon}.
779
780 Attributes:
781 - C{color}: the color of the text.
782 - C{draggable}: whether the text can be dragged by the user.
783
784 @cvar _SYMBOLS: A dictionary mapping from symbols to the character
785 in the C{symbol} font used to render them.
786 """
787 _SYMBOLS = {'neg':'\330', 'disj':'\332', 'conj': '\331',
788 'lambda': '\154', 'merge': '\304',
789 'forall': '\042', 'exists': '\044',
790 'subseteq': '\315', 'subset': '\314',
791 'notsubset': '\313', 'emptyset': '\306',
792 'imp': '\336', 'rightarrow': '\256',
793 'equal': '\75', 'notequal': '\271',
794 'epsilon': 'e'}
795
797 """
798 Create a new symbol widget.
799
800 @type canvas: C{Tkinter.Canvas}
801 @param canvas: This canvas widget's canvas.
802 @type symbol: C{string}
803 @param symbol: The name of the symbol to display.
804 @param attribs: The new canvas widget's attributes.
805 """
806 attribs['font'] = 'symbol'
807 TextWidget.__init__(self, canvas, '', **attribs)
808 self.set_symbol(symbol)
809
811 """
812 @return: the name of the symbol that is displayed by this
813 symbol widget.
814 @rtype: C{string}
815 """
816 return self._symbol
817
819 """
820 Change the symbol that is displayed by this symbol widget.
821
822 @type symbol: C{string}
823 @param symbol: The name of the symbol to display.
824 """
825 if not SymbolWidget._SYMBOLS.has_key(symbol):
826 raise ValueError('Unknown symbol: %s' % symbol)
827 self._symbol = symbol
828 self.set_text(SymbolWidget._SYMBOLS[symbol])
829
832
834 """
835 An abstract class for canvas widgets that contain a single child,
836 such as C{CanvasBox} and C{CanvasOval}. Subclasses must define
837 a constructor, which should create any new graphical elements and
838 then call the C{AbstractCanvasContainer} constructor. Subclasses
839 must also define the C{_update} method and the C{_tags} method;
840 and any subclasses that define attributes should define
841 C{__setitem__} and C{__getitem__}.
842 """
844 """
845 Create a new container widget. This constructor should only
846 be called by subclass constructors.
847
848 @type canvas: C{Tkinter.Canvas}
849 @param canvas: This canvas widget's canvas.
850 @param child: The container's child widget. C{child} must not
851 have a parent.
852 @type child: C{CanvasWidget}
853 @param attribs: The new canvas widget's attributes.
854 """
855 self._child = child
856 self._add_child_widget(child)
857 CanvasWidget.__init__(self, canvas, **attribs)
858
860 self._update(self._child)
861
863 """
864 @return: The child widget contained by this container widget.
865 @rtype: C{CanvasWidget}
866 """
867 return self._child
868
870 """
871 Change the child widget contained by this container widget.
872
873 @param child: The new child widget. C{child} must not have a
874 parent.
875 @type child: C{CanvasWidget}
876 @rtype: C{None}
877 """
878 self._remove_child_widget(self._child)
879 self._add_child_widget(child)
880 self._child = child
881 self.update(child)
882
887
889 """
890 A canvas widget that places a box around a child widget.
891
892 Attributes:
893 - C{fill}: The color used to fill the interior of the box.
894 - C{outline}: The color used to draw the outline of the box.
895 - C{width}: The width of the outline of the box.
896 - C{margin}: The number of pixels space left between the child
897 and the box.
898 - C{draggable}: whether the text can be dragged by the user.
899 """
901 """
902 Create a new box widget.
903
904 @type canvas: C{Tkinter.Canvas}
905 @param canvas: This canvas widget's canvas.
906 @param child: The child widget. C{child} must not have a
907 parent.
908 @type child: C{CanvasWidget}
909 @param attribs: The new canvas widget's attributes.
910 """
911 self._child = child
912 self._margin = 1
913 self._box = canvas.create_rectangle(1,1,1,1)
914 canvas.tag_lower(self._box)
915 AbstractContainerWidget.__init__(self, canvas, child, **attribs)
916
918 if attr == 'margin': self._margin = value
919 elif attr in ('outline', 'fill', 'width'):
920 self.canvas().itemconfig(self._box, {attr:value})
921 else:
922 CanvasWidget.__setitem__(self, attr, value)
923
925 if attr == 'margin': return self._margin
926 elif attr == 'width':
927 return float(self.canvas().itemcget(self._box, attr))
928 elif attr in ('outline', 'fill', 'width'):
929 return self.canvas().itemcget(self._box, attr)
930 else:
931 return CanvasWidget.__getitem__(self, attr)
932
934 (x1, y1, x2, y2) = child.bbox()
935 margin = self._margin + self['width']/2
936 self.canvas().coords(self._box, x1-margin, y1-margin,
937 x2+margin, y2+margin)
938
940
942 """
943 A canvas widget that places a box around a child widget.
944
945 Attributes:
946 - C{fill}: The color used to fill the interior of the box.
947 - C{outline}: The color used to draw the outline of the box.
948 - C{width}: The width of the outline of the box.
949 - C{margin}: The number of pixels space left between the child
950 and the box.
951 - C{draggable}: whether the text can be dragged by the user.
952 """
954 """
955 Create a new box widget.
956
957 @type canvas: C{Tkinter.Canvas}
958 @param canvas: This canvas widget's canvas.
959 @param child: The child widget. C{child} must not have a
960 parent.
961 @type child: C{CanvasWidget}
962 @param attribs: The new canvas widget's attributes.
963 """
964 self._child = child
965 self._margin = 1
966 self._box = canvas.create_polygon(1,1,1,1,1,1,1,1,1,1,1,1)
967 canvas.tag_lower(self._box)
968 AbstractContainerWidget.__init__(self, canvas, child, **attribs)
969
971 if attr == 'margin': self._margin = value
972 elif attr in ('outline', 'fill', 'width'):
973 self.canvas().itemconfig(self._box, {attr:value})
974 else:
975 CanvasWidget.__setitem__(self, attr, value)
976
978 if attr == 'margin': return self._margin
979 elif attr == 'width':
980 return float(self.canvas().itemcget(self._box, attr))
981 elif attr in ('outline', 'fill', 'width'):
982 return self.canvas().itemcget(self._box, attr)
983 else:
984 return CanvasWidget.__getitem__(self, attr)
985
987 (x1, y1, x2, y2) = child.bbox()
988 margin = self._margin + self['width']/2
989 self.canvas().coords(self._box, x1-margin, y1-margin,
990 x2+margin, y1-margin,(x2+margin)+6,
991 ((y1-margin) + (y2+margin))/2,
992 x2+margin, y2+margin,
993 x1-margin,y2+margin,(x1-margin)-6,
994 ((y1-margin) + (y2+margin))/2)
995
997
999 """
1000 A canvas widget that places a oval around a child widget.
1001
1002 Attributes:
1003 - C{fill}: The color used to fill the interior of the oval.
1004 - C{outline}: The color used to draw the outline of the oval.
1005 - C{width}: The width of the outline of the oval.
1006 - C{margin}: The number of pixels space left between the child
1007 and the oval.
1008 - C{draggable}: whether the text can be dragged by the user.
1009 """
1011 """
1012 Create a new oval widget.
1013
1014 @type canvas: C{Tkinter.Canvas}
1015 @param canvas: This canvas widget's canvas.
1016 @param child: The child widget. C{child} must not have a
1017 parent.
1018 @type child: C{CanvasWidget}
1019 @param attribs: The new canvas widget's attributes.
1020 """
1021 self._child = child
1022 self._margin = 1
1023 self._oval = canvas.create_oval(1,1,1,1)
1024 canvas.tag_lower(self._oval)
1025 AbstractContainerWidget.__init__(self, canvas, child, **attribs)
1026
1028 if attr == 'margin': self._margin = value
1029 elif attr in ('outline', 'fill', 'width'):
1030 self.canvas().itemconfig(self._oval, {attr:value})
1031 else:
1032 CanvasWidget.__setitem__(self, attr, value)
1033
1035 if attr == 'margin': return self._margin
1036 elif attr == 'width':
1037 return float(self.canvas().itemcget(self._oval, attr))
1038 elif attr in ('outline', 'fill', 'width'):
1039 return self.canvas().itemcget(self._oval, attr)
1040 else:
1041 return CanvasWidget.__getitem__(self, attr)
1042
1043 # The ratio between inscribed & circumscribed ovals
1044 RATIO = 2.
1045 ## RATIO = 2.4142135623730949
1046
1048 R = OvalWidget.RATIO
1049 (x1, y1, x2, y2) = child.bbox()
1050 margin = self._margin + self['width']/2
1051 left = int(( x1*(1+R) + x2*(1-R) ) / 2 - margin)
1052 right = left + int((x2-x1)*R)
1053 top = int(( y1*(1+R) + y2*(1-R) ) / 2 - margin)
1054 bot = top + int((y2-y1)*R)
1055 self.canvas().coords(self._oval, left, top, right, bot)
1056
1058
1060 """
1061 A canvas widget that contains an image widget.
1062
1063 Attributes:
1064 - C{fill}: The color used to fill the interior of the oval.
1065 - C{outline}: The color used to draw the outline of the oval.
1066 - C{width}: The width of the outline of the oval.
1067 - C{margin}: The number of pixels space left between the child
1068 and the oval.
1069 - C{draggable}: whether the text can be dragged by the user.
1070 """
1072 """
1073 Create a new image widget.
1074
1075 @type canvas: C{Tkinter.Canvas}
1076 @param canvas: This canvas widget's canvas.
1077 @param child: The child widget. C{child} must not have a
1078 parent.
1079 @type child: C{CanvasWidget}
1080 @param attribs: The new canvas widget's attributes.
1081 """
1082 self._image = canvas.create_image(1,1,image=child)
1083 canvas.tag_lower(self._image)
1084 CanvasWidget.__init__(self, canvas, **attribs)
1085
1087
1089 """
1090 A canvas widget that places a pair of parenthases around a child
1091 widget.
1092
1093 Attributes:
1094 - C{color}: The color used to draw the parenthases.
1095 - C{width}: The width of the parenthases.
1096 - C{draggable}: whether the text can be dragged by the user.
1097 """
1099 """
1100 Create a new parenthasis widget.
1101
1102 @type canvas: C{Tkinter.Canvas}
1103 @param canvas: This canvas widget's canvas.
1104 @param child: The child widget. C{child} must not have a
1105 parent.
1106 @type child: C{CanvasWidget}
1107 @param attribs: The new canvas widget's attributes.
1108 """
1109 self._child = child
1110 self._oparen = canvas.create_arc(1,1,1,1, style='arc',
1111 start=90, extent=180)
1112 self._cparen = canvas.create_arc(1,1,1,1, style='arc',
1113 start=-90, extent=180)
1114 AbstractContainerWidget.__init__(self, canvas, child, **attribs)
1115
1117 if attr == 'color':
1118 self.canvas().itemconfig(self._oparen, outline=value)
1119 self.canvas().itemconfig(self._cparen, outline=value)
1120 elif attr == 'width':
1121 self.canvas().itemconfig(self._oparen, width=value)
1122 self.canvas().itemconfig(self._cparen, width=value)
1123 else:
1124 CanvasWidget.__setitem__(self, attr, value)
1125
1127 if attr == 'color':
1128 return self.canvas().itemcget(self._oparen, 'outline')
1129 elif attr == 'width':
1130 return self.canvas().itemcget(self._oparen, 'width')
1131 else:
1132 return CanvasWidget.__getitem__(self, attr)
1133
1135 (x1, y1, x2, y2) = child.bbox()
1136 width = max((y2-y1)/6, 4)
1137 self.canvas().coords(self._oparen, x1-width, y1, x1+width, y2)
1138 self.canvas().coords(self._cparen, x2-width, y1, x2+width, y2)
1139
1141
1143 """
1144 A canvas widget that places a pair of brackets around a child
1145 widget.
1146
1147 Attributes:
1148 - C{color}: The color used to draw the brackets.
1149 - C{width}: The width of the brackets.
1150 - C{draggable}: whether the text can be dragged by the user.
1151 """
1153 """
1154 Create a new bracket widget.
1155
1156 @type canvas: C{Tkinter.Canvas}
1157 @param canvas: This canvas widget's canvas.
1158 @param child: The child widget. C{child} must not have a
1159 parent.
1160 @type child: C{CanvasWidget}
1161 @param attribs: The new canvas widget's attributes.
1162 """
1163 self._child = child
1164 self._obrack = canvas.create_line(1,1,1,1,1,1,1,1)
1165 self._cbrack = canvas.create_line(1,1,1,1,1,1,1,1)
1166 AbstractContainerWidget.__init__(self, canvas, child, **attribs)
1167
1169 if attr == 'color':
1170 self.canvas().itemconfig(self._obrack, fill=value)
1171 self.canvas().itemconfig(self._cbrack, fill=value)
1172 elif attr == 'width':
1173 self.canvas().itemconfig(self._obrack, width=value)
1174 self.canvas().itemconfig(self._cbrack, width=value)
1175 else:
1176 CanvasWidget.__setitem__(self, attr, value)
1177
1179 if attr == 'color':
1180 return self.canvas().itemcget(self._obrack, 'outline')
1181 elif attr == 'width':
1182 return self.canvas().itemcget(self._obrack, 'width')
1183 else:
1184 return CanvasWidget.__getitem__(self, attr)
1185
1187 (x1, y1, x2, y2) = child.bbox()
1188 width = max((y2-y1)/8, 2)
1189 self.canvas().coords(self._obrack, x1, y1, x1-width, y1,
1190 x1-width, y2, x1, y2)
1191 self.canvas().coords(self._cbrack, x2, y1, x2+width, y1,
1192 x2+width, y2, x2, y2)
1193
1195
1197 """
1198 A canvas widget that keeps a list of canvas widgets in a
1199 horizontal line.
1200
1201 Attributes:
1202 - C{align}: The vertical alignment of the children. Possible
1203 values are C{'top'}, C{'center'}, and C{'bottom'}. By
1204 default, children are center-aligned.
1205 - C{space}: The amount of horizontal space to place between
1206 children. By default, one pixel of space is used.
1207 """
1209 """
1210 Create a new sequence widget.
1211
1212 @type canvas: C{Tkinter.Canvas}
1213 @param canvas: This canvas widget's canvas.
1214 @param children: The widgets that should be aligned
1215 horizontally. Each child must not have a parent.
1216 @type children: C{list} of C{CanvasWidget}
1217 @param attribs: The new canvas widget's attributes.
1218 """
1219 self._align = 'center'
1220 self._space = 1
1221 self._children = list(children)
1222 for child in children: self._add_child_widget(child)
1223 CanvasWidget.__init__(self, canvas, **attribs)
1224
1226 if attr == 'align':
1227 if value not in ('top', 'bottom', 'center'):
1228 raise ValueError('Bad alignment: %r' % value)
1229 self._align = value
1230 elif attr == 'space': self._space = value
1231 else: CanvasWidget.__setitem__(self, attr, value)
1232
1234 if attr == 'align': return value
1235 elif attr == 'space': return self._space
1236 else: return CanvasWidget.__getitem__(self, attr)
1237
1239
1243
1245 if self._align == 'top': return top
1246 if self._align == 'bottom': return bot
1247 if self._align == 'center': return (top+bot)/2
1248
1250 # Align all children with child.
1251 (left, top, right, bot) = child.bbox()
1252 y = self._yalign(top, bot)
1253
1254 index = self._children.index(child)
1255
1256 # Line up children to the right of child.
1257 x = right + self._space
1258 for i in range(index+1, len(self._children)):
1259 (x1, y1, x2, y2) = self._children[i].bbox()
1260 self._children[i].move(x-x1, y-self._yalign(y1,y2))
1261 x += x2-x1 + self._space
1262
1263 # Line up children to the left of child.
1264 x = left - self._space
1265 for i in range(index-1, -1, -1):
1266 (x1, y1, x2, y2) = self._children[i].bbox()
1267 self._children[i].move(x-x2, y-self._yalign(y1,y2))
1268 x -= x2-x1 + self._space
1269
1272
1274 """
1275 A canvas widget that keeps a list of canvas widgets in a vertical
1276 line.
1277
1278 Attributes:
1279 - C{align}: The horizontal alignment of the children. Possible
1280 values are C{'left'}, C{'center'}, and C{'right'}. By
1281 default, children are center-aligned.
1282 - C{space}: The amount of vertical space to place between
1283 children. By default, one pixel of space is used.
1284 """
1286 """
1287 Create a new stack widget.
1288
1289 @type canvas: C{Tkinter.Canvas}
1290 @param canvas: This canvas widget's canvas.
1291 @param children: The widgets that should be aligned
1292 vertically. Each child must not have a parent.
1293 @type children: C{list} of C{CanvasWidget}
1294 @param attribs: The new canvas widget's attributes.
1295 """
1296 self._align = 'center'
1297 self._space = 1
1298 self._children = list(children)
1299 for child in children: self._add_child_widget(child)
1300 CanvasWidget.__init__(self, canvas, **attribs)
1301
1303 if attr == 'align':
1304 if value not in ('left', 'right', 'center'):
1305 raise ValueError('Bad alignment: %r' % value)
1306 self._align = value
1307 elif attr == 'space': self._space = value
1308 else: CanvasWidget.__setitem__(self, attr, value)
1309
1311 if attr == 'align': return value
1312 elif attr == 'space': return self._space
1313 else: return CanvasWidget.__getitem__(self, attr)
1314
1316
1320
1322 if self._align == 'left': return left
1323 if self._align == 'right': return right
1324 if self._align == 'center': return (left+right)/2
1325
1327 # Align all children with child.
1328 (left, top, right, bot) = child.bbox()
1329 x = self._xalign(left, right)
1330
1331 index = self._children.index(child)
1332
1333 # Line up children below the child.
1334 y = bot + self._space
1335 for i in range(index+1, len(self._children)):
1336 (x1, y1, x2, y2) = self._children[i].bbox()
1337 self._children[i].move(x-self._xalign(x1,x2), y-y1)
1338 y += y2-y1 + self._space
1339
1340 # Line up children above the child.
1341 y = top - self._space
1342 for i in range(index-1, -1, -1):
1343 (x1, y1, x2, y2) = self._children[i].bbox()
1344 self._children[i].move(x-self._xalign(x1,x2), y-y2)
1345 y -= y2-y1 + self._space
1346
1349
1351 """
1352 A canvas widget that takes up space but does not display
1353 anything. C{SpaceWidget}s can be used to add space between
1354 elements. Each space widget is characterized by a width and a
1355 height. If you wish to only create horizontal space, then use a
1356 height of zero; and if you wish to only create vertical space, use
1357 a width of zero.
1358 """
1360 """
1361 Create a new space widget.
1362
1363 @type canvas: C{Tkinter.Canvas}
1364 @param canvas: This canvas widget's canvas.
1365 @type width: C{int}
1366 @param width: The width of the new space widget.
1367 @type height: C{int}
1368 @param height: The height of the new space widget.
1369 @param attribs: The new canvas widget's attributes.
1370 """
1371 # For some reason,
1372 if width > 4: width -= 4
1373 if height > 4: height -= 4
1374 self._tag = canvas.create_line(1, 1, width, height, fill='')
1375 CanvasWidget.__init__(self, canvas, **attribs)
1376
1377 # note: width() and height() are already defined by CanvasWidget.
1379 """
1380 Change the width of this space widget.
1381
1382 @param width: The new width.
1383 @type width: C{int}
1384 @rtype: C{None}
1385 """
1386 [x1, y1, x2, y2] = self._tag.bbox()
1387 self._canvas.coords(self._tag, x1, y1, x1+width, y2)
1388
1390 """
1391 Change the height of this space widget.
1392
1393 @param height: The new height.
1394 @type height: C{int}
1395 @rtype: C{None}
1396 """
1397 [x1, y1, x2, y2] = self._tag.bbox()
1398 self._canvas.coords(self._tag, x1, y1, x2, y1+height)
1399
1401
1403
1405 """
1406 A special canvas widget that adjusts its C{Canvas}'s scrollregion
1407 to always include the bounding boxes of all of its children. The
1408 scroll-watcher widget will only increase the size of the
1409 C{Canvas}'s scrollregion; it will never decrease it.
1410 """
1412 """
1413 Create a new scroll-watcher widget.
1414
1415 @type canvas: C{Tkinter.Canvas}
1416 @param canvas: This canvas widget's canvas.
1417 @type children: C{list} of C{CanvasWidget}
1418 @param children: The canvas widgets watched by the
1419 scroll-watcher. The scroll-watcher will ensure that these
1420 canvas widgets are always contained in their canvas's
1421 scrollregion.
1422 @param attribs: The new canvas widget's attributes.
1423 """
1424 for child in children: self._add_child_widget(child)
1425 CanvasWidget.__init__(self, canvas, **attribs)
1426
1428 """
1429 Add a new canvas widget to the scroll-watcher. The
1430 scroll-watcher will ensure that the new canvas widget is
1431 always contained in its canvas's scrollregion.
1432
1433 @param canvaswidget: The new canvas widget.
1434 @type canvaswidget: C{CanvasWidget}
1435 @rtype: C{None}
1436 """
1437 self._add_child_widget(canvaswidget)
1438 self.update(canvaswidget)
1439
1441 """
1442 Remove a canvas widget from the scroll-watcher. The
1443 scroll-watcher will no longer ensure that the new canvas
1444 widget is always contained in its canvas's scrollregion.
1445
1446 @param canvaswidget: The canvas widget to remove.
1447 @type canvaswidget: C{CanvasWidget}
1448 @rtype: C{None}
1449 """
1450 self._remove_child_widget(canvaswidget)
1451
1453
1455 self._adjust_scrollregion()
1456
1458 """
1459 Adjust the scrollregion of this scroll-watcher's C{Canvas} to
1460 include the bounding boxes of all of its children.
1461 """
1462 bbox = self.bbox()
1463 canvas = self.canvas()
1464 scrollregion = [int(n) for n in canvas['scrollregion'].split()]
1465 if len(scrollregion) != 4: return
1466 if (bbox[0] < scrollregion[0] or bbox[1] < scrollregion[1] or
1467 bbox[2] > scrollregion[2] or bbox[3] > scrollregion[3]):
1468 scrollregion = ('%d %d %d %d' %
1469 (min(bbox[0], scrollregion[0]),
1470 min(bbox[1], scrollregion[1]),
1471 max(bbox[2], scrollregion[2]),
1472 max(bbox[3], scrollregion[3])))
1473 canvas['scrollregion'] = scrollregion
1474
1475 ##//////////////////////////////////////////////////////
1476 ## Canvas Frame
1477 ##//////////////////////////////////////////////////////
1478
1480 """
1481 A C{Tkinter} frame containing a canvas and scrollbars.
1482 C{CanvasFrame} uses a C{ScrollWatcherWidget} to ensure that all of
1483 the canvas widgets contained on its canvas are within its
1484 scrollregion. In order for C{CanvasFrame} to make these checks,
1485 all canvas widgets must be registered with C{add_widget} when they
1486 are added to the canvas; and destroyed with C{destroy_widget} when
1487 they are no longer needed.
1488
1489 If a C{CanvasFrame} is created with no parent, then it will create
1490 its own main window, including a "Done" button and a "Print"
1491 button.
1492 """
1494 """
1495 Create a new C{CanvasFrame}.
1496
1497 @type parent: C{Tkinter.BaseWidget} or C{Tkinter.Tk}
1498 @param parent: The parent C{Tkinter} widget. If no parent is
1499 specified, then C{CanvasFrame} will create a new main
1500 window.
1501 @param kw: Keyword arguments for the new C{Canvas}. See the
1502 documentation for C{Tkinter.Canvas} for more information.
1503 """
1504 # If no parent was given, set up a top-level window.
1505 if parent is None:
1506 self._parent = Tk()
1507 self._parent.title('NLTK')
1508 self._parent.bind('q', self.destroy)
1509 else:
1510 self._parent = parent
1511
1512 # Create a frame for the canvas & scrollbars
1513 self._frame = frame = Frame(self._parent)
1514 self._canvas = canvas = Canvas(frame, **kw)
1515 xscrollbar = Scrollbar(self._frame, orient='horizontal')
1516 yscrollbar = Scrollbar(self._frame, orient='vertical')
1517 xscrollbar['command'] = canvas.xview
1518 yscrollbar['command'] = canvas.yview
1519 canvas['xscrollcommand'] = xscrollbar.set
1520 canvas['yscrollcommand'] = yscrollbar.set
1521 yscrollbar.pack(fill='y', side='right')
1522 xscrollbar.pack(fill='x', side='bottom')
1523 canvas.pack(expand=1, fill='both', side='left')
1524
1525 # Set initial scroll region.
1526 scrollregion = '0 0 %s %s' % (canvas['width'], canvas['height'])
1527 canvas['scrollregion'] = scrollregion
1528
1529 self._scrollwatcher = ScrollWatcherWidget(canvas)
1530
1531 # If no parent was given, pack the frame.
1532 if parent is None:
1533 self.pack(expand=1, fill='both')
1534
1535 # Done button.
1536 buttonframe = Frame(self._parent)
1537 buttonframe.pack(fill='x', side='bottom')
1538 ok = Button(buttonframe, text='Done', command=self.destroy)
1539 ok.pack(side='right')
1540 ps = Button(buttonframe, text='Print',
1541 command=self.print_to_file)
1542 ps.pack(side='left')
1543 #help = Button(buttonframe, text='Help')
1544 #help.pack(side='right')
1545
1547 """
1548 Print the contents of this C{CanvasFrame} to a postscript
1549 file. If no filename is given, then prompt the user for one.
1550
1551 @param filename: The name of the file to print the tree to.
1552 @type filename: C{string}
1553 @rtype: C{None}
1554 """
1555 if filename is None:
1556 from tkFileDialog import asksaveasfilename
1557 ftypes = [('Postscript files', '.ps'),
1558 ('All files', '*')]
1559 filename = asksaveasfilename(filetypes=ftypes,
1560 defaultextension='.ps')
1561 if not filename: return
1562 (x0, y0, w, h) = self.scrollregion()
1563 self._canvas.postscript(file=filename, x=x0, y=y0,
1564 width=w+2, height=h+2)
1565
1567 """
1568 @return: The current scroll region for the canvas managed by
1569 this C{CanvasFrame}.
1570 @rtype: 4-tuple of C{int}
1571 """
1572 (x1, y1, x2, y2) = self._canvas['scrollregion'].split()
1573 return (int(x1), int(y1), int(x2), int(y2))
1574
1576 """
1577 @return: The canvas managed by this C{CanvasFrame}.
1578 @rtype: C{Tkinter.Canvas}
1579 """
1580 return self._canvas
1581
1583 """
1584 Register a canvas widget with this C{CanvasFrame}. The
1585 C{CanvasFrame} will ensure that this canvas widget is always
1586 within the C{Canvas}'s scrollregion. If no coordinates are
1587 given for the canvas widget, then the C{CanvasFrame} will
1588 attempt to find a clear area of the canvas for it.
1589
1590 @type canvaswidget: C{CanvasWidget}
1591 @param canvaswidget: The new canvas widget. C{canvaswidget}
1592 must have been created on this C{CanvasFrame}'s canvas.
1593 @type x: C{int}
1594 @param x: The initial x coordinate for the upper left hand
1595 corner of C{canvaswidget}, in the canvas's coordinate
1596 space.
1597 @type y: C{int}
1598 @param y: The initial y coordinate for the upper left hand
1599 corner of C{canvaswidget}, in the canvas's coordinate
1600 space.
1601 """
1602 if x is None or y is None:
1603 (x, y) = self._find_room(canvaswidget)
1604
1605 # Move to (x,y)
1606 (x1,y1,x2,y2) = canvaswidget.bbox()
1607 canvaswidget.move(x-x1,y-y1)
1608
1609 # Register with scrollwatcher.
1610 self._scrollwatcher.add_child(canvaswidget)
1611
1613 """
1614 Try to find a space for a given widget.
1615 """
1616 (left, top, right, bot) = self.scrollregion()
1617
1618 w = widget.width()
1619 h = widget.height()
1620
1621 # Move the widget out of the way, for now.
1622 (x1,y1,x2,y2) = widget.bbox()
1623 widget.move(left-x2-50, top-y2-50)
1624
1625 if abs(right-left-w) < 10 or abs(bot-top-h) < 10:
1626 # dp: Not sure why this happens, but it must be stopped
1627 return (0,0)
1628 for y in range(top, bot-h, (bot-top-h)/10):
1629 for x in range(left, right-w, (right-left-w)/10):
1630 if not self._canvas.find_overlapping(x-5, y-5, x+w+5, y+h+5):
1631 return (x,y)
1632 return (0,0)
1633
1635 """
1636 Remove a canvas widget from this C{CanvasFrame}. This
1637 deregisters the canvas widget, and destroys it.
1638 """
1639 # Deregister with scrollwatcher.
1640 self._scrollwatcher.remove_child(canvaswidget)
1641 canvaswidget.destroy()
1642
1644 """
1645 Pack this C{CanvasFrame}. See the documentation for
1646 C{Tkinter.Pack} for more information.
1647 """
1648 self._frame.pack(cnf, **kw)
1649 # Adjust to be big enough for kids?
1650
1652 """
1653 Destroy this C{CanvasFrame}. If this C{CanvasFrame} created a
1654 top-level window, then this will close that window.
1655 """
1656 if self._parent is None: return
1657 self._parent.destroy()
1658 self._parent = None
1659
1662
1664 """
1665 A C{Tkinter} window used to display a text. C{ShowText} is
1666 typically used by graphical tools to display help text, or similar
1667 information.
1668 """
1671 if width is None or height is None:
1672 (width, height) = self.find_dimensions(text, width, height)
1673
1674 # Create the main window.
1675 if root is None:
1676 self._top = top = Tk()
1677 else:
1678 self._top = top = Toplevel(root)
1679 top.title(title)
1680
1681 b = Button(top, text='Ok', command=self.destroy)
1682 b.pack(side='bottom')
1683
1684 tbf = Frame(top)
1685 tbf.pack(expand=1, fill='both')
1686 scrollbar = Scrollbar(tbf, orient='vertical')
1687 scrollbar.pack(side='right', fill='y')
1688 textbox = Text(tbf, wrap='word', width=width,
1689 height=height, **textbox_options)
1690 textbox.insert('end', text)
1691 textbox['state'] = 'disabled'
1692 textbox.pack(side='left', expand=1, fill='both')
1693 scrollbar['command'] = textbox.yview
1694 textbox['yscrollcommand'] = scrollbar.set
1695
1696 # Make it easy to close the window.
1697 top.bind('q', self.destroy)
1698 top.bind('x', self.destroy)
1699 top.bind('c', self.destroy)
1700 top.bind('<Return>', self.destroy)
1701 top.bind('<Escape>', self.destroy)
1702
1703 # Focus the scrollbar, so they can use up/down, etc.
1704 scrollbar.focus()
1705
1707 lines = text.split('\n')
1708 if width is None:
1709 maxwidth = max([len(line) for line in lines])
1710 width = min(maxwidth, 80)
1711
1712 # Now, find height.
1713 height = 0
1714 for line in lines:
1715 while len(line) > width:
1716 brk = line[:width].rfind(' ')
1717 line = line[brk:]
1718 height += 1
1719 height += 1
1720 height = min(height, 25)
1721
1722 return (width, height)
1723
1728
1730 self._top.mainloop()
1731
1732 ##//////////////////////////////////////////////////////
1733 ## Test code.
1734 ##//////////////////////////////////////////////////////
1735
1743 def color(cw):
1744 from random import randint
1745 cw['color'] = '#ff%04d' % randint(0,9999)
1746
1747 cf = CanvasFrame(root,closeenough=10, width=300, height=300)
1748 c = cf.canvas()
1749 ct3 = TextWidget(c, 'hiya there', draggable=1)
1750 ct2 = TextWidget(c, 'o o\n||\n___\n U', draggable=1, justify='center')
1751 co = OvalWidget(c, ct2, outline='red')
1752 ct = TextWidget(c, 'o o\n||\n\\___/', draggable=1, justify='center')
1753 cp = ParenWidget(c, ct, color='red')
1754 cb = BoxWidget(c, cp, fill='cyan', draggable=1, width=3, margin=10)
1755 equation = SequenceWidget(c,
1756 SymbolWidget(c, 'forall'), TextWidget(c, 'x'),
1757 SymbolWidget(c, 'exists'), TextWidget(c, 'y: '),
1758 TextWidget(c, 'x'), SymbolWidget(c, 'notequal'),
1759 TextWidget(c, 'y'))
1760 space = SpaceWidget(c, 0, 30)
1761 cstack = StackWidget(c, cb, ct3, space, co, equation, align='center')
1762 foo = TextWidget(c, 'try clicking\nand dragging',
1763 draggable=1, justify='center')
1764 cs = SequenceWidget(c, cstack, foo)
1765 zz = BracketWidget(c, cs, color='green4', width=3)
1766 cf.add_widget(zz, 60, 30)
1767
1768 cb.bind_click(fill)
1769 ct.bind_click(color)
1770 co.bind_click(fill)
1771 ct2.bind_click(color)
1772 ct3.bind_click(color)
1773
1774 #ShowText(None, 'title', ((('this is text'*150)+'\n')*5))
1775
1776
1777 if __name__ == '__main__':
1778 root = Tk()
1779 odemo()
1780 root.mainloop()
1781
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Wed Aug 19 16:48:38 2009 | http://epydoc.sourceforge.net |