Poodletooth-iLand/panda/python/Lib/site-packages/wx/lib/floatcanvas/GUIMode.py
Master Jumblespeed d882959bfa switch to remote
2015-05-18 22:11:33 -04:00

402 lines
13 KiB
Python
Executable file

#!/usr/bin/env python
#----------------------------------------------------------------------------
# Name: GUIMode.py
# Purpose:
#
# Author:
#
# Created:
# Version:
# Date:
# Licence:
# Tags: phoenix-port
#----------------------------------------------------------------------------
"""
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
import numpy as N
from . import FCEvents, Resources
from .Utilities import BBox
class Cursors(object):
"""
Class to hold the standard Cursors
"""
def __init__(self):
if "wxMac" in wx.PlatformInfo: # use 16X16 cursors for wxMac
self.HandCursor = wx.Cursor(Resources.getHand16Image())
self.GrabHandCursor = wx.Cursor(Resources.getGrabHand16Image())
img = Resources.getMagPlus16Image()
img.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
img.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
self.MagPlusCursor = wx.Cursor(img)
img = Resources.getMagMinus16Image()
img.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
img.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
self.MagMinusCursor = wx.Cursor(img)
else: # use 24X24 cursors for GTK and Windows
self.HandCursor = wx.Cursor(Resources.getHandImage())
self.GrabHandCursor = wx.Cursor(Resources.getGrabHandImage())
img = Resources.getMagPlusImage()
img.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
img.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
self.MagPlusCursor = wx.Cursor(img)
img = Resources.getMagMinusImage()
img.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
img.SetOption(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
self.MagMinusCursor = wx.Cursor(img)
class GUIBase(object):
"""
Basic Mouse mode and baseclass for other GUImode.
This one does nothing with any event
"""
def __init__(self, Canvas=None):
"""
Default class constructor.
:param `Canvas`: the canvas the GUI mode is attached too
"""
self.Canvas = Canvas # set the FloatCanvas for the mode
# it gets set when the Mode is set on the Canvas.
self.Cursors = Cursors()
Cursor = wx.NullCursor
def UnSet(self):
"""
this method gets called by FloatCanvas when a new mode is being set
on the Canvas
"""
pass
# 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 OnKeyDown(self, event):
pass
def OnKeyUp(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. Other GUIModes may require something similar
"""
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 = FCEvents.EVT_FC_LEFT_DOWN
if not self.Canvas.HitTest(event, EventType):
self.Canvas._RaiseMouseEvent(event, EventType)
def OnLeftUp(self, event):
EventType = FCEvents.EVT_FC_LEFT_UP
if not self.Canvas.HitTest(event, EventType):
self.Canvas._RaiseMouseEvent(event, EventType)
def OnLeftDouble(self, event):
EventType = FCEvents.EVT_FC_LEFT_DCLICK
if not self.Canvas.HitTest(event, EventType):
self.Canvas._RaiseMouseEvent(event, EventType)
def OnMiddleDown(self, event):
EventType = FCEvents.EVT_FC_MIDDLE_DOWN
if not self.Canvas.HitTest(event, EventType):
self.Canvas._RaiseMouseEvent(event, EventType)
def OnMiddleUp(self, event):
EventType = FCEvents.EVT_FC_MIDDLE_UP
if not self.Canvas.HitTest(event, EventType):
self.Canvas._RaiseMouseEvent(event, EventType)
def OnMiddleDouble(self, event):
EventType = FCEvents.EVT_FC_MIDDLE_DCLICK
if not self.Canvas.HitTest(event, EventType):
self.Canvas._RaiseMouseEvent(event, EventType)
def OnRightDown(self, event):
EventType = FCEvents.EVT_FC_RIGHT_DOWN
if not self.Canvas.HitTest(event, EventType):
self.Canvas._RaiseMouseEvent(event, EventType)
def OnRightUp(self, event):
EventType = FCEvents.EVT_FC_RIGHT_UP
if not self.Canvas.HitTest(event, EventType):
self.Canvas._RaiseMouseEvent(event, EventType)
def OnRightDouble(self, event):
EventType = FCEvents.EVT_FC_RIGHT_DCLICK
if not self.Canvas.HitTest(event, EventType):
self.Canvas._RaiseMouseEvent(event, EventType)
def OnWheel(self, event):
EventType = FCEvents.EVT_FC_MOUSEWHEEL
self.Canvas._RaiseMouseEvent(event, EventType)
def OnMove(self, event):
## The Move event always gets raised, even if there is a hit-test
self.Canvas.MouseOverTest(event)
self.Canvas._RaiseMouseEvent(event,FCEvents.EVT_FC_MOTION)
class GUIMove(GUIBase):
"""
Mode that moves the image (pans).
It doesn't change any coordinates, it only changes what the viewport is
"""
def __init__(self, canvas=None):
GUIBase.__init__(self, canvas)
self.Cursor = self.Cursors.HandCursor
self.GrabCursor = self.Cursors.GrabHandCursor
self.StartMove = None
self.MidMove = None
self.PrevMoveXY = None
## timer to give a delay when moving so that buffers aren't re-built too many times.
self.MoveTimer = wx.PyTimer(self.OnMoveTimer)
def OnLeftDown(self, event):
self.Canvas.SetCursor(self.GrabCursor)
self.Canvas.CaptureMouse()
self.StartMove = N.array( event.GetPosition() )
self.MidMove = self.StartMove
self.PrevMoveXY = (0,0)
def OnLeftUp(self, event):
self.Canvas.SetCursor(self.Cursor)
if self.StartMove is not None:
self.EndMove = N.array(event.GetPosition())
DiffMove = self.MidMove-self.EndMove
self.Canvas.MoveImage(DiffMove, 'Pixel', ReDraw=True)
def OnMove(self, event):
# Always raise the Move event.
self.Canvas._RaiseMouseEvent(event, FCEvents.EVT_FC_MOTION)
if event.Dragging() and event.LeftIsDown() and not self.StartMove is None:
self.EndMove = N.array(event.GetPosition())
self.MoveImage(event)
DiffMove = self.MidMove-self.EndMove
self.Canvas.MoveImage(DiffMove, 'Pixel', ReDraw=False)# reset the canvas without re-drawing
self.MidMove = self.EndMove
self.MoveTimer.Start(30, oneShot=True)
def OnMoveTimer(self, event=None):
self.Canvas.Draw()
def UpdateScreen(self):
## The screen has been re-drawn, so StartMove needs to be reset.
self.StartMove = self.MidMove
def MoveImage(self, event ):
#xy1 = N.array( event.GetPosition() )
xy1 = self.EndMove
wh = self.Canvas.PanelSize
xy_tl = xy1 - self.StartMove
dc = wx.ClientDC(self.Canvas)
x1,y1 = self.PrevMoveXY
x2,y2 = xy_tl
w,h = self.Canvas.PanelSize
##fixme: This sure could be cleaner!
## This is all to fill in the background with the background color
## without flashing as the image moves.
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.Canvas.BackgroundBrush)
dc.DrawRectangle(xa, ya, wa, ha)
dc.DrawRectangle(xb, yb, wb, hb)
self.PrevMoveXY = xy_tl
if self.Canvas._ForeDrawList:
dc.DrawBitmap(self.Canvas._ForegroundBuffer,xy_tl)
else:
dc.DrawBitmap(self.Canvas._Buffer,xy_tl)
#self.Canvas.Update()
def OnWheel(self, event):
"""
By default, zoom in/out by a 0.1 factor per Wheel event.
"""
if event.GetWheelRotation() < 0:
self.Canvas.Zoom(0.9)
else:
self.Canvas.Zoom(1.1)
class GUIZoomIn(GUIBase):
"""
Mode to zoom in.
"""
def __init__(self, canvas=None):
GUIBase.__init__(self, canvas)
self.StartRBBox = None
self.PrevRBBox = None
self.Cursor = self.Cursors.MagPlusCursor
def OnLeftDown(self, event):
self.StartRBBox = N.array( event.GetPosition() )
self.PrevRBBox = None
self.Canvas.CaptureMouse()
def OnLeftUp(self, event):
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.Canvas.PixelToWorld(EndRBBox)
StartRBBox = self.Canvas.PixelToWorld(StartRBBox)
self.Canvas.ZoomToBB( BBox.fromPoints(N.r_[EndRBBox,StartRBBox]) )
else:
Center = self.Canvas.PixelToWorld(StartRBBox)
self.Canvas.Zoom(1.5,Center)
self.StartRBBox = None
def OnMove(self, event):
# Always raise the Move event.
self.Canvas._RaiseMouseEvent(event,FCEvents.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.Canvas.AspectRatio))
wh[1] = int(wh[0] / self.Canvas.AspectRatio)
xy_c = (xy0 + xy1) / 2
dc = wx.ClientDC(self.Canvas)
dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetLogicalFunction(wx.XOR)
if self.PrevRBBox:
dc.DrawRectangle(*self.PrevRBBox)
self.PrevRBBox = ( xy_c - wh/2, wh )
dc.DrawRectangle( *self.PrevRBBox )
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 False:
if self.PrevRBBox is not None:
dc = wx.ClientDC(self.Canvas)
dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.SetLogicalFunction(wx.XOR)
dc.DrawRectangle(*self.PrevRBBox)
def OnRightDown(self, event):
self.Canvas.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
def OnWheel(self, event):
if event.GetWheelRotation() < 0:
self.Canvas.Zoom(0.9)
else:
self.Canvas.Zoom(1.1)
class GUIZoomOut(GUIBase):
"""
Mode to zoom out.
"""
def __init__(self, Canvas=None):
GUIBase.__init__(self, Canvas)
self.Cursor = self.Cursors.MagMinusCursor
def OnLeftDown(self, event):
self.Canvas.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
def OnRightDown(self, event):
self.Canvas.Zoom(1.5, event.GetPosition(), centerCoords="pixel")
def OnWheel(self, event):
if event.GetWheelRotation() < 0:
self.Canvas.Zoom(0.9)
else:
self.Canvas.Zoom(1.1)
def OnMove(self, event):
# Always raise the Move event.
self.Canvas._RaiseMouseEvent(event,FCEvents.EVT_FC_MOTION)