Mercurial > fife-parpg
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 |