""" Module that holds the GUI modes used by FloatCanvas Note that this can only be imported after a wx.App() has been created. This approach was inpired by Christian Blouin, who also wrote the initial version of the code. """ import wx ## fixme: events should live in their own module, so all of FloatCanvas ## wouldn't have to be imported here. import FloatCanvas, Resources import numpy as N ## create all the Cursors, so they don't need to be created each time. if "wxMac" in wx.PlatformInfo: # use 16X16 cursors for wxMac HandCursor = wx.CursorFromImage(Resources.getHand16Image()) GrabHandCursor = wx.CursorFromImage(Resources.getGrabHand16Image()) img = Resources.getMagPlus16Image() img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6) img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6) MagPlusCursor = wx.CursorFromImage(img) img = Resources.getMagMinus16Image() img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6) img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6) MagMinusCursor = wx.CursorFromImage(img) else: # use 24X24 cursors for GTK and Windows HandCursor = wx.CursorFromImage(Resources.getHandImage()) GrabHandCursor = wx.CursorFromImage(Resources.getGrabHandImage()) img = Resources.getMagPlusImage() img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9) img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9) MagPlusCursor = wx.CursorFromImage(img) img = Resources.getMagMinusImage() img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9) img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9) MagMinusCursor = wx.CursorFromImage(img) class GUIBase: """ Basic Mouse mode and baseclass for other GUImode. This one does nothing with any event """ def __init__(self, parent): self.parent = parent Cursor = wx.NullCursor # Handlers def OnLeftDown(self, event): pass def OnLeftUp(self, event): pass def OnLeftDouble(self, event): pass def OnRightDown(self, event): pass def OnRightUp(self, event): pass def OnRightDouble(self, event): pass def OnMiddleDown(self, event): pass def OnMiddleUp(self, event): pass def OnMiddleDouble(self, event): pass def OnWheel(self, event): pass def OnMove(self, event): pass def UpdateScreen(self): """ Update gets called if the screen has been repainted in the middle of a zoom in so the Rubber Band Box can get updated """ pass class GUIMouse(GUIBase): """ Mouse mode checks for a hit test, and if nothing is hit, raises a FloatCanvas mouse event for each event. """ Cursor = wx.NullCursor # Handlers def OnLeftDown(self, event): EventType = FloatCanvas.EVT_FC_LEFT_DOWN if not self.parent.HitTest(event, EventType): self.parent._RaiseMouseEvent(event, EventType) def OnLeftUp(self, event): EventType = FloatCanvas.EVT_FC_LEFT_UP if not self.parent.HitTest(event, EventType): self.parent._RaiseMouseEvent(event, EventType) def OnLeftDouble(self, event): EventType = FloatCanvas.EVT_FC_LEFT_DCLICK if not self.parent.HitTest(event, EventType): self.parent._RaiseMouseEvent(event, EventType) def OnMiddleDown(self, event): EventType = FloatCanvas.EVT_FC_MIDDLE_DOWN if not self.parent.HitTest(event, EventType): self.parent._RaiseMouseEvent(event, EventType) def OnMiddleUp(self, event): EventType = FloatCanvas.EVT_FC_MIDDLE_UP if not self.parent.HitTest(event, EventType): self.parent._RaiseMouseEvent(event, EventType) def OnMiddleDouble(self, event): EventType = FloatCanvas.EVT_FC_MIDDLE_DCLICK if not self.parent.HitTest(event, EventType): self.parent._RaiseMouseEvent(event, EventType) def OnRightDown(self, event): EventType = FloatCanvas.EVT_FC_RIGHT_DOWN if not self.parent.HitTest(event, EventType): self.parent._RaiseMouseEvent(event, EventType) def OnRightUp(self, event): EventType = FloatCanvas.EVT_FC_RIGHT_UP if not self.parent.HitTest(event, EventType): self.parent._RaiseMouseEvent(event, EventType) def OnRightDouble(self, event): EventType = FloatCanvas.EVT_FC_RIGHT_DCLICK if not self.parent.HitTest(event, EventType): self.parent._RaiseMouseEvent(event, EventType) def OnWheel(self, event): EventType = FloatCanvas.EVT_FC_MOUSEWHEEL self.parent._RaiseMouseEvent(event, EventType) def OnMove(self, event): ## The Move event always gets raised, even if there is a hit-test self.parent.MouseOverTest(event) self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION) class GUIMove(GUIBase): Cursor = HandCursor GrabCursor = GrabHandCursor def __init__(self, parent): GUIBase.__init__(self, parent) self.StartMove = None self.PrevMoveXY = None def OnLeftDown(self, event): self.parent.SetCursor(self.GrabCursor) self.parent.CaptureMouse() self.StartMove = N.array( event.GetPosition() ) self.PrevMoveXY = (0,0) def OnLeftUp(self, event): if self.StartMove is not None: StartMove = self.StartMove EndMove = N.array(event.GetPosition()) DiffMove = StartMove-EndMove if N.sum(DiffMove**2) > 16: self.parent.MoveImage(DiffMove, 'Pixel') self.StartMove = None self.parent.SetCursor(self.Cursor) def OnMove(self, event): # Allways raise the Move event. self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION) if event.Dragging() and event.LeftIsDown() and not self.StartMove is None: xy1 = N.array( event.GetPosition() ) wh = self.parent.PanelSize xy_tl = xy1 - self.StartMove dc = wx.ClientDC(self.parent) dc.BeginDrawing() x1,y1 = self.PrevMoveXY x2,y2 = xy_tl w,h = self.parent.PanelSize ##fixme: This sure could be cleaner! if x2 > x1 and y2 > y1: xa = xb = x1 ya = yb = y1 wa = w ha = y2 - y1 wb = x2- x1 hb = h elif x2 > x1 and y2 <= y1: xa = x1 ya = y1 wa = x2 - x1 ha = h xb = x1 yb = y2 + h wb = w hb = y1 - y2 elif x2 <= x1 and y2 > y1: xa = x1 ya = y1 wa = w ha = y2 - y1 xb = x2 + w yb = y1 wb = x1 - x2 hb = h - y2 + y1 elif x2 <= x1 and y2 <= y1: xa = x2 + w ya = y1 wa = x1 - x2 ha = h xb = x1 yb = y2 + h wb = w hb = y1 - y2 dc.SetPen(wx.TRANSPARENT_PEN) dc.SetBrush(self.parent.BackgroundBrush) dc.DrawRectangle(xa, ya, wa, ha) dc.DrawRectangle(xb, yb, wb, hb) self.PrevMoveXY = xy_tl if self.parent._ForeDrawList: dc.DrawBitmapPoint(self.parent._ForegroundBuffer,xy_tl) else: dc.DrawBitmapPoint(self.parent._Buffer,xy_tl) dc.EndDrawing() def OnWheel(self, event): """ By default, zoom in/out by a 0.1 factor per Wheel event. """ if event.GetWheelRotation() < 0: self.parent.Zoom(0.9) else: self.parent.Zoom(1.1) class GUIZoomIn(GUIBase): Cursor = MagPlusCursor def __init__(self, parent): GUIBase.__init__(self, parent) self.StartRBBox = None self.PrevRBBox = None def OnLeftDown(self, event): self.StartRBBox = N.array( event.GetPosition() ) self.PrevRBBox = None self.parent.CaptureMouse() def OnLeftUp(self, event): #if self.parent.HasCapture(): # self.parent.ReleaseMouse() if event.LeftUp() and not self.StartRBBox is None: self.PrevRBBox = None EndRBBox = event.GetPosition() StartRBBox = self.StartRBBox # if mouse has moved less that ten pixels, don't use the box. if ( abs(StartRBBox[0] - EndRBBox[0]) > 10 and abs(StartRBBox[1] - EndRBBox[1]) > 10 ): EndRBBox = self.parent.PixelToWorld(EndRBBox) StartRBBox = self.parent.PixelToWorld(StartRBBox) BB = N.array(((min(EndRBBox[0],StartRBBox[0]), min(EndRBBox[1],StartRBBox[1])), (max(EndRBBox[0],StartRBBox[0]), max(EndRBBox[1],StartRBBox[1]))),N.float_) self.parent.ZoomToBB(BB) else: Center = self.parent.PixelToWorld(StartRBBox) self.parent.Zoom(1.5,Center) self.StartRBBox = None def OnMove(self, event): # Allways raise the Move event. self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION) if event.Dragging() and event.LeftIsDown() and not (self.StartRBBox is None): xy0 = self.StartRBBox xy1 = N.array( event.GetPosition() ) wh = abs(xy1 - xy0) wh[0] = max(wh[0], int(wh[1]*self.parent.AspectRatio)) wh[1] = int(wh[0] / self.parent.AspectRatio) xy_c = (xy0 + xy1) / 2 dc = wx.ClientDC(self.parent) dc.BeginDrawing() dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH)) dc.SetBrush(wx.TRANSPARENT_BRUSH) dc.SetLogicalFunction(wx.XOR) if self.PrevRBBox: dc.DrawRectanglePointSize(*self.PrevRBBox) self.PrevRBBox = ( xy_c - wh/2, wh ) dc.DrawRectanglePointSize( *self.PrevRBBox ) dc.EndDrawing() def UpdateScreen(self): """ Update gets called if the screen has been repainted in the middle of a zoom in so the Rubber Band Box can get updated """ if self.PrevRBBox is not None: dc = wx.ClientDC(self.parent) dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH)) dc.SetBrush(wx.TRANSPARENT_BRUSH) dc.SetLogicalFunction(wx.XOR) dc.DrawRectanglePointSize(*self.PrevRBBox) def OnRightDown(self, event): self.parent.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel") def OnWheel(self, event): if event.GetWheelRotation() < 0: self.parent.Zoom(0.9) else: self.parent.Zoom(1.1) class GUIZoomOut(GUIBase): Cursor = MagMinusCursor def OnLeftDown(self, event): self.parent.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel") def OnRightDown(self, event): self.parent.Zoom(1.5, event.GetPosition(), centerCoords="pixel") def OnWheel(self, event): if event.GetWheelRotation() < 0: self.parent.Zoom(0.9) else: self.parent.Zoom(1.1) def OnMove(self, event): # Allways raise the Move event. self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)