~brian-sidebotham/wxwidgets-cmake/wxpython-2.9.4

« back to all changes in this revision

Viewing changes to wxPython/wx/lib/floatcanvas/GUIMode.py

  • Committer: Brian Sidebotham
  • Date: 2013-08-03 14:30:08 UTC
  • Revision ID: brian.sidebotham@gmail.com-20130803143008-c7806tkych1tp6fc
Initial import into Bazaar

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
"""
 
3
 
 
4
Module that holds the GUI modes used by FloatCanvas
 
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
from Utilities import BBox
 
18
import numpy as N
 
19
 
 
20
class Cursors(object):
 
21
    """
 
22
    Class to hold the standard Cursors
 
23
    
 
24
    """
 
25
    def __init__(self):
 
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())
 
29
        
 
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)
 
34
        
 
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())
 
42
        
 
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)
 
47
        
 
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)
 
52
 
 
53
 
 
54
class GUIBase(object):
 
55
    """
 
56
    Basic Mouse mode and baseclass for other GUImode.
 
57
 
 
58
    This one does nothing with any event
 
59
 
 
60
    """
 
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()
 
65
 
 
66
    Cursor = wx.NullCursor
 
67
    def UnSet(self):
 
68
        """
 
69
        this method gets called by FloatCanvas when a new mode is being set
 
70
        on the Canvas
 
71
        """
 
72
        pass
 
73
    # Handlers
 
74
    def OnLeftDown(self, event):
 
75
        pass
 
76
    def OnLeftUp(self, event):
 
77
        pass
 
78
    def OnLeftDouble(self, event):
 
79
        pass
 
80
    def OnRightDown(self, event):
 
81
        pass
 
82
    def OnRightUp(self, event):
 
83
        pass
 
84
    def OnRightDouble(self, event):
 
85
        pass
 
86
    def OnMiddleDown(self, event):
 
87
        pass
 
88
    def OnMiddleUp(self, event):
 
89
        pass
 
90
    def OnMiddleDouble(self, event):
 
91
        pass
 
92
    def OnWheel(self, event):
 
93
        pass
 
94
    def OnMove(self, event):
 
95
        pass
 
96
    def OnKeyDown(self, event):
 
97
        pass
 
98
    def OnKeyUp(self, event):
 
99
        pass
 
100
    def UpdateScreen(self):
 
101
        """
 
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
 
104
        """
 
105
        pass
 
106
 
 
107
class GUIMouse(GUIBase):
 
108
    """
 
109
 
 
110
    Mouse mode checks for a hit test, and if nothing is hit,
 
111
    raises a FloatCanvas mouse event for each event.
 
112
 
 
113
    """
 
114
 
 
115
    Cursor = wx.NullCursor
 
116
 
 
117
    # Handlers
 
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)
 
122
 
 
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)
 
127
 
 
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)
 
132
 
 
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)
 
137
 
 
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)
 
142
 
 
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)
 
147
 
 
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)
 
152
 
 
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)
 
157
 
 
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)
 
162
 
 
163
    def OnWheel(self, event):
 
164
        EventType = FloatCanvas.EVT_FC_MOUSEWHEEL
 
165
        self.Canvas._RaiseMouseEvent(event, EventType)
 
166
 
 
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)
 
171
 
 
172
 
 
173
class GUIMove(GUIBase):
 
174
    """
 
175
    Mode that moves the image (pans).
 
176
    It doesn't change any coordinates, it only changes what the viewport is
 
177
    """
 
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
 
183
        self.MidMove = None
 
184
        self.PrevMoveXY = None
 
185
        
 
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)
 
188
 
 
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)
 
195
 
 
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)
 
202
 
 
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)
 
213
 
 
214
    def OnMoveTimer(self, event=None):
 
215
        self.Canvas.Draw()
 
216
 
 
217
    def UpdateScreen(self):
 
218
        ## The screen has been re-drawn, so StartMove needs to be reset.
 
219
        self.StartMove = self.MidMove
 
220
 
 
221
    def MoveImage(self, event ):
 
222
        #xy1 = N.array( event.GetPosition() )
 
223
        xy1 = self.EndMove
 
224
        wh = self.Canvas.PanelSize
 
225
        xy_tl = xy1 - self.StartMove
 
226
        dc = wx.ClientDC(self.Canvas)
 
227
        dc.BeginDrawing()
 
228
        x1,y1 = self.PrevMoveXY
 
229
        x2,y2 = xy_tl
 
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:
 
235
            xa = xb = x1
 
236
            ya = yb = y1
 
237
            wa = w
 
238
            ha = y2 - y1
 
239
            wb = x2-  x1
 
240
            hb = h
 
241
        elif x2 > x1 and y2 <= y1:
 
242
            xa = x1
 
243
            ya = y1
 
244
            wa = x2 - x1
 
245
            ha = h
 
246
            xb = x1
 
247
            yb = y2 + h
 
248
            wb = w
 
249
            hb = y1 - y2
 
250
        elif x2 <= x1 and y2 > y1:
 
251
            xa = x1
 
252
            ya = y1
 
253
            wa = w
 
254
            ha = y2 - y1
 
255
            xb = x2 + w
 
256
            yb = y1
 
257
            wb = x1 - x2
 
258
            hb = h - y2 + y1
 
259
        elif x2 <= x1 and y2 <= y1:
 
260
            xa = x2 + w
 
261
            ya = y1
 
262
            wa = x1 - x2
 
263
            ha = h
 
264
            xb = x1
 
265
            yb = y2 + h
 
266
            wb = w
 
267
            hb = y1 - y2
 
268
 
 
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)
 
276
        else:
 
277
            dc.DrawBitmapPoint(self.Canvas._Buffer,xy_tl)
 
278
        dc.EndDrawing()
 
279
        #self.Canvas.Update()
 
280
 
 
281
    def OnWheel(self, event):
 
282
        """
 
283
           By default, zoom in/out by a 0.1 factor per Wheel event.
 
284
        """
 
285
        if event.GetWheelRotation() < 0:
 
286
            self.Canvas.Zoom(0.9)
 
287
        else:
 
288
            self.Canvas.Zoom(1.1)
 
289
 
 
290
class GUIZoomIn(GUIBase):
 
291
 
 
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
 
297
 
 
298
    def OnLeftDown(self, event):
 
299
        self.StartRBBox = N.array( event.GetPosition() )
 
300
        self.PrevRBBox = None
 
301
        self.Canvas.CaptureMouse()
 
302
 
 
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]) )
 
314
            else:
 
315
                Center = self.Canvas.PixelToWorld(StartRBBox)
 
316
                self.Canvas.Zoom(1.5,Center)
 
317
            self.StartRBBox = None
 
318
 
 
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() )
 
325
            wh  = abs(xy1 - xy0)
 
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)
 
330
            dc.BeginDrawing()
 
331
            dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
 
332
            dc.SetBrush(wx.TRANSPARENT_BRUSH)
 
333
            dc.SetLogicalFunction(wx.XOR)
 
334
            if self.PrevRBBox:
 
335
                dc.DrawRectanglePointSize(*self.PrevRBBox)
 
336
            self.PrevRBBox = ( xy_c - wh/2, wh )
 
337
            dc.DrawRectanglePointSize( *self.PrevRBBox )
 
338
            dc.EndDrawing()
 
339
            
 
340
    def UpdateScreen(self):
 
341
        """
 
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
 
344
        """
 
345
        #if False:
 
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)
 
352
 
 
353
    def OnRightDown(self, event):
 
354
        self.Canvas.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
 
355
 
 
356
    def OnWheel(self, event):
 
357
        if event.GetWheelRotation() < 0:
 
358
            self.Canvas.Zoom(0.9)
 
359
        else:
 
360
            self.Canvas.Zoom(1.1)
 
361
 
 
362
class GUIZoomOut(GUIBase):
 
363
 
 
364
    def __init__(self, Canvas=None):
 
365
        GUIBase.__init__(self, Canvas)
 
366
        self.Cursor = self.Cursors.MagMinusCursor
 
367
        
 
368
    def OnLeftDown(self, event):
 
369
        self.Canvas.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
 
370
 
 
371
    def OnRightDown(self, event):
 
372
        self.Canvas.Zoom(1.5, event.GetPosition(), centerCoords="pixel")
 
373
 
 
374
    def OnWheel(self, event):
 
375
        if event.GetWheelRotation() < 0:
 
376
            self.Canvas.Zoom(0.9)
 
377
        else:
 
378
            self.Canvas.Zoom(1.1)
 
379
 
 
380
    def OnMove(self, event):
 
381
        # Always raise the Move event.
 
382
        self.Canvas._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
 
383