]>
Commit | Line | Data |
---|---|---|
0b0849b5 RD |
1 | """ |
2 | ||
3 | Module that holds the GUI modes used by FloatCanvas | |
4 | ||
5 | ||
6 | Note that this can only be imported after a wx.App() has been created. | |
7 | ||
8 | This approach was inpired by Christian Blouin, who also wrote the initial | |
9 | version of the code. | |
10 | ||
11 | """ | |
12 | ||
13 | import wx | |
14 | ## fixme: events should live in their own module, so all of FloatCanvas | |
15 | ## wouldn't have to be imported here. | |
16 | import FloatCanvas, Resources | |
17 | import numpy as N | |
18 | ||
19 | ## create all the Cursors, so they don't need to be created each time. | |
20 | if "wxMac" in wx.PlatformInfo: # use 16X16 cursors for wxMac | |
21 | HandCursor = wx.CursorFromImage(Resources.getHand16Image()) | |
22 | GrabHandCursor = wx.CursorFromImage(Resources.getGrabHand16Image()) | |
23 | ||
24 | img = Resources.getMagPlus16Image() | |
25 | img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6) | |
26 | img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6) | |
27 | MagPlusCursor = wx.CursorFromImage(img) | |
28 | ||
29 | img = Resources.getMagMinus16Image() | |
30 | img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6) | |
31 | img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6) | |
32 | MagMinusCursor = wx.CursorFromImage(img) | |
33 | else: # use 24X24 cursors for GTK and Windows | |
34 | HandCursor = wx.CursorFromImage(Resources.getHandImage()) | |
35 | GrabHandCursor = wx.CursorFromImage(Resources.getGrabHandImage()) | |
36 | ||
37 | img = Resources.getMagPlusImage() | |
38 | img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9) | |
39 | img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9) | |
40 | MagPlusCursor = wx.CursorFromImage(img) | |
41 | ||
42 | img = Resources.getMagMinusImage() | |
43 | img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9) | |
44 | img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9) | |
45 | MagMinusCursor = wx.CursorFromImage(img) | |
46 | ||
47 | ||
48 | class GUIBase: | |
49 | """ | |
50 | Basic Mouse mode and baseclass for other GUImode. | |
51 | ||
52 | This one does nothing with any event | |
53 | ||
54 | """ | |
55 | def __init__(self, parent): | |
56 | self.parent = parent | |
57 | ||
58 | Cursor = wx.NullCursor | |
59 | ||
60 | # Handlers | |
61 | def OnLeftDown(self, event): | |
62 | pass | |
63 | def OnLeftUp(self, event): | |
64 | pass | |
65 | def OnLeftDouble(self, event): | |
66 | pass | |
67 | def OnRightDown(self, event): | |
68 | pass | |
69 | def OnRightUp(self, event): | |
70 | pass | |
71 | def OnRightDouble(self, event): | |
72 | pass | |
73 | def OnMiddleDown(self, event): | |
74 | pass | |
75 | def OnMiddleUp(self, event): | |
76 | pass | |
77 | def OnMiddleDouble(self, event): | |
78 | pass | |
79 | def OnWheel(self, event): | |
80 | pass | |
81 | def OnMove(self, event): | |
82 | pass | |
83 | ||
84 | def UpdateScreen(self): | |
85 | """ | |
86 | Update gets called if the screen has been repainted in the middle of a zoom in | |
87 | so the Rubber Band Box can get updated | |
88 | """ | |
89 | pass | |
90 | ||
91 | class GUIMouse(GUIBase): | |
92 | """ | |
93 | ||
94 | Mouse mode checks for a hit test, and if nothing is hit, | |
95 | raises a FloatCanvas mouse event for each event. | |
96 | ||
97 | """ | |
98 | ||
99 | Cursor = wx.NullCursor | |
100 | ||
101 | # Handlers | |
102 | def OnLeftDown(self, event): | |
103 | EventType = FloatCanvas.EVT_FC_LEFT_DOWN | |
104 | if not self.parent.HitTest(event, EventType): | |
105 | self.parent._RaiseMouseEvent(event, EventType) | |
106 | ||
107 | def OnLeftUp(self, event): | |
108 | EventType = FloatCanvas.EVT_FC_LEFT_UP | |
109 | if not self.parent.HitTest(event, EventType): | |
110 | self.parent._RaiseMouseEvent(event, EventType) | |
111 | ||
112 | def OnLeftDouble(self, event): | |
113 | EventType = FloatCanvas.EVT_FC_LEFT_DCLICK | |
114 | if not self.parent.HitTest(event, EventType): | |
115 | self.parent._RaiseMouseEvent(event, EventType) | |
116 | ||
117 | def OnMiddleDown(self, event): | |
118 | EventType = FloatCanvas.EVT_FC_MIDDLE_DOWN | |
119 | if not self.parent.HitTest(event, EventType): | |
120 | self.parent._RaiseMouseEvent(event, EventType) | |
121 | ||
122 | def OnMiddleUp(self, event): | |
123 | EventType = FloatCanvas.EVT_FC_MIDDLE_UP | |
124 | if not self.parent.HitTest(event, EventType): | |
125 | self.parent._RaiseMouseEvent(event, EventType) | |
126 | ||
127 | def OnMiddleDouble(self, event): | |
128 | EventType = FloatCanvas.EVT_FC_MIDDLE_DCLICK | |
129 | if not self.parent.HitTest(event, EventType): | |
130 | self.parent._RaiseMouseEvent(event, EventType) | |
131 | ||
132 | def OnRightDown(self, event): | |
133 | EventType = FloatCanvas.EVT_FC_RIGHT_DOWN | |
134 | if not self.parent.HitTest(event, EventType): | |
135 | self.parent._RaiseMouseEvent(event, EventType) | |
136 | ||
137 | def OnRightUp(self, event): | |
138 | EventType = FloatCanvas.EVT_FC_RIGHT_UP | |
139 | if not self.parent.HitTest(event, EventType): | |
140 | self.parent._RaiseMouseEvent(event, EventType) | |
141 | ||
142 | def OnRightDouble(self, event): | |
143 | EventType = FloatCanvas.EVT_FC_RIGHT_DCLICK | |
144 | if not self.parent.HitTest(event, EventType): | |
145 | self.parent._RaiseMouseEvent(event, EventType) | |
146 | ||
147 | def OnWheel(self, event): | |
148 | EventType = FloatCanvas.EVT_FC_MOUSEWHEEL | |
149 | self.parent._RaiseMouseEvent(event, EventType) | |
150 | ||
151 | def OnMove(self, event): | |
152 | ## The Move event always gets raised, even if there is a hit-test | |
153 | self.parent.MouseOverTest(event) | |
154 | self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION) | |
155 | ||
156 | ||
157 | class GUIMove(GUIBase): | |
158 | ||
159 | Cursor = HandCursor | |
160 | GrabCursor = GrabHandCursor | |
161 | def __init__(self, parent): | |
162 | GUIBase.__init__(self, parent) | |
163 | self.StartMove = None | |
164 | self.PrevMoveXY = None | |
165 | ||
166 | def OnLeftDown(self, event): | |
167 | self.parent.SetCursor(self.GrabCursor) | |
168 | self.parent.CaptureMouse() | |
169 | self.StartMove = N.array( event.GetPosition() ) | |
170 | self.PrevMoveXY = (0,0) | |
171 | ||
172 | def OnLeftUp(self, event): | |
173 | if self.StartMove is not None: | |
174 | StartMove = self.StartMove | |
175 | EndMove = N.array(event.GetPosition()) | |
176 | DiffMove = StartMove-EndMove | |
177 | if N.sum(DiffMove**2) > 16: | |
178 | self.parent.MoveImage(DiffMove, 'Pixel') | |
179 | self.StartMove = None | |
180 | self.parent.SetCursor(self.Cursor) | |
181 | ||
182 | def OnMove(self, event): | |
183 | # Allways raise the Move event. | |
184 | self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION) | |
185 | if event.Dragging() and event.LeftIsDown() and not self.StartMove is None: | |
186 | xy1 = N.array( event.GetPosition() ) | |
187 | wh = self.parent.PanelSize | |
188 | xy_tl = xy1 - self.StartMove | |
189 | dc = wx.ClientDC(self.parent) | |
190 | dc.BeginDrawing() | |
191 | x1,y1 = self.PrevMoveXY | |
192 | x2,y2 = xy_tl | |
193 | w,h = self.parent.PanelSize | |
194 | ##fixme: This sure could be cleaner! | |
195 | if x2 > x1 and y2 > y1: | |
196 | xa = xb = x1 | |
197 | ya = yb = y1 | |
198 | wa = w | |
199 | ha = y2 - y1 | |
200 | wb = x2- x1 | |
201 | hb = h | |
202 | elif x2 > x1 and y2 <= y1: | |
203 | xa = x1 | |
204 | ya = y1 | |
205 | wa = x2 - x1 | |
206 | ha = h | |
207 | xb = x1 | |
208 | yb = y2 + h | |
209 | wb = w | |
210 | hb = y1 - y2 | |
211 | elif x2 <= x1 and y2 > y1: | |
212 | xa = x1 | |
213 | ya = y1 | |
214 | wa = w | |
215 | ha = y2 - y1 | |
216 | xb = x2 + w | |
217 | yb = y1 | |
218 | wb = x1 - x2 | |
219 | hb = h - y2 + y1 | |
220 | elif x2 <= x1 and y2 <= y1: | |
221 | xa = x2 + w | |
222 | ya = y1 | |
223 | wa = x1 - x2 | |
224 | ha = h | |
225 | xb = x1 | |
226 | yb = y2 + h | |
227 | wb = w | |
228 | hb = y1 - y2 | |
229 | ||
230 | dc.SetPen(wx.TRANSPARENT_PEN) | |
231 | dc.SetBrush(self.parent.BackgroundBrush) | |
232 | dc.DrawRectangle(xa, ya, wa, ha) | |
233 | dc.DrawRectangle(xb, yb, wb, hb) | |
234 | self.PrevMoveXY = xy_tl | |
235 | if self.parent._ForeDrawList: | |
236 | dc.DrawBitmapPoint(self.parent._ForegroundBuffer,xy_tl) | |
237 | else: | |
238 | dc.DrawBitmapPoint(self.parent._Buffer,xy_tl) | |
239 | dc.EndDrawing() | |
240 | ||
241 | def OnWheel(self, event): | |
242 | """ | |
243 | By default, zoom in/out by a 0.1 factor per Wheel event. | |
244 | """ | |
245 | if event.GetWheelRotation() < 0: | |
246 | self.parent.Zoom(0.9) | |
247 | else: | |
248 | self.parent.Zoom(1.1) | |
249 | ||
250 | class GUIZoomIn(GUIBase): | |
251 | ||
252 | Cursor = MagPlusCursor | |
253 | ||
254 | def __init__(self, parent): | |
255 | GUIBase.__init__(self, parent) | |
256 | self.StartRBBox = None | |
257 | self.PrevRBBox = None | |
258 | ||
259 | def OnLeftDown(self, event): | |
260 | self.StartRBBox = N.array( event.GetPosition() ) | |
261 | self.PrevRBBox = None | |
262 | self.parent.CaptureMouse() | |
263 | ||
264 | def OnLeftUp(self, event): | |
265 | #if self.parent.HasCapture(): | |
266 | # self.parent.ReleaseMouse() | |
267 | if event.LeftUp() and not self.StartRBBox is None: | |
268 | self.PrevRBBox = None | |
269 | EndRBBox = event.GetPosition() | |
270 | StartRBBox = self.StartRBBox | |
271 | # if mouse has moved less that ten pixels, don't use the box. | |
272 | if ( abs(StartRBBox[0] - EndRBBox[0]) > 10 | |
273 | and abs(StartRBBox[1] - EndRBBox[1]) > 10 ): | |
274 | EndRBBox = self.parent.PixelToWorld(EndRBBox) | |
275 | StartRBBox = self.parent.PixelToWorld(StartRBBox) | |
276 | BB = N.array(((min(EndRBBox[0],StartRBBox[0]), | |
277 | min(EndRBBox[1],StartRBBox[1])), | |
278 | (max(EndRBBox[0],StartRBBox[0]), | |
279 | max(EndRBBox[1],StartRBBox[1]))),N.float_) | |
280 | self.parent.ZoomToBB(BB) | |
281 | else: | |
282 | Center = self.parent.PixelToWorld(StartRBBox) | |
283 | self.parent.Zoom(1.5,Center) | |
284 | self.StartRBBox = None | |
285 | ||
286 | def OnMove(self, event): | |
287 | # Allways raise the Move event. | |
288 | self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION) | |
289 | if event.Dragging() and event.LeftIsDown() and not (self.StartRBBox is None): | |
290 | xy0 = self.StartRBBox | |
291 | xy1 = N.array( event.GetPosition() ) | |
292 | wh = abs(xy1 - xy0) | |
293 | wh[0] = max(wh[0], int(wh[1]*self.parent.AspectRatio)) | |
294 | wh[1] = int(wh[0] / self.parent.AspectRatio) | |
295 | xy_c = (xy0 + xy1) / 2 | |
296 | dc = wx.ClientDC(self.parent) | |
297 | dc.BeginDrawing() | |
298 | dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH)) | |
299 | dc.SetBrush(wx.TRANSPARENT_BRUSH) | |
300 | dc.SetLogicalFunction(wx.XOR) | |
301 | if self.PrevRBBox: | |
302 | dc.DrawRectanglePointSize(*self.PrevRBBox) | |
303 | self.PrevRBBox = ( xy_c - wh/2, wh ) | |
304 | dc.DrawRectanglePointSize( *self.PrevRBBox ) | |
305 | dc.EndDrawing() | |
306 | ||
307 | def UpdateScreen(self): | |
308 | """ | |
309 | Update gets called if the screen has been repainted in the middle of a zoom in | |
310 | so the Rubber Band Box can get updated | |
311 | """ | |
312 | if self.PrevRBBox is not None: | |
313 | dc = wx.ClientDC(self.parent) | |
314 | dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH)) | |
315 | dc.SetBrush(wx.TRANSPARENT_BRUSH) | |
316 | dc.SetLogicalFunction(wx.XOR) | |
317 | dc.DrawRectanglePointSize(*self.PrevRBBox) | |
318 | ||
319 | def OnRightDown(self, event): | |
320 | self.parent.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel") | |
321 | ||
322 | def OnWheel(self, event): | |
323 | if event.GetWheelRotation() < 0: | |
324 | self.parent.Zoom(0.9) | |
325 | else: | |
326 | self.parent.Zoom(1.1) | |
327 | ||
328 | class GUIZoomOut(GUIBase): | |
329 | ||
330 | Cursor = MagMinusCursor | |
331 | ||
332 | def OnLeftDown(self, event): | |
333 | self.parent.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel") | |
334 | ||
335 | def OnRightDown(self, event): | |
336 | self.parent.Zoom(1.5, event.GetPosition(), centerCoords="pixel") | |
337 | ||
338 | def OnWheel(self, event): | |
339 | if event.GetWheelRotation() < 0: | |
340 | self.parent.Zoom(0.9) | |
341 | else: | |
342 | self.parent.Zoom(1.1) | |
343 | ||
344 | def OnMove(self, event): | |
345 | # Allways raise the Move event. | |
346 | self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION) | |
347 |