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

1 by Brian Sidebotham
Initial import into Bazaar
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