| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- from functools import partial
- from geometry import Positionable
- from logger import Logger
- from trackers import create_tracker
- from abc import ABCMeta, abstractmethod
- class Widget(Positionable, Logger):
- """
- Abstract class for widget implementations. A widget represents a 2D object
- on the screen in which gestures can occur. Handlers for a specific gesture
- type can be bound to a widget.
- """
- __metaclass__ = ABCMeta
- def __init__(self, x=None, y=None):
- Positionable.__init__(self, x, y)
- # List of all active trackers
- self.trackers = []
- # Map of gesture types to a list of handlers for that type
- self.handlers = {}
- # Widget tree references
- self.parent = None
- self.children = []
- def get_root_widget(self):
- """
- Traverse up in the widget tree to find the root widget.
- """
- if self.parent:
- return self.parent.get_root_widget()
- return self
- def get_screen_offset(self):
- """
- Get the position relative to the screen.
- """
- root = self.get_root_widget()
- return root + self.get_offset(root)
- def get_offset(self, offset_parent=None):
- """
- Get the position relative to an offset parent. If no offset parent is
- specified, the position relative to the root widget is returned. The
- position of the root widget itself is (0, 0).
- """
- if not offset_parent:
- offset_parent = self.get_root_widget()
- if not self.parent:
- if offset_parent is self:
- return 0, 0
- ox, oy = offset_paret
- x = y = 0
- else:
- ox, oy = offset_parent
- x = self.x
- y = self.y
- return x - ox, y - oy
- def add_widget(self, widget):
- """
- Add a new child widget.
- """
- self.children.append(widget)
- widget.set_parent(self)
- def remove_widget(self, widget):
- """
- Remove a child widget.
- """
- self.children.remove(widget)
- widget.set_parent(None)
- def set_parent(self, widget):
- """
- Set a new parent widget. If a parent widget has already been assigned,
- remove the widget from that parent first.
- """
- if widget and self.parent:
- self.parent.remove_widget(self)
- self.parent = widget
- def unbind(self, gesture_type, handler=None):
- """
- Unbind a single handler, or all handlers bound to a gesture type.
- Remove the corresponding tracker if it is no longer needed.
- """
- if gesture_type not in self.handlers:
- raise KeyError('Gesture type "%s" is not bound.')
- if handler:
- # Remove a specific handler
- self.handlers[gesture_type].remove(handler)
- # Only remove the handler list and optionally the tracker if no
- # other handlers exist for the gesture type
- if self.handlers[gesture_type]:
- return
- else:
- # Remove handler list
- del self.handlers[gesture_type][:]
- tracker = self.find_tracker(gesture_type)
- # Check if any other handlers need the tracker
- for gtype in tracker.gesture_types:
- if gtype in self.handlers:
- return
- # No more handlers are bound, remove unused tracker and handlers
- self.trackers.remove(gesture_type)
- for gtype in tracker.gesture_types:
- del self.handlers[gtype]
- def find_tracker(self, gesture_type):
- """
- Find a tracker that is tracking some gesture type.
- """
- for tracker in self.trackers:
- if gesture_type in tracker.gesture_types:
- return tracker
- def bind(self, gesture_type, handler, remove_existing=False, **kwargs):
- """
- Bind a handler to the specified type of gesture. Create a tracker for
- the gesture type if it does not exists yet. Ik a new tracker is
- created, configure is with any keyword arguments that have been
- specified.
- """
- if gesture_type not in self.handlers:
- tracker = create_tracker(gesture_type, self)
- tracker.configure(**kwargs)
- self.trackers.append(tracker)
- self.handlers[gesture_type] = []
- # Create empty tracker lists for all supported gestures
- for gtype in tracker.gesture_types:
- self.handlers[gtype] = []
- elif remove_existing:
- del self.handlers[gesture_type][:]
- self.handlers[gesture_type].append(handler)
- def __getattr__(self, name):
- """
- Allow calls like:
- widget.on_gesture(...)
- instead of:
- widget.bind('gesture', ...)
- """
- if len(name) < 4 or name[:3] != 'on_':
- raise AttributeError("'%s' has no attribute '%s'"
- % (self.__class__.__name__, name))
- return partial(self.bind, name[3:])
- @abstractmethod
- def contains_event(self, event):
- """
- Check if the coordinates of an event are contained within this widget.
- """
- raise NotImplementedError
- def delegate_event(self, event):
- """
- Delegate a triggered event to all child widgets. If a child stops
- propagation, return so that its siblings and the parent widget will not
- delegate the event to their trackers.
- """
- child_found = False
- if self.children:
- event.set_offset(self.get_offset())
- # Delegate to children in reverse order because widgets that are
- # added later, should be placed over previously added siblings
- for child in reversed(self.children):
- if child.contains_event(event):
- child_found = True
- child.delegate_event(event)
- if event.is_propagation_stopped():
- return
- if not child_found:
- self.propagate_event(event)
- def propagate_event(self, event):
- for tracker in self.trackers:
- tracker.handle_event(event)
- if event.is_immediate_propagation_stopped():
- break
- if self.parent and not event.is_propagation_stopped():
- self.parent.propagate_event(event)
- def handle_gesture(self, gesture):
- """
- Handle a gesture that is triggered by a gesture tracker. First, all
- handlers bound to the gesture type are called. Second, if the gesture's
- propagation has not been stopped by a handler, the gesture is
- propagated to the parent widget.
- """
- for handler in self.handlers.get(gesture.get_type(), ()):
- handler(gesture)
- #if self.parent and not gesture.is_propagation_stopped():
- # self.parent.handle_gesture(gesture)
|