1 """This modules defines the Widget class, which is the base of the PGU widget
6 from . import pguglobals
8 from .errors import PguError
11 # The function to call
13 # The parameters to pass to the function (as a list)
17 """Base class for all PGU graphical objects.
19 Example - Creating your own Widget:
21 class Draw(gui.Widget):
23 # Paint the pygame.Surface
27 # Update the pygame.Surface and return the update rects
28 return [pygame.Rect(0,0,self.rect.w,self.rect.h)]
31 # Handle the pygame.Event
34 def resize(self,width=None,height=None):
35 # Return the width and height of this widget
39 # The name of the widget (or None if not defined)
41 # The container this widget belongs to
43 # Whether this widget has been painted yet
45 # The widget used to paint the background
52 # A dictionary of signal callbacks, hashed by signal ID
54 # The area covered by the widget, relative to the parent widget
57 def __init__(self, **params):
58 """Create a new Widget instance given the style parameters.
61 decorate -- whether to call theme.decorate(self) to allow the
62 theme a chance to decorate the widget. (default is true)
63 style -- a dict of style parameters.
64 x, y -- position parameters
65 width, height -- size parameters
66 align, valign -- alignment parameters, passed along to style
67 font -- the font to use with this widget
68 color -- the color property, if applicable
69 background -- the widget used to paint the background
70 cls -- class name as used by Theme
71 name -- name of widget as used by Form. If set, will call
72 form.add(self,name) to add the widget to the most recently
74 focusable -- True if this widget can receive focus via Tab, etc.
76 disabled -- True of this widget is disabled (defaults is False)
77 value -- initial value
80 #object.Object.__init__(self)
82 params.setdefault('decorate',True)
83 params.setdefault('style',{})
84 params.setdefault('focusable',True)
85 params.setdefault('disabled',False)
87 self.focusable = params['focusable']
88 self.disabled = params['disabled']
90 self.rect = pygame.Rect(params.get('x',0),
92 params.get('width',0),
93 params.get('height',0))
96 #some of this is a bit "theme-ish" but it is very handy, so these
97 #things don't have to be put directly into the style.
98 for att in ('align','valign','x','y','width','height','color','font','background'):
99 if att in params: s[att] = params[att]
100 self.style = style.Style(self,s)
103 if 'cls' in params: self.cls = params['cls']
106 self.name = params['name']
108 form.Form.form.add(self)
109 self.form = form.Form.form
110 if 'value' in params: self.value = params['value']
113 if params['decorate'] != False:
114 if (not pguglobals.app):
115 # TODO - fix this somehow
118 pguglobals.app.theme.decorate(self,params['decorate'])
121 """Focus this Widget."""
123 if self.container.myfocus != self: ## by Gal Koren
124 self.container.focus(self)
127 """Blur this Widget."""
128 if self.container: self.container.blur(self)
131 """Open this widget as a modal dialog."""
132 #if getattr(self,'container',None) != None: self.container.open(self)
133 pguglobals.app.open(self)
135 def close(self, w=None):
136 """Close this widget, if it is currently an open dialog."""
137 #if getattr(self,'container',None) != None: self.container.close(self)
140 pguglobals.app.close(w)
143 return (self in pguglobals.app.windows)
145 def is_hovering(self):
146 """Returns true if the mouse is hovering over this widget."""
148 return (self.container.myhover is self)
151 def resize(self,width=None,height=None):
152 """Resize this widget and all sub-widgets, returning the new size.
154 This should be implemented by a subclass.
157 return (self.style.width, self.style.height)
160 """Signal that this widget has changed its size."""
162 if (not self._painted):
165 if (not self.container):
169 pguglobals.app.chsize()
172 """Updates the surface and returns a rect list of updated areas
174 This should be implemented by a subclass.
180 """Render this widget onto the given surface
182 This should be implemented by a subclass.
188 """Request a repaint of this Widget."""
189 if self.container: self.container.repaint(self)
190 #pguglobals.app.repaint_widget(self)
192 def repaintall(self):
193 """Request a repaint of all Widgets."""
194 if self.container: self.container.repaintall()
197 """Request a reupdate of this Widget."""
198 if self.container: self.container.reupdate(self)
201 """Pass focus to next Widget.
203 Widget order determined by the order they were added to their container.
206 if self.container: self.container.next(self)
209 """Pass focus to previous Widget.
211 Widget order determined by the order they were added to their container.
215 if self.container: self.container.previous(self)
217 def get_abs_rect(self):
218 """Returns the absolute rect of this widget on the App screen."""
219 x, y = self.rect.x, self.rect.y
224 if cnt._rect_content:
225 x += cnt._rect_content.x
226 y += cnt._rect_content.y
228 return pygame.Rect(x, y, self.rect.w, self.rect.h)
230 def connect(self,code,func,*params):
231 """Connect an event code to a callback function.
233 Note that there may be multiple callbacks per event code.
237 fnc -- callback function
238 *values -- values to pass to callback.
240 Please note that callbacks may also have "magicaly" parameters.
243 _event -- receive the event
244 _code -- receive the event code
245 _widget -- receive the sending widget
249 print ('click', value)
252 w.connect(gui.CLICK,onclick,'PGU Button Clicked')
255 if (not code in self.connects):
256 self.connects[code] = []
257 for cb in self.connects[code]:
258 if (cb.func == func):
259 # Already connected to this callback function
261 # Wrap the callback function and add it to the list
262 cb = SignalCallback()
265 self.connects[code].append(cb)
267 # Remove signal handlers from the given event code. If func is specified,
268 # only those handlers will be removed. If func is None, all handlers
270 def disconnect(self, code, func=None):
271 if (not code in self.connects):
274 # Remove all signal handlers
275 del self.connects[code]
277 # Remove handlers that call 'func'
279 callbacks = self.connects[code]
280 while (n < len(callbacks)):
281 if (callbacks[n].func == func):
282 # Remove this callback
287 def send(self,code,event=None):
288 """Send a code, event callback trigger."""
289 if (not code in self.connects):
291 # Trigger all connected signal handlers
292 for cb in self.connects[code]:
294 values = list(cb.params)
296 # Attempt to be compatible with previous versions of python
300 code = func.func_code
302 nargs = code.co_argcount
303 names = list(code.co_varnames)[:nargs]
305 # If the function is bound to an instance, remove the first argument name. Again
306 # we keep compatibility with older versions of python.
307 if (hasattr(func, "__self__") and hasattr(func.__self__, "__class__") or
308 hasattr(func,'im_class')):
312 magic = {'_event':event,'_code':code,'_widget':self}
314 if name in magic.keys():
315 args.append(magic[name])
317 args.append(values.pop(0))
324 if self.disabled: return
329 """Called when an event is passed to this object.
331 Please note that if you use an event, returning the value True
332 will stop parent containers from also using the event. (For example, if
333 your widget handles TABs or arrow keys, and you don't want those to
334 also alter the focus.)
336 This should be implemented by a subclass.
341 def get_toplevel(self):
342 """Returns the top-level widget (usually the Desktop) by following the
343 chain of 'container' references."""
345 while (top.container):
349 def collidepoint(self, pos):
350 """Test if the given point hits this widget. Over-ride this function
351 for more advanced collision testing."""
352 return self.rect.collidepoint(pos)