]>
Commit | Line | Data |
---|---|---|
1 | #--------------------------------------------------------------------------- | |
2 | # Name: EventManager.py | |
3 | # Purpose: A module to demonstrate wxPython.lib.evtmgr.EventManager. | |
4 | # | |
5 | # Author: Robb Shecter (robb@acm.org) | |
6 | # | |
7 | # Created: 16-December-2002 | |
8 | # Copyright: (c) 2002 by Robb Shecter (robb@acm.org) | |
9 | # Licence: wxWindows license | |
10 | #--------------------------------------------------------------------------- | |
11 | ||
12 | import wx | |
13 | import wx.lib.evtmgr as em | |
14 | ||
15 | #---------------------------------------------------------------------- | |
16 | ||
17 | class TestPanel(wx.Panel): | |
18 | def __init__(self, parent, log): | |
19 | wx.Panel.__init__(self, parent, -1) | |
20 | self.log = log | |
21 | ||
22 | fsize = self.GetFont().GetPointSize() | |
23 | f1 = wx.Font(fsize+0, wx.SWISS, wx.NORMAL, wx.NORMAL) | |
24 | f2 = wx.Font(fsize+2, wx.SWISS, wx.NORMAL, wx.BOLD) | |
25 | f3 = wx.Font(fsize+6, wx.SWISS, wx.NORMAL, wx.BOLD) | |
26 | ||
27 | title1 = wx.StaticText(self, -1, 'EventManager') | |
28 | title1.SetFont(f3) | |
29 | txt = """\ | |
30 | This demo shows (1) basic uses and features of the EventManager, as well | |
31 | as (2) how it helps with a real-world task: creating independent, object- | |
32 | oriented components.""" | |
33 | message0 = wx.StaticText(self, -1, txt) | |
34 | message0.SetFont(f1) | |
35 | ||
36 | title2 = wx.StaticText(self, -1, 'Event Listeners') | |
37 | title2.SetFont(f2) | |
38 | ||
39 | txt = """\ | |
40 | These objects listen to motion events from the target window, using the ability | |
41 | to register one event with multiple listeners. They also register for mouse events | |
42 | on themselves to implement toggle-button functionality.""" | |
43 | message1 = wx.StaticText(self, -1, txt) | |
44 | message1.SetFont(f1) | |
45 | ||
46 | title3 = wx.StaticText(self, -1, 'Target Window') | |
47 | title3.SetFont(f2) | |
48 | ||
49 | txt = """\ | |
50 | A passive window that's used as an event generator. Move the mouse over it to | |
51 | send events to the listeners above.""" | |
52 | message2 = wx.StaticText(self, -1, txt) | |
53 | message2.SetFont(f1) | |
54 | ||
55 | targetPanel = Tile(self, log, bgColor=wx.Colour(80,10,10), active=0) | |
56 | buttonPanel = wx.Panel(self ,-1) | |
57 | sizer = wx.BoxSizer(wx.HORIZONTAL) | |
58 | target = targetPanel.tile | |
59 | ||
60 | sizer.Add((0,0), 1) | |
61 | for factor in [0.2, 0.3, 0.4, 0.5, 0.6, 0.7]: | |
62 | sizer.Add(Tile(buttonPanel, log, factor-0.05, target), 0, wx.ALIGN_CENTER) | |
63 | sizer.Add((0,0),1) | |
64 | sizer.Add(Tile(buttonPanel, log, factor, target), 0, wx.ALIGN_CENTER) | |
65 | sizer.Add((0,0),1) | |
66 | ||
67 | buttonPanel.SetSizer(sizer) | |
68 | sizer.Fit(buttonPanel) | |
69 | ||
70 | sizer = wx.BoxSizer(wx.VERTICAL) | |
71 | sizer.Add(title1, 0, wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, 6) | |
72 | sizer.Add(message0, 0, wx.ALIGN_CENTER | wx.ALL, 6) | |
73 | sizer.Add(title2, 0, wx.ALIGN_CENTER | wx.LEFT | wx.TOP | wx.RIGHT, 16) | |
74 | sizer.Add(message1, 0, wx.ALIGN_CENTER | wx.ALL, 6) | |
75 | sizer.Add(buttonPanel, 0, wx.EXPAND | wx.LEFT | wx.BOTTOM | wx.RIGHT, 16) | |
76 | sizer.Add(title3, 0, wx.ALIGN_CENTER | wx.LEFT | wx.RIGHT, 16) | |
77 | sizer.Add(message2, 0, wx.ALIGN_CENTER | wx.ALL, 6) | |
78 | sizer.Add(targetPanel, 2, wx.EXPAND | wx.LEFT | wx.BOTTOM | wx.RIGHT, 16) | |
79 | self.SetSizer(sizer) | |
80 | ||
81 | ||
82 | ||
83 | class Tile(wx.Window): | |
84 | """ | |
85 | This outer class is responsible for changing | |
86 | its border color in response to certain mouse | |
87 | events over its contained 'InnerTile'. | |
88 | """ | |
89 | normal = wx.Colour(150,150,150) | |
90 | active = wx.Colour(250,245,245) | |
91 | hover = wx.Colour(210,220,210) | |
92 | ||
93 | def __init__(self, parent, log, factor=1, thingToWatch=None, bgColor=None, active=1, size=(38,38), borderWidth=3): | |
94 | wx.Window.__init__(self, parent, -1, size=size, style=wx.CLIP_CHILDREN) | |
95 | self.tile = InnerTile(self, log, factor, thingToWatch, bgColor) | |
96 | self.log = log | |
97 | sizer = wx.BoxSizer(wx.HORIZONTAL) | |
98 | sizer.Add(self.tile, 1, wx.EXPAND | wx.ALL, borderWidth) | |
99 | self.SetSizer(sizer) | |
100 | self.Layout() | |
101 | em.eventManager.Register(self.doLayout, wx.EVT_SIZE, self) | |
102 | self.SetBackgroundColour(Tile.normal) | |
103 | if active: | |
104 | # Register myself for mouse events over self.tile in order to | |
105 | # create typical button/hyperlink visual effects. | |
106 | em.eventManager.Register(self.setHover, wx.EVT_ENTER_WINDOW, self.tile) | |
107 | em.eventManager.Register(self.setNormal, wx.EVT_LEAVE_WINDOW, self.tile) | |
108 | em.eventManager.Register(self.setActive, wx.EVT_LEFT_DOWN, self.tile) | |
109 | em.eventManager.Register(self.setHover, wx.EVT_LEFT_UP, self.tile) | |
110 | ||
111 | ||
112 | def doLayout(self, event): | |
113 | self.Layout() | |
114 | ||
115 | ||
116 | def setHover(self, event): | |
117 | self.SetBackgroundColour(Tile.hover) | |
118 | self.Refresh() | |
119 | ||
120 | ||
121 | def setActive(self, event): | |
122 | self.SetBackgroundColour(Tile.active) | |
123 | self.Refresh() | |
124 | ||
125 | ||
126 | def setNormal(self, event): | |
127 | self.SetBackgroundColour(Tile.normal) | |
128 | self.Refresh() | |
129 | ||
130 | ||
131 | ||
132 | class InnerTile(wx.Window): | |
133 | IDLE_COLOR = wx.Colour( 80, 10, 10) | |
134 | START_COLOR = wx.Colour(200, 70, 50) | |
135 | FINAL_COLOR = wx.Colour( 20, 80,240) | |
136 | OFF_COLOR = wx.Colour(185,190,185) | |
137 | # Some pre-computation. | |
138 | DELTAS = map(lambda a,b: b-a, START_COLOR.Get(), FINAL_COLOR.Get()) | |
139 | START_COLOR_TUPLE = START_COLOR.Get() | |
140 | ||
141 | """ | |
142 | This inner panel changes its color in reaction to mouse | |
143 | events over the 'thingToWatch'. | |
144 | """ | |
145 | def __init__(self, parent, log, factor, thingToWatch=None, bgColor=None): | |
146 | wx.Window.__init__(self, parent, -1) | |
147 | self.log=log | |
148 | if bgColor: | |
149 | self.SetBackgroundColour(bgColor) | |
150 | if thingToWatch: | |
151 | self.factor = factor | |
152 | self.thingToWatch = thingToWatch | |
153 | self.state = 0 | |
154 | self.toggleOnOff() | |
155 | # Watch for the mouse click to enable/disable myself. | |
156 | em.eventManager.Register(self.toggleOnOff, wx.EVT_LEFT_UP, self) | |
157 | ||
158 | ||
159 | def toggleOnOff(self, event=None): | |
160 | # Implement being on or off by registering and | |
161 | # de-registering self.makeColor() from the event manager. | |
162 | if self.state: | |
163 | em.eventManager.DeregisterListener(self.makeColor) | |
164 | else: | |
165 | em.eventManager.Register(self.makeColor, wx.EVT_MOTION, self.thingToWatch) | |
166 | self.state = 1 - self.state | |
167 | self.resetColor() | |
168 | ||
169 | ||
170 | def resetColor(self, event=None): | |
171 | if self.state: | |
172 | self.setColor(InnerTile.IDLE_COLOR) | |
173 | else: | |
174 | self.setColor(InnerTile.OFF_COLOR) | |
175 | ||
176 | ||
177 | def setColor(self, color): | |
178 | self.SetBackgroundColour(color) | |
179 | self.Refresh() | |
180 | ||
181 | ||
182 | def makeColor(self, mouseEvent): | |
183 | self.makeColorFromTuple(mouseEvent.GetPositionTuple()) | |
184 | ||
185 | ||
186 | def makeColorFromTuple(self, (x, y)): | |
187 | MAX = 180.0 | |
188 | scaled = min((x + y) * self.factor, MAX) # In range [0..MAX] | |
189 | percent = scaled / MAX | |
190 | r = InnerTile.START_COLOR_TUPLE[0] + (InnerTile.DELTAS[0] * percent) | |
191 | g = InnerTile.START_COLOR_TUPLE[1] + (InnerTile.DELTAS[1] * percent) | |
192 | b = InnerTile.START_COLOR_TUPLE[2] + (InnerTile.DELTAS[2] * percent) | |
193 | self.setColor(wx.Colour(int(r), int(g), int(b))) | |
194 | ||
195 | ||
196 | ||
197 | ||
198 | #---------------------------------------------------------------------- | |
199 | ||
200 | def runTest(frame, nb, log): | |
201 | win = TestPanel(nb, log) | |
202 | return win | |
203 | ||
204 | #---------------------------------------------------------------------- | |
205 | ||
206 | ||
207 | ||
208 | overview = """<html><body> | |
209 | <h2>EventManager</h2> | |
210 | ||
211 | <p> The goal of the EventManager is to make wxWindows events more | |
212 | 'Pythonic' (ie. object-oriented) and easier to work with, without | |
213 | impacting performance. It offers these features: | |
214 | ||
215 | <p> | |
216 | <ul> | |
217 | ||
218 | <li> Allows any number of listeners to register for a single | |
219 | event. (In addition to the standard wxPython feature of a single | |
220 | listener being able to respond to many events.) | |
221 | ||
222 | <li> Makes it easy to disconnect and reconnect listeners. This | |
223 | has the effect of reducing the need for case-based branching in | |
224 | application code. | |
225 | ||
226 | <li> Has an object-oriented API. Programmers register to get | |
227 | events directly from the objects that generate them, instead of | |
228 | using ID numbers. | |
229 | ||
230 | </ul> | |
231 | ||
232 | <h3>Usage</h3> | |
233 | ||
234 | <p>The EventManager class has three public methods. First get a | |
235 | reference to it: | |
236 | ||
237 | <PRE> | |
238 | from wxPython.lib.evtmgr import eventManager | |
239 | </PRE> | |
240 | ||
241 | <p>...and then invoke any of the following methods. These methods are | |
242 | 'safe'; duplicate registrations or de-registrations will have no | |
243 | effect. | |
244 | ||
245 | <p><b>Registering a listener:</b> | |
246 | ||
247 | <PRE> | |
248 | eventManager.Register(listener, event, event-source) | |
249 | </PRE> | |
250 | ||
251 | ||
252 | <p><b>De-registering by window:</b> | |
253 | ||
254 | <PRE> | |
255 | eventManager.DeregisterWindow(event-source) | |
256 | </PRE> | |
257 | ||
258 | ||
259 | <p><b>De-registering by listener:</b> | |
260 | ||
261 | <PRE> | |
262 | eventManager.DeregisterListener(listener) | |
263 | </PRE> | |
264 | ||
265 | <p><b>Simple Example:</b> | |
266 | ||
267 | <PRE> | |
268 | from wxPython.lib.evtmgr import eventManager | |
269 | ||
270 | aButton = wxButton(somePanel, -1, 'Click me') | |
271 | eventManager.Register(self.someMethod, EVT_BUTTON, aButton) | |
272 | </PRE> | |
273 | ||
274 | <p> See the demo code as well as the documentation in the source of | |
275 | <tt>wxPython.lib.evtmgr</tt> for more details. | |
276 | ||
277 | ||
278 | <p> | |
279 | by Robb Shecter (robb@acm.org) | |
280 | </body></html> | |
281 | """ | |
282 | ||
283 | ||
284 | ||
285 | if __name__ == '__main__': | |
286 | import sys,os | |
287 | import run | |
288 | run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) | |
289 |