comparison engine/python/fife/extensions/pychan/widgets/widget.py @ 378:64738befdf3b

bringing in the changes from the build_system_rework branch in preparation for the 0.3.0 release. This commit will require the Jan2010 devkit. Clients will also need to be modified to the new way to import fife.
author vtchill@33b003aa-7bff-0310-803a-e67f0ece8222
date Mon, 11 Jan 2010 23:34:52 +0000
parents
children f44b149f63e7
comparison
equal deleted inserted replaced
377:fe6fb0e0ed23 378:64738befdf3b
1 # -*- coding: utf-8 -*-
2
3 # ####################################################################
4 # Copyright (C) 2005-2009 by the FIFE team
5 # http://www.fifengine.de
6 # This file is part of FIFE.
7 #
8 # FIFE is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU Lesser General Public
10 # License as published by the Free Software Foundation; either
11 # version 2.1 of the License, or (at your option) any later version.
12 #
13 # This library is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # Lesser General Public License for more details.
17 #
18 # You should have received a copy of the GNU Lesser General Public
19 # License along with this library; if not, write to the
20 # Free Software Foundation, Inc.,
21 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 # ####################################################################
23
24 from fife.extensions.pychan.widgets.common import *
25
26 class Widget(object):
27 """
28 This is the common widget base class, which provides most of the wrapping
29 functionality.
30
31 Attributes
32 ==========
33
34 Widgets are manipulated (mostly) through attributes - and these can all be set by XML attributes.
35 Derived widgets will have other attributes. Please see their B{New Attributes} sections. The types of the
36 attributes are pretty straightforward, but note that Position and Color attribute types will also accept
37 C{fife.Point} and C{fife.Color} values.
38
39 - name: String: The identification of the widget, most useful if it is unique within a given widget hiarachy.
40 This is used to find widgets by L{mapEvents},L{distributeInitialData},L{distributeData} and L{collectData}.
41 - position: Position: The position relative to the parent widget - or on screen, if this is the root widget.
42 - size: Position: The real size of the widget (including border and margins). Usually you do not need to set this.
43 A notable exception is the L{ScrollArea}.
44 - min_size: Position: The minimal size this widget is allowed to have. This is enforced through the accessor methods
45 of the actual size attribute.
46 - max_size: Position: The maximal size this widget is allowed to have. This is enforced through the accessor methods
47 of the actual size attribute.
48 - base_color: Color
49 - background_color: Color
50 - foreground_color: Color
51 - selection_color: Color
52 - font: String: This should identify a font that was loaded via L{loadFonts} before.
53 - helptext: Unicode: Text which can be used for e.g. tooltips.
54 - border_size: Integer: The size of the border in pixels.
55 - position_technique: This can be either "automatic" or "explicit" - only L{Window} has this set to "automatic" which
56 results in new windows being centered on screen (for now).
57 If it is set to "explicit" the position attribute will not be touched.
58 - vexpand: Integer: >= 0. Proportion to expand this widget vertically.
59 - hexpand: Integer: >= 0. Proportion to expand this widget horizontally.
60
61 Convenience Attributes
62 ======================
63
64 These attributes are convenience/shorthand versions of above mentioned attributes and assignment will reflect
65 the associated attributes values. E.g. the following is equivalent::
66 # Set X position, leave Y alone
67 widget.x = 10
68 # Same here
69 posi = widget.position
70 widget.position = (10, posi[1])
71
72 Here they are.
73
74 - x: Integer: The horizontal part of the position attribute.
75 - y: Integer: The vertical part of the position attribute.
76 - width: Integer: The horizontal part of the size attribute.
77 - height: Integer: The vertical part of the size attribute.
78
79 """
80
81 ATTRIBUTES = [ Attr('name'), PointAttr('position'),
82 PointAttr('min_size'), PointAttr('size'), PointAttr('max_size'),
83 ColorAttr('base_color'),ColorAttr('background_color'),ColorAttr('foreground_color'),ColorAttr('selection_color'),
84 Attr('style'), Attr('font'),IntAttr('border_size'),Attr('position_technique'),
85 IntAttr('vexpand'),IntAttr('hexpand'),
86 UnicodeAttr('helptext'), BoolAttr('is_focusable')
87 ]
88
89 DEFAULT_NAME = '__unnamed__'
90 DEFAULT_HEXPAND = 0
91 DEFAULT_VEXPAND = 0
92 DEFAULT_MAX_SIZE = 500000, 500000
93
94 HIDE_SHOW_ERROR = """\
95 You can only show/hide the top widget of a hierachy.
96 Use 'addChild' or 'removeChild' to add/remove labels for example.
97 """
98
99
100 def __init__(self,parent = None, name = DEFAULT_NAME,
101 size = (-1,-1), min_size=(0,0), max_size=DEFAULT_MAX_SIZE,
102 helptext=u"",
103 position = (0,0),
104 style = None, **kwargs):
105
106 assert( hasattr(self,'real_widget') )
107 self.event_mapper = events.EventMapper(self)
108 self._visible = False
109 self._extra_border = (0,0)
110 self.hexpand = kwargs.get("hexpand",self.DEFAULT_HEXPAND)
111 self.vexpand = kwargs.get("vexpand",self.DEFAULT_VEXPAND)
112 # Simple way to get at least some compat layout:
113 if get_manager().compat_layout:
114 self.hexpand, self.vexpand = 0,0
115
116 # Data distribution & retrieval settings
117 self.accepts_data = False
118 self.accepts_initial_data = False
119
120 # Parent attribute makes sure we only have one parent,
121 # that tests self.__parent - so make sure we have the attr here.
122 self.__parent = None
123 self.parent = parent
124
125 self.has_name = False
126 self.name = name
127
128 self.position = position
129 self.min_size = min_size
130 self.max_size = max_size
131 self.size = size
132 self.position_technique = "explicit"
133 self.font = 'default'
134
135 # Inherit style
136 if style is None and parent:
137 style = parent.style
138 self.style = style or "default"
139
140 self.helptext = helptext
141 # Not needed as attrib assignment will trigger manager.stylize call
142 #manager.stylize(self,self.style)
143
144 def execute(self,bind):
145 """
146 Execute a dialog synchronously.
147
148 As argument a dictionary mapping widget names to return values
149 is expected. Events from these widgets will cause this function
150 to return with the associated return value.
151
152 This function will not return until such an event occurs.
153 The widget will be shown before execution and hidden afterwards.
154 You can only execute root widgets.
155
156 Note: This feature is not tested well, and the API will probably
157 change. Otherwise have fun::
158 # Okay this a very condensed example :-)
159 return pychan.loadXML("contents/gui/dialog.xml").execute({ 'okButton' : True, 'closeButton' : False })
160
161 """
162 if not get_manager().can_execute:
163 raise RuntimeError("Synchronous execution is not set up!")
164 if self.__parent:
165 raise RuntimeError("You can only 'execute' root widgets, not %s!" % str(self))
166
167 for name,returnValue in bind.items():
168 def _quitThisDialog(returnValue = returnValue ):
169 get_manager().breakFromMainLoop( returnValue )
170 self.hide()
171 self.findChild(name=name).capture( _quitThisDialog , group_name = "__execute__" )
172 self.show()
173 return get_manager().mainLoop()
174
175 def match(self,**kwargs):
176 """
177 Matches the widget against a list of key-value pairs.
178 Only if all keys are attributes and their value is the same it returns True.
179 """
180 for k,v in kwargs.items():
181 if v != getattr(self,k,None):
182 return False
183 return True
184
185 def capture(self, callback, event_name="action", group_name="default"):
186 """
187 Add a callback to be executed when the widget event occurs on this widget.
188
189 The callback must be either a callable or None.
190 The old event handler (if any) will be overridden by the callback.
191 If None is given, the event will be disabled. You can query L{isCaptured}
192 wether this widgets events are currently captured.
193
194 It might be useful to check out L{tools.callbackWithArguments}.
195
196 @param callback: Event callback - may accept keyword arguments event and widget.
197 @paran event_name: The event to capture - may be one of L{events.EVENTS} and defaults to "action"
198 @paran group_name: Event group.
199
200 Event groups are used to have different B{channels} which don't interfere with each other.
201 For derived widgets that need to capture events it's advised to use the group_name 'widget'.
202 The 'default' group is used by default, and should be reserved for the application programmers.
203 """
204 self.event_mapper.capture( event_name, callback, group_name )
205
206 def isCaptured(self):
207 """
208 Check whether this widgets events are captured
209 (a callback is installed) or not.
210 """
211 return bool(self.event_mapper.getCapturedEvents())
212
213 def show(self):
214 """
215 Show the widget and all contained widgets.
216 """
217 if self.parent:
218 raise RuntimeError(Widget.HIDE_SHOW_ERROR)
219 if self._visible: return
220 self.adaptLayout()
221 self.beforeShow()
222 get_manager().show(self)
223 self._visible = True
224
225 def hide(self):
226 """
227 Hide the widget and all contained widgets.
228 """
229 if self.parent:
230 raise RuntimeError(Widget.HIDE_SHOW_ERROR)
231 if not self._visible: return
232
233 get_manager().hide(self)
234
235 self.afterHide()
236 self._visible = False
237
238 def isVisible(self):
239 """
240 Check whether the widget is currently shown,
241 either directly or as part of a container widget.
242 """
243 widget = self
244 while widget.parent:
245 widget = widget.parent
246 return widget._visible
247
248 def adaptLayout(self,recurse=True):
249 """
250 Execute the Layout engine. Automatically called by L{show}.
251 In case you want to relayout a visible widget.
252 This function will automatically perform the layout adaption
253 from the top-most layouted widget.
254
255 To make this clear consider this arrangement::
256 VBox 1
257 - Container
258 - VBox 2
259 - HBox
260 - Label
261
262 If you call adaptLayout on the Label the layout from the VBox 2
263 will get recalculated, while the VBox 1 stays untouched.
264
265 @param recurse Pass False here to force the layout to start from
266 this widget.
267 """
268 widget = self
269 while widget.parent and recurse:
270 if not isLayouted(widget.parent):
271 break
272 widget = widget.parent
273 widget._recursiveResizeToContent()
274 widget._recursiveExpandContent()
275
276 def beforeShow(self):
277 """
278 This method is called just before the widget is shown.
279 You can override this in derived widgets to add finalization
280 behaviour.
281 """
282
283 def afterHide(self):
284 """
285 This method is called just before the widget is hidden.
286 You can override this in derived widgets to add finalization
287 behaviour.
288 """
289
290 def findChildren(self,**kwargs):
291 """
292 Find all contained child widgets by attribute values.
293
294 Usage::
295 closeButtons = root_widget.findChildren(name='close')
296 buttons = root_widget.findChildren(__class__=pychan.widgets.Button)
297 """
298
299 children = []
300 def _childCollector(widget):
301 if widget.match(**kwargs):
302 children.append(widget)
303 self.deepApply(_childCollector)
304 return children
305
306 def getNamedChildren(self, include_unnamed = False):
307 """
308 Create a dictionary of child widgets with the keys being
309 their name. This will contain only Widgets which have
310 a name different from "__unnamed__" (which is the default).
311
312 @param include_unnamed Defaults to false. If this is true unnamed widgets are added, too.
313
314 The values are lists of widgets, so not only unique names
315 are handled correctly.
316
317 Usage::
318 children = widget.getNamedChildren()
319 for widget in children.get("info",[])
320 print widget.name , " == info"
321 """
322 children = {}
323 if include_unnamed:
324 def _childCollector(widget):
325 children.setdefault(widget._name,[]).append(widget)
326 else:
327 def _childCollector(widget):
328 if widget.has_name:
329 children.setdefault(widget._name,[]).append(widget)
330 self.deepApply(_childCollector)
331 return children
332
333 def findChild(self,**kwargs):
334 """ Find the first contained child widgets by attribute values.
335
336 Usage::
337 closeButton = root_widget.findChild(name='close')
338 """
339 if kwargs.keys() == ["name"]:
340 return self.findChildByName(kwargs["name"])
341
342 children = self.findChildren(**kwargs)
343 if children:
344 return children[0]
345 return None
346
347 def findChildByName(self,name):
348 """
349 Find first contained child widget by its name.
350
351 Note that this is the fast version of findChild(name="...")
352 and that you don't have to call this explicitly, it is used
353 if possible.
354 """
355 result = []
356 def _childCollector(widget):
357 if widget._name == name:
358 result.append(widget)
359 raise StopTreeWalking
360 try:
361 self.deepApply(_childCollector)
362 except StopTreeWalking:
363 return result[0]
364 return None
365
366 def addChild(self,widget):
367 """
368 This function adds a widget as child widget and is only implemented
369 in container widgets.
370
371 You'll need to call L{adaptLayout} if the container is already shown,
372 to adapt the layout to the new widget. This doesn't happen
373 automatically.
374 """
375 raise RuntimeError("Trying to add a widget to %s, which doesn't allow this." % repr(self))
376
377 def insertChild(self, widget, position):
378 """
379 This function inserts a widget a given index in the child list.
380
381 See L{addChild} and L{insertChildBefore}
382 """
383 raise RuntimeError("Trying to insert a widget to %s, which doesn't allow this." % repr(self))
384
385 def insertChildBefore(self, widget, before):
386 """
387 Inserts a child widget before a given widget. If the widget isn't found,
388 the widget is appended to the children list.
389
390 See L{addChild} and L{insertChild}
391 """
392 raise RuntimeError("Trying to insert a widget to %s, which doesn't allow this." % repr(self))
393
394 def addChildren(self,*widgets):
395 """
396 Add multiple widgets as children.
397 Only implemented for container widgets. See also L{addChild}
398
399 Usage::
400 container.addChildren( widget1, widget2, ... )
401 # or you can use this on a list
402 container.addChildren( [widget1,widget2,...] )
403 """
404 if len(widgets) == 1 and not isinstance(widgets[0],Widget):
405 widgets = widgets[0]
406 for widget in widgets:
407 self.addChild(widget)
408
409 def removeChild(self,widget):
410 """
411 This function removes a direct child widget and is only implemented
412 in container widgets.
413
414 You'll need to call L{adaptLayout} if the container is already shown,
415 to adapt the layout to the removed widget. This doesn't happen
416 automatically.
417 """
418 raise RuntimeError("Trying to remove a widget from %s, which is not a container widget." % repr(self))
419
420 def removeChildren(self,*widgets):
421 """
422 Remove a list of direct child widgets.
423 All widgets have to be direct child widgets.
424 To 'clear' a container take a look at L{removeAllChildren}.
425 See also L{removeChild}.
426
427 Usage::
428 container.removeChildren( widget1, widget2, ... )
429 # or you can use this on a list
430 container.removeChildren( [widget1,widget2,...] )
431 """
432 if len(widgets) == 1 and not isinstance(widgets[0],Widget):
433 widgets = widgets[0]
434 for widget in widgets:
435 self.removeChild(widget)
436
437 def removeAllChildren(self):
438 """
439 This function will remove all direct child widgets.
440 This will work even for non-container widgets.
441 """
442 children = self.findChildren(parent=self)
443 for widget in children:
444 self.removeChild(widget)
445
446 def mapEvents(self,eventMap,ignoreMissing = False):
447 """
448 Convenience function to map widget events to functions
449 in a batch.
450
451 Subsequent calls of mapEvents will merge events with different
452 widget names and override the previously set callback.
453 You can also pass C{None} instead of a callback, which will
454 disable the event completely.
455
456 @param eventMap: A dictionary with widget/event names as keys and callbacks as values.
457 @param ignoreMissing: Normally this method raises an RuntimeError, when a widget
458 can not be found - this behaviour can be overriden by passing True here.
459
460 The keys in the dictionary are parsed as C{"widgetName/eventName"} with the slash
461 separating the two. If no slash is found the eventName is assumed to be "action".
462
463 Additionally you can supply a group name or channel C{"widgetName/eventName/groupName"}.
464 Event handlers from one group are not overridden by handlers from another group.
465 The default group name is C{"default"}.
466
467 Example::
468 guiElement.mapEvents({
469 "button" : guiElement.hide,
470 "button/mouseEntered" : toggleButtonColorGreen,
471 "button/mouseExited" : toggleButtonColorBlue,
472 })
473
474 """
475 children = self.getNamedChildren(include_unnamed=True)
476 for descr,func in eventMap.items():
477 name, event_name, group_name = events.splitEventDescriptor(descr)
478 #print name, event_name, group_name
479 widgets = children.get(name,[])
480 if widgets:
481 for widget in widgets:
482 widget.capture( func, event_name = event_name, group_name = group_name )
483 elif not ignoreMissing:
484 raise RuntimeError("No widget with the name: %s" % name)
485
486 def setInitialData(self,data):
487 """
488 Set the initial data on a widget, what this means depends on the Widget.
489 In case the widget does not accept initial data, a L{RuntimeError} is thrown.
490 """
491 if not self.accepts_initial_data:
492 raise RuntimeError("Trying to set data on a widget that does not accept initial data. Widget: %s Data: %s " % (repr(self),repr(data)))
493 self._realSetInitialData(data)
494
495 def setData(self,data):
496 """
497 Set the user-mutable data on a widget, what this means depends on the Widget.
498 In case the widget does not accept data, a L{RuntimeError} is thrown.
499 This is inverse to L{getData}.
500 """
501 if not self.accepts_data:
502 raise RuntimeError("Trying to set data on a widget that does not accept data.")
503 self._realSetData(data)
504
505 def getData(self):
506 """
507 Get the user-mutable data of a widget, what this means depends on the Widget.
508 In case the widget does not have user mutable data, a L{RuntimeError} is thrown.
509 This is inverse to L{setData}.
510 """
511 if not self.accepts_data:
512 raise RuntimeError("Trying to retrieve data from a widget that does not accept data.")
513 return self._realGetData()
514
515 def distributeInitialData(self,initialDataMap):
516 """
517 Distribute B{initial} (not mutable by the user) data from a dictionary over the widgets in the hierachy
518 using the keys as names and the values as the data (which is set via L{setInitialData}).
519 If more than one widget matches - the data is set on ALL matching widgets.
520 By default a missing widget is just ignored.
521
522 Use it like this::
523 guiElement.distributeInitialData({
524 'myTextField' : 'Hello World!',
525 'myListBox' : ["1","2","3"]
526 })
527
528 """
529 children = self.getNamedChildren(include_unnamed=True)
530 for name,data in initialDataMap.items():
531 widgetList = children.get(name,[])
532 for widget in widgetList:
533 widget.setInitialData(data)
534
535 def distributeData(self,dataMap):
536 """
537 Distribute data from a dictionary over the widgets in the hierachy
538 using the keys as names and the values as the data (which is set via L{setData}).
539 This will only accept unique matches.
540
541 Use it like this::
542 guiElement.distributeData({
543 'myTextField' : 'Hello World!',
544 'myListBox' : ["1","2","3"]
545 })
546
547 """
548 children = self.getNamedChildren(include_unnamed=True)
549 for name,data in dataMap.items():
550 widgetList = children.get(name,[])
551 if len(widgetList) != 1:
552 if get_manager().debug:
553 self.listNamedWidgets()
554 raise RuntimeError("DistributeData can only handle widgets with unique names.")
555 widgetList[0].setData(data)
556
557 def collectDataAsDict(self,widgetNames):
558 """
559 Collect data from a widget hierachy by names into a dictionary.
560 This can only handle UNIQUE widget names (in the hierachy)
561 and will raise a RuntimeError if the number of matching widgets
562 is not equal to one.
563
564 Usage::
565 data = guiElement.collectDataAsDict(['myTextField','myListBox'])
566 print "You entered:",data['myTextField']," and selected ",data['myListBox']
567
568 """
569 children = self.getNamedChildren(include_unnamed=True)
570 dataMap = {}
571 for name in widgetNames:
572 widgetList = children.get(name,[])
573 if len(widgetList) != 1:
574 if get_manager().debug:
575 self.listNamedWidgets()
576 raise RuntimeError("CollectData can only handle widgets with unique names.")
577
578 dataMap[name] = widgetList[0].getData()
579 return dataMap
580
581 def collectData(self,*widgetNames):
582 """
583 Collect data from a widget hierachy by names.
584 This can only handle UNIQUE widget names (in the hierachy)
585 and will raise a RuntimeError if the number of matching widgets
586 is not equal to one.
587
588 This function takes an arbitrary number of widget names and
589 returns a list of the collected data in the same order.
590
591 In case only one argument is given, it will return just the
592 data, with out putting it into a list.
593
594 Usage::
595 # Multiple element extraction:
596 text, selected = guiElement.collectData('myTextField','myListBox')
597 print "You entered:",text," and selected item nr",selected
598 # Single elements are handled gracefully, too:
599 test = guiElement.collectData('testElement')
600
601 """
602 children = self.getNamedChildren(include_unnamed=True)
603 dataList = []
604 for name in widgetNames:
605 widgetList = children.get(name,[])
606 if len(widgetList) != 1:
607 if get_manager().debug:
608 self.listNamedWidgets()
609 raise RuntimeError("CollectData can only handle widgets with unique names.")
610 dataList.append( widgetList[0].getData() )
611 if len(dataList) == 1:
612 return dataList[0]
613 return dataList
614
615 def listNamedWidgets(self):
616 """
617 This function will print a list of all currently named child-widgets
618 to the standard output. This is useful for debugging purposes.
619 """
620 def _printNamedWidget(widget):
621 if widget.name != Widget.DEFAULT_NAME:
622 print widget.name.ljust(20),repr(widget).ljust(50),repr(widget.__parent)
623 print "Named child widgets of ",repr(self)
624 print "name".ljust(20),"widget".ljust(50),"parent"
625 self.deepApply(_printNamedWidget)
626
627 def stylize(self,style,**kwargs):
628 """
629 Recursively apply a style to all widgets.
630 """
631 def _restyle(widget):
632 get_manager().stylize(widget,style,**kwargs)
633 self.deepApply(_restyle)
634
635 def resizeToContent(self,recurse = True):
636 """
637 Try to shrink the widget, so that it fits closely around its content.
638 Do not call directly.
639 """
640
641 def expandContent(self,recurse = True):
642 """
643 Try to expand any spacer in the widget within the current size.
644 Do not call directly.
645 """
646
647
648 def _recursiveResizeToContent(self):
649 """
650 Recursively call L{resizeToContent}. Uses L{deepApply}.
651 Do not call directly.
652 """
653 def _callResizeToContent(widget):
654 #print "RTC:",widget
655 widget.resizeToContent()
656 self.deepApply(_callResizeToContent)
657
658 def _recursiveExpandContent(self):
659 """
660 Recursively call L{expandContent}. Uses L{deepApply}.
661 Do not call directly.
662 """
663 def _callExpandContent(widget):
664 #print "ETC:",widget
665 widget.expandContent()
666 self.deepApply(_callExpandContent, leaves_first=False)
667
668 def deepApply(self,visitorFunc, leaves_first = True):
669 """
670 Recursively apply a callable to all contained widgets and then the widget itself.
671 """
672 visitorFunc(self)
673
674 def getAbsolutePos(self):
675 """
676 Get absolute position on screen
677 """
678 absX = self.x
679 absY = self.y
680 parent = self.parent
681 while parent is not None:
682 absX += parent.x
683 absY += parent.y
684 parent = parent.parent
685 return (absX, absY)
686
687 def sizeChanged(self):
688 pass
689
690 def __str__(self):
691 return "%s(name='%s')" % (self.__class__.__name__,self.name)
692
693 def __repr__(self):
694 return "<%s(name='%s') at %x>" % (self.__class__.__name__,self.name,id(self))
695
696 def _setSize(self,size):
697 if isinstance(size,fife.Point):
698 self.width, self.height = size.x, size.y
699 else:
700 self.width, self.height = size
701
702 def _getSize(self):
703 return self.width, self.height
704
705 def _setPosition(self,size):
706 if isinstance(size,fife.Point):
707 self.x, self.y = size.x, size.y
708 else:
709 self.x, self.y = size
710
711 def _getPosition(self):
712 return self.x, self.y
713
714 def _setX(self,x):self.real_widget.setX(x)
715 def _getX(self): return self.real_widget.getX()
716 def _setY(self,y): self.real_widget.setY(y)
717 def _getY(self): return self.real_widget.getY()
718
719 def _setWidth(self,w):
720 old_width = self.width
721 w = max(self.min_size[0],w)
722 w = min(self.max_size[0],w)
723 self.real_widget.setWidth(w)
724 if w != old_width:
725 self.sizeChanged()
726
727 def _getWidth(self): return self.real_widget.getWidth()
728 def _setHeight(self,h):
729 old_height = self.height
730 h = max(self.min_size[1],h)
731 h = min(self.max_size[1],h)
732 self.real_widget.setHeight(h)
733 if h != old_height:
734 self.sizeChanged()
735
736 def _getHeight(self): return self.real_widget.getHeight()
737
738 def _getMinWidth(self): return self.min_size[0]
739 def _getMaxWidth(self): return self.max_size[0]
740 def _getMinHeight(self): return self.min_size[1]
741 def _getMaxHeight(self): return self.max_size[1]
742 def _setMinWidth(self,w):
743 self.min_size = w, self.min_size[1]
744 def _setMaxWidth(self,w):
745 self.max_size = w, self.max_size[1]
746 def _setMinHeight(self,h):
747 self.min_size = self.min_size[0],h
748 def _setMaxHeight(self,h):
749 self.max_size = self.max_size[0],h
750
751 def _setFont(self, font):
752 self._font = font
753 self.real_font = get_manager().getFont(font)
754 self.real_widget.setFont(self.real_font)
755 def _getFont(self):
756 return self._font
757
758 def _getBorderSize(self): return self.real_widget.getFrameSize()
759 def _setBorderSize(self,size): self.real_widget.setFrameSize(size)
760
761 base_color = ColorProperty("BaseColor")
762 background_color = ColorProperty("BackgroundColor")
763 foreground_color = ColorProperty("ForegroundColor")
764 selection_color = ColorProperty("SelectionColor")
765
766 def _getStyle(self): return self._style
767 def _setStyle(self,style):
768 self._style = style
769 get_manager().stylize(self,style)
770 style = property(_getStyle,_setStyle)
771
772 def _getParent(self): return self.__parent
773 def _setParent(self,parent):
774 if self.__parent is not parent:
775 if self.__parent and parent is not None:
776 print "Widget containment fumble:", self, self.__parent, parent
777 self.__parent.removeChild(self)
778 self.__parent = parent
779 parent = property(_getParent,_setParent)
780
781 def _setName(self,name):
782 self._name = name
783 if name != Widget.DEFAULT_NAME:
784 self.has_name = True
785 def _getName(self):
786 # __str__ relies on self.name
787 return getattr(self,'_name','__no_name_yet__')
788 name = property(_getName,_setName)
789
790 def _setFocusable(self, b): self.real_widget.setFocusable(b)
791 def _isFocusable(self):
792 return self.real_widget.isFocusable()
793
794 x = property(_getX,_setX)
795 y = property(_getY,_setY)
796 width = property(_getWidth,_setWidth)
797 height = property(_getHeight,_setHeight)
798 min_width = property(_getMinWidth,_setMinWidth)
799 min_height = property(_getMinHeight,_setMinHeight)
800 max_width = property(_getMaxWidth,_setMaxWidth)
801 max_height = property(_getMaxHeight,_setMaxHeight)
802 size = property(_getSize,_setSize)
803 position = property(_getPosition,_setPosition)
804 font = property(_getFont,_setFont)
805 border_size = property(_getBorderSize,_setBorderSize)
806 is_focusable = property(_isFocusable,_setFocusable)
807
808 def setEnterCallback(self, cb):
809 """
810 *DEPRECATED*
811
812 Callback is called when mouse enters the area of Widget
813 callback should have form of function(button)
814 """
815 if cb is None:
816 self.capture(None, event_name = "mouseEntered" )
817 return
818
819 def callback(widget=None):
820 return cb(widget)
821 print "PyChan: You are using the DEPRECATED functionality: setEnterCallback."
822 self.capture(callback, event_name = "mouseEntered" )
823
824 def setExitCallback(self, cb):
825 """
826 *DEPRECATED*
827
828 Callback is called when mouse exits the area of Widget
829 callback should have form of function(button)
830 """
831 if cb is None:
832 self.capture(None, event_name = "mouseExited" )
833 return
834
835 def callback(widget=None):
836 return cb(widget)
837 print "PyChan: You are using the DEPRECATED functionality: setExitCallback."
838 self.capture(callback, event_name = "mouseExited" )
839