1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
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
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
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)
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
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
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
116 self.eventQueue = Queue.Queue()
117 self.handlers = {}
118
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
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
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