4
Module that holds the GUI modes used by FloatCanvas
6
Note that this can only be imported after a wx.App() has been created.
8
This approach was inpired by Christian Blouin, who also wrote the initial
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
from Utilities import BBox
20
class Cursors(object):
22
Class to hold the standard Cursors
26
if "wxMac" in wx.PlatformInfo: # use 16X16 cursors for wxMac
27
self.HandCursor = wx.CursorFromImage(Resources.getHand16Image())
28
self.GrabHandCursor = wx.CursorFromImage(Resources.getGrabHand16Image())
30
img = Resources.getMagPlus16Image()
31
img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
32
img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
33
self.MagPlusCursor = wx.CursorFromImage(img)
35
img = Resources.getMagMinus16Image()
36
img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
37
img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
38
self.MagMinusCursor = wx.CursorFromImage(img)
39
else: # use 24X24 cursors for GTK and Windows
40
self.HandCursor = wx.CursorFromImage(Resources.getHandImage())
41
self.GrabHandCursor = wx.CursorFromImage(Resources.getGrabHandImage())
43
img = Resources.getMagPlusImage()
44
img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
45
img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
46
self.MagPlusCursor = wx.CursorFromImage(img)
48
img = Resources.getMagMinusImage()
49
img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
50
img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
51
self.MagMinusCursor = wx.CursorFromImage(img)
54
class GUIBase(object):
56
Basic Mouse mode and baseclass for other GUImode.
58
This one does nothing with any event
61
def __init__(self, Canvas=None):
62
self.Canvas = Canvas # set the FloatCanvas for the mode
63
# it gets set when the Mode is set on the Canvas.
64
self.Cursors = Cursors()
66
Cursor = wx.NullCursor
69
this method gets called by FloatCanvas when a new mode is being set
74
def OnLeftDown(self, event):
76
def OnLeftUp(self, event):
78
def OnLeftDouble(self, event):
80
def OnRightDown(self, event):
82
def OnRightUp(self, event):
84
def OnRightDouble(self, event):
86
def OnMiddleDown(self, event):
88
def OnMiddleUp(self, event):
90
def OnMiddleDouble(self, event):
92
def OnWheel(self, event):
94
def OnMove(self, event):
96
def OnKeyDown(self, event):
98
def OnKeyUp(self, event):
100
def UpdateScreen(self):
102
Update gets called if the screen has been repainted in the middle of a zoom in
103
so the Rubber Band Box can get updated. Other GUIModes may require something similar
107
class GUIMouse(GUIBase):
110
Mouse mode checks for a hit test, and if nothing is hit,
111
raises a FloatCanvas mouse event for each event.
115
Cursor = wx.NullCursor
118
def OnLeftDown(self, event):
119
EventType = FloatCanvas.EVT_FC_LEFT_DOWN
120
if not self.Canvas.HitTest(event, EventType):
121
self.Canvas._RaiseMouseEvent(event, EventType)
123
def OnLeftUp(self, event):
124
EventType = FloatCanvas.EVT_FC_LEFT_UP
125
if not self.Canvas.HitTest(event, EventType):
126
self.Canvas._RaiseMouseEvent(event, EventType)
128
def OnLeftDouble(self, event):
129
EventType = FloatCanvas.EVT_FC_LEFT_DCLICK
130
if not self.Canvas.HitTest(event, EventType):
131
self.Canvas._RaiseMouseEvent(event, EventType)
133
def OnMiddleDown(self, event):
134
EventType = FloatCanvas.EVT_FC_MIDDLE_DOWN
135
if not self.Canvas.HitTest(event, EventType):
136
self.Canvas._RaiseMouseEvent(event, EventType)
138
def OnMiddleUp(self, event):
139
EventType = FloatCanvas.EVT_FC_MIDDLE_UP
140
if not self.Canvas.HitTest(event, EventType):
141
self.Canvas._RaiseMouseEvent(event, EventType)
143
def OnMiddleDouble(self, event):
144
EventType = FloatCanvas.EVT_FC_MIDDLE_DCLICK
145
if not self.Canvas.HitTest(event, EventType):
146
self.Canvas._RaiseMouseEvent(event, EventType)
148
def OnRightDown(self, event):
149
EventType = FloatCanvas.EVT_FC_RIGHT_DOWN
150
if not self.Canvas.HitTest(event, EventType):
151
self.Canvas._RaiseMouseEvent(event, EventType)
153
def OnRightUp(self, event):
154
EventType = FloatCanvas.EVT_FC_RIGHT_UP
155
if not self.Canvas.HitTest(event, EventType):
156
self.Canvas._RaiseMouseEvent(event, EventType)
158
def OnRightDouble(self, event):
159
EventType = FloatCanvas.EVT_FC_RIGHT_DCLICK
160
if not self.Canvas.HitTest(event, EventType):
161
self.Canvas._RaiseMouseEvent(event, EventType)
163
def OnWheel(self, event):
164
EventType = FloatCanvas.EVT_FC_MOUSEWHEEL
165
self.Canvas._RaiseMouseEvent(event, EventType)
167
def OnMove(self, event):
168
## The Move event always gets raised, even if there is a hit-test
169
self.Canvas.MouseOverTest(event)
170
self.Canvas._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
173
class GUIMove(GUIBase):
175
Mode that moves the image (pans).
176
It doesn't change any coordinates, it only changes what the viewport is
178
def __init__(self, canvas=None):
179
GUIBase.__init__(self, canvas)
180
self.Cursor = self.Cursors.HandCursor
181
self.GrabCursor = self.Cursors.GrabHandCursor
182
self.StartMove = None
184
self.PrevMoveXY = None
186
## timer to give a delay when moving so that buffers aren't re-built too many times.
187
self.MoveTimer = wx.PyTimer(self.OnMoveTimer)
189
def OnLeftDown(self, event):
190
self.Canvas.SetCursor(self.GrabCursor)
191
self.Canvas.CaptureMouse()
192
self.StartMove = N.array( event.GetPosition() )
193
self.MidMove = self.StartMove
194
self.PrevMoveXY = (0,0)
196
def OnLeftUp(self, event):
197
self.Canvas.SetCursor(self.Cursor)
198
if self.StartMove is not None:
199
self.EndMove = N.array(event.GetPosition())
200
DiffMove = self.MidMove-self.EndMove
201
self.Canvas.MoveImage(DiffMove, 'Pixel', ReDraw=True)
203
def OnMove(self, event):
204
# Always raise the Move event.
205
self.Canvas._RaiseMouseEvent(event, FloatCanvas.EVT_FC_MOTION)
206
if event.Dragging() and event.LeftIsDown() and not self.StartMove is None:
207
self.EndMove = N.array(event.GetPosition())
208
self.MoveImage(event)
209
DiffMove = self.MidMove-self.EndMove
210
self.Canvas.MoveImage(DiffMove, 'Pixel', ReDraw=False)# reset the canvas without re-drawing
211
self.MidMove = self.EndMove
212
self.MoveTimer.Start(30, oneShot=True)
214
def OnMoveTimer(self, event=None):
217
def UpdateScreen(self):
218
## The screen has been re-drawn, so StartMove needs to be reset.
219
self.StartMove = self.MidMove
221
def MoveImage(self, event ):
222
#xy1 = N.array( event.GetPosition() )
224
wh = self.Canvas.PanelSize
225
xy_tl = xy1 - self.StartMove
226
dc = wx.ClientDC(self.Canvas)
228
x1,y1 = self.PrevMoveXY
230
w,h = self.Canvas.PanelSize
231
##fixme: This sure could be cleaner!
232
## This is all to fill in the background with the background color
233
## without flashing as the image moves.
234
if x2 > x1 and y2 > y1:
241
elif x2 > x1 and y2 <= y1:
250
elif x2 <= x1 and y2 > y1:
259
elif x2 <= x1 and y2 <= y1:
269
dc.SetPen(wx.TRANSPARENT_PEN)
270
dc.SetBrush(self.Canvas.BackgroundBrush)
271
dc.DrawRectangle(xa, ya, wa, ha)
272
dc.DrawRectangle(xb, yb, wb, hb)
273
self.PrevMoveXY = xy_tl
274
if self.Canvas._ForeDrawList:
275
dc.DrawBitmapPoint(self.Canvas._ForegroundBuffer,xy_tl)
277
dc.DrawBitmapPoint(self.Canvas._Buffer,xy_tl)
279
#self.Canvas.Update()
281
def OnWheel(self, event):
283
By default, zoom in/out by a 0.1 factor per Wheel event.
285
if event.GetWheelRotation() < 0:
286
self.Canvas.Zoom(0.9)
288
self.Canvas.Zoom(1.1)
290
class GUIZoomIn(GUIBase):
292
def __init__(self, canvas=None):
293
GUIBase.__init__(self, canvas)
294
self.StartRBBox = None
295
self.PrevRBBox = None
296
self.Cursor = self.Cursors.MagPlusCursor
298
def OnLeftDown(self, event):
299
self.StartRBBox = N.array( event.GetPosition() )
300
self.PrevRBBox = None
301
self.Canvas.CaptureMouse()
303
def OnLeftUp(self, event):
304
if event.LeftUp() and not self.StartRBBox is None:
305
self.PrevRBBox = None
306
EndRBBox = event.GetPosition()
307
StartRBBox = self.StartRBBox
308
# if mouse has moved less that ten pixels, don't use the box.
309
if ( abs(StartRBBox[0] - EndRBBox[0]) > 10
310
and abs(StartRBBox[1] - EndRBBox[1]) > 10 ):
311
EndRBBox = self.Canvas.PixelToWorld(EndRBBox)
312
StartRBBox = self.Canvas.PixelToWorld(StartRBBox)
313
self.Canvas.ZoomToBB( BBox.fromPoints(N.r_[EndRBBox,StartRBBox]) )
315
Center = self.Canvas.PixelToWorld(StartRBBox)
316
self.Canvas.Zoom(1.5,Center)
317
self.StartRBBox = None
319
def OnMove(self, event):
320
# Always raise the Move event.
321
self.Canvas._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
322
if event.Dragging() and event.LeftIsDown() and not (self.StartRBBox is None):
323
xy0 = self.StartRBBox
324
xy1 = N.array( event.GetPosition() )
326
wh[0] = max(wh[0], int(wh[1]*self.Canvas.AspectRatio))
327
wh[1] = int(wh[0] / self.Canvas.AspectRatio)
328
xy_c = (xy0 + xy1) / 2
329
dc = wx.ClientDC(self.Canvas)
331
dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
332
dc.SetBrush(wx.TRANSPARENT_BRUSH)
333
dc.SetLogicalFunction(wx.XOR)
335
dc.DrawRectanglePointSize(*self.PrevRBBox)
336
self.PrevRBBox = ( xy_c - wh/2, wh )
337
dc.DrawRectanglePointSize( *self.PrevRBBox )
340
def UpdateScreen(self):
342
Update gets called if the screen has been repainted in the middle of a zoom in
343
so the Rubber Band Box can get updated
346
if self.PrevRBBox is not None:
347
dc = wx.ClientDC(self.Canvas)
348
dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
349
dc.SetBrush(wx.TRANSPARENT_BRUSH)
350
dc.SetLogicalFunction(wx.XOR)
351
dc.DrawRectanglePointSize(*self.PrevRBBox)
353
def OnRightDown(self, event):
354
self.Canvas.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
356
def OnWheel(self, event):
357
if event.GetWheelRotation() < 0:
358
self.Canvas.Zoom(0.9)
360
self.Canvas.Zoom(1.1)
362
class GUIZoomOut(GUIBase):
364
def __init__(self, Canvas=None):
365
GUIBase.__init__(self, Canvas)
366
self.Cursor = self.Cursors.MagMinusCursor
368
def OnLeftDown(self, event):
369
self.Canvas.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
371
def OnRightDown(self, event):
372
self.Canvas.Zoom(1.5, event.GetPosition(), centerCoords="pixel")
374
def OnWheel(self, event):
375
if event.GetWheelRotation() < 0:
376
self.Canvas.Zoom(0.9)
378
self.Canvas.Zoom(1.1)
380
def OnMove(self, event):
381
# Always raise the Move event.
382
self.Canvas._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)