Package pallavi :: Module EventActionManager
[hide private]
[frames] | no frames]

Source Code for Module pallavi.EventActionManager

  1  # Copyright (c) 2006-2007 Dusty Phillips 
  2   
  3  # Permission is hereby granted, free of charge, to any person obtaining a 
  4  # copy of this software and associated documentation files (the "Software"), 
  5  # to deal in the Software without restriction, including without limitation 
  6  # the rights to use, copy, modify, merge, publish, distribute, sublicense, 
  7  # and/or sell copies of the Software, and to permit persons to whom the 
  8  # Software is furnished to do so, subject to the following conditions: 
  9   
 10  # The above copyright notice and this permission notice shall be included in 
 11  # all copies or substantial portions of the Software. 
 12   
 13  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 14  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 15  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 16  # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 17  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 18  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 19  # DEALINGS IN THE SOFTWARE. 
 20   
 21  '''This mmodule contains the classes that allow plugins to communicate with the core 
 22  and with each other. Actions are commands that can be invoked by name, while events 
 23  are things that have happened and may need to be responded to.''' 
 24   
 25  import Queue 
 26   
27 -class Action(object): # This line leaves me thinking of lawyers
28 '''Any action that can occur in the editor. This can be used as communication between 29 two plugins (this allows decoupling, so either plugin could be replaced by a different 30 version), or actions can be bound to keystrokes, menu items, toolbars, etc. They can 31 be created dynamically by macros and plugins and bound at any time.''' 32
33 - def __init__(self, name, callback, label=None, description=None, icon=None):
34 '''Initialize the action. The name must be provided and must be unique from all 35 other actions in the editor. This includes plugins, so plugin-developers are 36 encouraged to watch their namespace. For example, prepend the name of your plugin 37 to all actions the plugin might define. A callback function that takes one argument 38 (may be None) must also be supplied unless the Action class has been subclassed and 39 the invoke method has been overridden. 40 41 A label, description, and icon can also be provided. The label serves two 42 purposes: First, it behaves as a flag that this is an end-user action as 43 opposed to an action for communication betwen plugins. If it is set to None, 44 then it is an internal action. Second it provides a label that will be displayed to 45 end users in identifying the action in action lists or in adding the action to menus 46 or other visible items. The description is optional, but highly recommended for 47 user-visible actions. It is used to describe the action in action lists or as 48 a mouse tooltip for menuitems, toolbars, and the like. Finally, an optional icon 49 can be specified to be associated with the action in gui menus or toolbars.''' 50 51 52 self.name = name 53 self.callback = callback 54 self.label = label 55 self.description = description 56 self.icon = icon
57
58 - def IsUserAction(self):
59 '''return true if this is an action that should be visible to end users, 60 false otherwise.''' 61 return label != None
62
63 - def Invoke(self, *args, **kwargs):
64 '''Invokes the action. The default implementation calls the callback 65 associated with the Action, but it could be subclassed and this method 66 overridden to provide this behaviour directly. 67 68 The optional data parameter can contain action-specific information that 69 the callback must interpret. There is an implicit agreement between event 70 callers and handlers that this data will make sense to each other, but 71 event callbacks should not rely on this. The source of the data may not be 72 known, though non-primitive data is encouraged to have a "GetEventObject()" 73 method.''' 74 self.callback(*args, **kwargs)
75
76 -class ActionList(dict):
77 '''A dictionary of actions for rapid lookup of action by name. Keys are names 78 of actions and should be equivalent to action.name''' 79
80 - def __setitem__(self, key, value):
81 '''Check that all actions added to the dictionary have the correct name. This means 82 that assignments of the form actionList[x] = y must indicate that y is an action 83 with a name and that x = action.name. Typically assignment would be of the form 84 actionList[action.name] = action, but the AddAction method should really be used 85 instead''' 86 if key != value.name: 87 raise TypeError, "Action key must be its name" 88 else: 89 dict.__setitem__(self, key, value)
90 - def AddAction(self, action):
91 '''Add an action to the list''' 92 self[action.name] = action
93
94 - def Invoke(self, actionName, *args, **kwargs):
95 '''Invokes a specific action based on its name and pass optional data to the action''' 96 if self.has_key(actionName): 97 self[actionName].Invoke(*args, **kwargs) 98 else: 99 print "Warning: Action Not Found:", actionName
100
101 - def UserActions(self):
102 '''Obtain a list of actions that are considered visible to the end user as opposed 103 to internal to editor and plugin communication. Such actions would be added to 104 keybinding lists, menus, toolbars, context menus, etc. These actions are identified 105 by having a label that is not None''' 106 return filter(lambda action: action.label != None, self.values())
107
108 -class EventBus(object):
109 '''Maintains a list of event handlers based on named events. Any plugin 110 or core built-in can listen for specific types of events by name. Further, 111 any action or input can queue events to send at any time. An idle function 112 processes the events on the wx MainLoop, so callbacks should make an effort 113 to return ASAP (ie: spawn a new thread for lengthy tasks).''' 114
115 - def __init__(self):
116 self.eventQueue = Queue.Queue() 117 self.handlers = {}
118
119 - def AddListener(self, eventName, callback):
120 '''Add a handler for a specific type of event. The name is the type of event 121 to be handled, and the callback is a function that takes two arguments, the 122 name and data of the event. Data may be none and its interpretation is listener 123 specific.''' 124 if self.handlers.has_key(eventName): 125 self.handlers[eventName].append(callback) 126 else: 127 self.handlers[eventName] = [callback]
128
129 - def RemoveListener(self, eventName, callback):
130 '''Remove a specific handler for a specific type of events. If its listening to 131 other types of events, they won't be interfered with.''' 132 self.handlers[eventName].remove(callback)
133
134 - def QueueEvent(self, eventName, eventData=None):
135 '''Put an event on the queue for ASAP processing. Data may be event specific 136 handlers are expected to know what to do with it (similarly, callers of this 137 function are expected to provide consistent data for each event type). 138 All handlers listening for that type of event will be notified of the event.''' 139 self.eventQueue.put((eventName, eventData))
140
141 - def idle(self, idleEvent):
142 '''A wx idle function. When the system is idling, it processes events on the queue.''' 143 if not self.eventQueue.empty(): 144 eventName, callback = self.eventQueue.get() 145 if self.handlers.has_key(eventName): 146 for handler in self.handlers[eventName]: 147 handler(eventName, callback) 148 149 if not self.eventQueue.empty(): 150 idleEvent.RequestMore()
151