Forward port recent changes on the 2.8 branch to HEAD
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@46083 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
File diff suppressed because it is too large
Load Diff
347
wxPython/wx/lib/floatcanvas/GUIMode.py
Normal file
347
wxPython/wx/lib/floatcanvas/GUIMode.py
Normal file
@@ -0,0 +1,347 @@
|
||||
"""
|
||||
|
||||
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)
|
||||
|
@@ -4,18 +4,9 @@ A Panel that includes the FloatCanvas and Navigation controls
|
||||
"""
|
||||
|
||||
import wx
|
||||
|
||||
import FloatCanvas, Resources
|
||||
|
||||
ID_ZOOM_IN_BUTTON = wx.NewId()
|
||||
ID_ZOOM_OUT_BUTTON = wx.NewId()
|
||||
ID_ZOOM_TO_FIT_BUTTON = wx.NewId()
|
||||
ID_MOVE_MODE_BUTTON = wx.NewId()
|
||||
ID_POINTER_BUTTON = wx.NewId()
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class NavCanvas(wx.Panel):
|
||||
"""
|
||||
NavCanvas.py
|
||||
@@ -23,96 +14,80 @@ class NavCanvas(wx.Panel):
|
||||
This is a high level window that encloses the FloatCanvas in a panel
|
||||
and adds a Navigation toolbar.
|
||||
|
||||
Copyright: Christopher Barker)
|
||||
"""
|
||||
|
||||
License: Same as the version of wxPython you are using it with
|
||||
|
||||
Please let me know if you're using this!!!
|
||||
|
||||
Contact me at:
|
||||
|
||||
Chris.Barker@noaa.gov
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, parent, id = -1,
|
||||
size = wx.DefaultSize,
|
||||
**kwargs): # The rest just get passed into FloatCanvas
|
||||
|
||||
wx.Panel.__init__( self, parent, id, wx.DefaultPosition, size)
|
||||
def __init__(self,
|
||||
parent,
|
||||
id = wx.ID_ANY,
|
||||
size = wx.DefaultSize,
|
||||
**kwargs): # The rest just get passed into FloatCanvas
|
||||
wx.Panel.__init__(self, parent, id, size=size)
|
||||
|
||||
self.BuildToolbar()
|
||||
## Create the vertical sizer for the toolbar and Panel
|
||||
box = wx.BoxSizer(wx.VERTICAL)
|
||||
box.Add(self.BuildToolbar(), 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)
|
||||
|
||||
self.Canvas = FloatCanvas.FloatCanvas( self, wx.NewId(),
|
||||
size = wx.DefaultSize,
|
||||
**kwargs)
|
||||
box.Add(self.Canvas,1,wx.GROW)
|
||||
box.Add(self.ToolBar, 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)
|
||||
|
||||
box.Fit(self)
|
||||
self.SetSizer(box)
|
||||
self.Canvas = FloatCanvas.FloatCanvas(self, **kwargs)
|
||||
box.Add(self.Canvas, 1, wx.GROW)
|
||||
|
||||
self.SetSizerAndFit(box)
|
||||
|
||||
|
||||
import GUIMode # here so that it doesn't get imported before wx.App()
|
||||
self.GUIZoomIn = GUIMode.GUIZoomIn(self.Canvas)
|
||||
self.GUIZoomOut = GUIMode.GUIZoomOut(self.Canvas)
|
||||
self.GUIMove = GUIMode.GUIMove(self.Canvas)
|
||||
self.GUIMouse = GUIMode.GUIMouse(self.Canvas)
|
||||
|
||||
# default to Mouse mode
|
||||
self.ToolBar.ToggleTool(ID_POINTER_BUTTON,1)
|
||||
self.Canvas.SetMode("Mouse")
|
||||
|
||||
self.ToolBar.ToggleTool(self.PointerTool.GetId(), True)
|
||||
self.Canvas.SetMode(self.GUIMouse)
|
||||
|
||||
return None
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""
|
||||
Delegate all extra methods to the Canvas
|
||||
"""
|
||||
attrib = getattr(self.Canvas, name)
|
||||
## add the attribute to this module's dict for future calls
|
||||
self.__dict__[name] = attrib
|
||||
return attrib
|
||||
|
||||
def BuildToolbar(self):
|
||||
tb = wx.ToolBar(self,-1)
|
||||
tb = wx.ToolBar(self)
|
||||
self.ToolBar = tb
|
||||
|
||||
tb.SetToolBitmapSize((24,24))
|
||||
|
||||
tb.AddTool(ID_POINTER_BUTTON, Resources.getPointerBitmap(), isToggle=True, shortHelpString = "Pointer")
|
||||
wx.EVT_TOOL(self, ID_POINTER_BUTTON, self.SetToolMode)
|
||||
|
||||
tb.AddTool(ID_ZOOM_IN_BUTTON, Resources.getMagPlusBitmap(), isToggle=True, shortHelpString = "Zoom In")
|
||||
wx.EVT_TOOL(self, ID_ZOOM_IN_BUTTON, self.SetToolMode)
|
||||
|
||||
tb.AddTool(ID_ZOOM_OUT_BUTTON, Resources.getMagMinusBitmap(), isToggle=True, shortHelpString = "Zoom Out")
|
||||
wx.EVT_TOOL(self, ID_ZOOM_OUT_BUTTON, self.SetToolMode)
|
||||
|
||||
tb.AddTool(ID_MOVE_MODE_BUTTON, Resources.getHandBitmap(), isToggle=True, shortHelpString = "Move")
|
||||
wx.EVT_TOOL(self, ID_MOVE_MODE_BUTTON, self.SetToolMode)
|
||||
|
||||
self.PointerTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getPointerBitmap(), shortHelp = "Pointer")
|
||||
self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIMouse), self.PointerTool)
|
||||
|
||||
self.ZoomInTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getMagPlusBitmap(), shortHelp = "Zoom In")
|
||||
self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIZoomIn), self.ZoomInTool)
|
||||
|
||||
self.ZoomOutTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getMagMinusBitmap(), shortHelp = "Zoom Out")
|
||||
self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIZoomOut), self.ZoomOutTool)
|
||||
|
||||
self.MoveTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getHandBitmap(), shortHelp = "Move")
|
||||
self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIMove), self.MoveTool)
|
||||
|
||||
tb.AddSeparator()
|
||||
|
||||
tb.AddControl(wx.Button(tb, ID_ZOOM_TO_FIT_BUTTON, "Zoom To Fit",wx.DefaultPosition, wx.DefaultSize))
|
||||
wx.EVT_BUTTON(self, ID_ZOOM_TO_FIT_BUTTON, self.ZoomToFit)
|
||||
|
||||
self.ZoomButton = wx.Button(tb, label="Zoom To Fit")
|
||||
tb.AddControl(self.ZoomButton)
|
||||
self.ZoomButton.Bind(wx.EVT_BUTTON, self.ZoomToFit)
|
||||
|
||||
tb.Realize()
|
||||
S = tb.GetSize()
|
||||
tb.SetSizeHints(S[0],S[1])
|
||||
## fixme: remove this when the bug is fixed!
|
||||
wx.CallAfter(self.HideShowHack) # this required on wxPython 2.8.3 on OS-X
|
||||
|
||||
return tb
|
||||
|
||||
def SetToolMode(self,event):
|
||||
for id in [ID_ZOOM_IN_BUTTON,
|
||||
ID_ZOOM_OUT_BUTTON,
|
||||
ID_MOVE_MODE_BUTTON,
|
||||
ID_POINTER_BUTTON]:
|
||||
self.ToolBar.ToggleTool(id,0)
|
||||
self.ToolBar.ToggleTool(event.GetId(),1)
|
||||
if event.GetId() == ID_ZOOM_IN_BUTTON:
|
||||
self.Canvas.SetMode("ZoomIn")
|
||||
elif event.GetId() == ID_ZOOM_OUT_BUTTON:
|
||||
self.Canvas.SetMode("ZoomOut")
|
||||
elif event.GetId() == ID_MOVE_MODE_BUTTON:
|
||||
self.Canvas.SetMode("Move")
|
||||
elif event.GetId() == ID_POINTER_BUTTON:
|
||||
self.Canvas.SetMode("Mouse")
|
||||
def HideShowHack(self):
|
||||
##fixme: remove this when the bug is fixed!
|
||||
"""
|
||||
Hack to hide and show button on toolbar to get around OS-X bug on
|
||||
wxPython2.8 on OS-X
|
||||
"""
|
||||
self.ZoomButton.Hide()
|
||||
self.ZoomButton.Show()
|
||||
|
||||
def SetMode(self, Mode):
|
||||
self.Canvas.SetMode(Mode)
|
||||
|
||||
def ZoomToFit(self,Event):
|
||||
self.Canvas.ZoomToBB()
|
||||
self.Canvas.SetFocus() # Otherwise the focus stays on the Button, and wheel events are lost.
|
||||
|
||||
|
170
wxPython/wx/lib/floatcanvas/Utilities/BBox.py
Normal file
170
wxPython/wx/lib/floatcanvas/Utilities/BBox.py
Normal file
@@ -0,0 +1,170 @@
|
||||
"""
|
||||
A Bounding Box object and assorted utilities , subclassed from a numpy array
|
||||
|
||||
"""
|
||||
|
||||
import numpy as N
|
||||
|
||||
class BBox(N.ndarray):
|
||||
"""
|
||||
A Bounding Box object:
|
||||
|
||||
Takes Data as an array. Data is any python sequence that can be turned into a
|
||||
2x2 numpy array of floats:
|
||||
|
||||
[[MinX, MinY ],
|
||||
[MaxX, MaxY ]]
|
||||
|
||||
It is a subclass of numpy.ndarray, so for the most part it can be used as
|
||||
an array, and arrays that fit the above description can be used in its place.
|
||||
|
||||
Usually created by the factory functions:
|
||||
|
||||
asBBox
|
||||
|
||||
and
|
||||
|
||||
fromPoints
|
||||
|
||||
"""
|
||||
def __new__(subtype, data):
|
||||
"""
|
||||
Takes Data as an array. Data is any python sequence that can be turned into a
|
||||
2x2 numpy array of floats:
|
||||
|
||||
[[MinX, MinY ],
|
||||
[MaxX, MaxY ]]
|
||||
|
||||
You don't usually call this directly. BBox objects are created with the factory functions:
|
||||
|
||||
asBBox
|
||||
|
||||
and
|
||||
|
||||
fromPoints
|
||||
|
||||
"""
|
||||
arr = N.array(data, N.float)
|
||||
arr.shape = (2,2)
|
||||
if arr[0,0] > arr[1,0] or arr[0,1] > arr[1,1]:
|
||||
# note: zero sized BB OK.
|
||||
raise ValueError("BBox values not aligned: \n minimum values must be less that maximum values")
|
||||
return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
|
||||
|
||||
def Overlaps(self, BB):
|
||||
"""
|
||||
Overlap(BB):
|
||||
|
||||
Tests if the given Bounding Box overlaps with this one.
|
||||
Returns True is the Bounding boxes overlap, False otherwise
|
||||
If they are just touching, returns True
|
||||
"""
|
||||
|
||||
if ( (self[1,0] >= BB[0,0]) and (self[0,0] <= BB[1,0]) and
|
||||
(self[1,1] >= BB[0,1]) and (self[0,1] <= BB[1,1]) ):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def Inside(self, BB):
|
||||
"""
|
||||
Inside(BB):
|
||||
|
||||
Tests if the given Bounding Box is entirely inside this one.
|
||||
|
||||
Returns True if it is entirely inside, or touching the
|
||||
border.
|
||||
|
||||
Returns False otherwise
|
||||
"""
|
||||
if ( (BB[0,0] >= self[0,0]) and (BB[1,0] <= self[1,0]) and
|
||||
(BB[0,1] >= self[0,1]) and (BB[1,1] <= self[1,1]) ):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def Merge(self, BB):
|
||||
"""
|
||||
Joins this bounding box with the one passed in, maybe making this one bigger
|
||||
|
||||
"""
|
||||
|
||||
if BB[0,0] < self[0,0]: self[0,0] = BB[0,0]
|
||||
if BB[0,1] < self[0,1]: self[0,1] = BB[0,1]
|
||||
if BB[1,0] > self[1,0]: self[1,0] = BB[1,0]
|
||||
if BB[1,1] > self[1,1]: self[1,1] = BB[1,1]
|
||||
|
||||
### This could be used for a make BB from a bunch of BBs
|
||||
|
||||
#~ def _getboundingbox(bboxarray): # lrk: added this
|
||||
#~ # returns the bounding box of a bunch of bounding boxes
|
||||
#~ upperleft = N.minimum.reduce(bboxarray[:,0])
|
||||
#~ lowerright = N.maximum.reduce(bboxarray[:,1])
|
||||
#~ return N.array((upperleft, lowerright), N.float)
|
||||
#~ _getboundingbox = staticmethod(_getboundingbox)
|
||||
|
||||
|
||||
## Save the ndarray __eq__ for internal use.
|
||||
Array__eq__ = N.ndarray.__eq__
|
||||
def __eq__(self, BB):
|
||||
"""
|
||||
__eq__(BB) The equality operator
|
||||
|
||||
A == B if and only if all the entries are the same
|
||||
|
||||
"""
|
||||
return N.all(self.Array__eq__(BB))
|
||||
|
||||
|
||||
def asBBox(data):
|
||||
"""
|
||||
returns a BBox object.
|
||||
|
||||
If object is a BBox, it is returned unaltered
|
||||
|
||||
If object is a numpy array, a BBox object is returned that shares a
|
||||
view of the data with that array
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(data, BBox):
|
||||
return data
|
||||
arr = N.asarray(data, N.float)
|
||||
return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
|
||||
|
||||
def fromPoints(Points):
|
||||
"""
|
||||
fromPoints (Points).
|
||||
|
||||
reruns the bounding box of the set of points in Points. Points can
|
||||
be any python object that can be turned into a numpy NX2 array of Floats.
|
||||
|
||||
If a single point is passed in, a zero-size Bounding Box is returned.
|
||||
|
||||
"""
|
||||
Points = N.asarray(Points, N.float).reshape(-1,2)
|
||||
|
||||
arr = N.vstack( (Points.min(0), Points.max(0)) )
|
||||
return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
|
||||
|
||||
def fromBBArray(BBarray):
|
||||
"""
|
||||
Builds a BBox object from an array of Bounding Boxes.
|
||||
The resulting Bounding Box encompases all the included BBs.
|
||||
|
||||
The BBarray is in the shape: (Nx2x2) where BBarray[n] is a 2x2 array that represents a BBox
|
||||
"""
|
||||
|
||||
#upperleft = N.minimum.reduce(BBarray[:,0])
|
||||
#lowerright = N.maximum.reduce(BBarray[:,1])
|
||||
|
||||
# BBarray = N.asarray(BBarray, N.float).reshape(-1,2)
|
||||
# arr = N.vstack( (BBarray.min(0), BBarray.max(0)) )
|
||||
BBarray = N.asarray(BBarray, N.float).reshape(-1,2,2)
|
||||
arr = N.vstack( (BBarray[:,0,:].min(0), BBarray[:,1,:].max(0)) )
|
||||
return asBBox(arr)
|
||||
#return asBBox( (upperleft, lowerright) ) * 2
|
||||
|
||||
|
||||
|
||||
|
354
wxPython/wx/lib/floatcanvas/Utilities/BBoxTest.py
Normal file
354
wxPython/wx/lib/floatcanvas/Utilities/BBoxTest.py
Normal file
@@ -0,0 +1,354 @@
|
||||
|
||||
"""
|
||||
Test code for the BBox Object
|
||||
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
from BBox import *
|
||||
|
||||
class testCreator(unittest.TestCase):
|
||||
def testCreates(self):
|
||||
B = BBox(((0,0),(5,5)))
|
||||
self.failUnless(isinstance(B, BBox))
|
||||
|
||||
def testType(self):
|
||||
B = N.array(((0,0),(5,5)))
|
||||
self.failIf(isinstance(B, BBox))
|
||||
|
||||
def testDataType(self):
|
||||
B = BBox(((0,0),(5,5)))
|
||||
self.failUnless(B.dtype == N.float)
|
||||
|
||||
def testShape(self):
|
||||
B = BBox((0,0,5,5))
|
||||
self.failUnless(B.shape == (2,2))
|
||||
|
||||
def testShape2(self):
|
||||
self.failUnlessRaises(ValueError, BBox, (0,0,5) )
|
||||
|
||||
def testShape3(self):
|
||||
self.failUnlessRaises(ValueError, BBox, (0,0,5,6,7) )
|
||||
|
||||
def testArrayConstruction(self):
|
||||
A = N.array(((4,5),(10,12)), N.float_)
|
||||
B = BBox(A)
|
||||
self.failUnless(isinstance(B, BBox))
|
||||
|
||||
def testMinMax(self):
|
||||
self.failUnlessRaises(ValueError, BBox, (0,0,-1,6) )
|
||||
|
||||
def testMinMax2(self):
|
||||
self.failUnlessRaises(ValueError, BBox, (0,0,1,-6) )
|
||||
|
||||
def testMinMax(self):
|
||||
# OK to have a zero-sized BB
|
||||
B = BBox(((0,0),(0,5)))
|
||||
self.failUnless(isinstance(B, BBox))
|
||||
|
||||
def testMinMax2(self):
|
||||
# OK to have a zero-sized BB
|
||||
B = BBox(((10.0,-34),(10.0,-34.0)))
|
||||
self.failUnless(isinstance(B, BBox))
|
||||
|
||||
def testMinMax3(self):
|
||||
# OK to have a tiny BB
|
||||
B = BBox(((0,0),(1e-20,5)))
|
||||
self.failUnless(isinstance(B, BBox))
|
||||
|
||||
def testMinMax4(self):
|
||||
# Should catch tiny difference
|
||||
self.failUnlessRaises(ValueError, BBox, ((0,0), (-1e-20,5)) )
|
||||
|
||||
class testAsBBox(unittest.TestCase):
|
||||
|
||||
def testPassThrough(self):
|
||||
B = BBox(((0,0),(5,5)))
|
||||
C = asBBox(B)
|
||||
self.failUnless(B is C)
|
||||
|
||||
def testPassThrough2(self):
|
||||
B = (((0,0),(5,5)))
|
||||
C = asBBox(B)
|
||||
self.failIf(B is C)
|
||||
|
||||
def testPassArray(self):
|
||||
# Different data type
|
||||
A = N.array( (((0,0),(5,5))) )
|
||||
C = asBBox(A)
|
||||
self.failIf(A is C)
|
||||
|
||||
def testPassArray2(self):
|
||||
# same data type -- should be a view
|
||||
A = N.array( (((0,0),(5,5))), N.float_ )
|
||||
C = asBBox(A)
|
||||
A[0,0] = -10
|
||||
self.failUnless(C[0,0] == A[0,0])
|
||||
|
||||
class testIntersect(unittest.TestCase):
|
||||
|
||||
def testSame(self):
|
||||
B = BBox(((-23.5, 456),(56, 532.0)))
|
||||
C = BBox(((-23.5, 456),(56, 532.0)))
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testUpperLeft(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (0, 12),(10, 32.0) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testUpperRight(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (12, 12),(25, 32.0) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testLowerRight(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (12, 5),(25, 15) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testLowerLeft(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (-10, 5),(8.5, 15) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testBelow(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (-10, 5),(8.5, 9.2) ) )
|
||||
self.failIf(B.Overlaps(C) )
|
||||
|
||||
def testAbove(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (-10, 25.001),(8.5, 32) ) )
|
||||
self.failIf(B.Overlaps(C) )
|
||||
|
||||
def testLeft(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (4, 8),(4.95, 32) ) )
|
||||
self.failIf(B.Overlaps(C) )
|
||||
|
||||
def testRight(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (17.1, 8),(17.95, 32) ) )
|
||||
self.failIf(B.Overlaps(C) )
|
||||
|
||||
def testInside(self):
|
||||
B = BBox( ( (-15, -25),(-5, -10) ) )
|
||||
C = BBox( ( (-12, -22), (-6, -8) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testOutside(self):
|
||||
B = BBox( ( (-15, -25),(-5, -10) ) )
|
||||
C = BBox( ( (-17, -26), (3, 0) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testTouch(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (15, 8),(17.95, 32) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testCorner(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (15, 25),(17.95, 32) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testZeroSize(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (15, 25),(15, 25) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testZeroSize2(self):
|
||||
B = BBox( ( (5, 10),(5, 10) ) )
|
||||
C = BBox( ( (15, 25),(15, 25) ) )
|
||||
self.failIf(B.Overlaps(C) )
|
||||
|
||||
def testZeroSize3(self):
|
||||
B = BBox( ( (5, 10),(5, 10) ) )
|
||||
C = BBox( ( (0, 8),(10, 12) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
def testZeroSize4(self):
|
||||
B = BBox( ( (5, 1),(10, 25) ) )
|
||||
C = BBox( ( (8, 8),(8, 8) ) )
|
||||
self.failUnless(B.Overlaps(C) )
|
||||
|
||||
|
||||
|
||||
class testEquality(unittest.TestCase):
|
||||
def testSame(self):
|
||||
B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
C = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
self.failUnless(B == C)
|
||||
|
||||
def testIdentical(self):
|
||||
B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
self.failUnless(B == B)
|
||||
|
||||
def testNotSame(self):
|
||||
B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
C = BBox( ( (1.0, 2.0), (5.0, 10.1) ) )
|
||||
self.failIf(B == C)
|
||||
|
||||
def testWithArray(self):
|
||||
B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
C = N.array( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
self.failUnless(B == C)
|
||||
|
||||
def testWithArray2(self):
|
||||
B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
C = N.array( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
self.failUnless(C == B)
|
||||
|
||||
def testWithArray2(self):
|
||||
B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
C = N.array( ( (1.01, 2.0), (5.0, 10.0) ) )
|
||||
self.failIf(C == B)
|
||||
|
||||
class testInside(unittest.TestCase):
|
||||
def testSame(self):
|
||||
B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
C = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
self.failUnless(B.Inside(C))
|
||||
|
||||
def testPoint(self):
|
||||
B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
C = BBox( ( (3.0, 4.0), (3.0, 4.0) ) )
|
||||
self.failUnless(B.Inside(C))
|
||||
|
||||
def testPointOutside(self):
|
||||
B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
C = BBox( ( (-3.0, 4.0), (0.10, 4.0) ) )
|
||||
self.failIf(B.Inside(C))
|
||||
|
||||
def testUpperLeft(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (0, 12),(10, 32.0) ) )
|
||||
self.failIf(B.Inside(C) )
|
||||
|
||||
def testUpperRight(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (12, 12),(25, 32.0) ) )
|
||||
self.failIf(B.Inside(C) )
|
||||
|
||||
def testLowerRight(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (12, 5),(25, 15) ) )
|
||||
self.failIf(B.Inside(C) )
|
||||
|
||||
def testLowerLeft(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (-10, 5),(8.5, 15) ) )
|
||||
self.failIf(B.Inside(C) )
|
||||
|
||||
def testBelow(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (-10, 5),(8.5, 9.2) ) )
|
||||
self.failIf(B.Inside(C) )
|
||||
|
||||
def testAbove(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (-10, 25.001),(8.5, 32) ) )
|
||||
self.failIf(B.Inside(C) )
|
||||
|
||||
def testLeft(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (4, 8),(4.95, 32) ) )
|
||||
self.failIf(B.Inside(C) )
|
||||
|
||||
def testRight(self):
|
||||
B = BBox( ( (5, 10),(15, 25) ) )
|
||||
C = BBox( ( (17.1, 8),(17.95, 32) ) )
|
||||
self.failIf(B.Inside(C) )
|
||||
|
||||
class testFromPoints(unittest.TestCase):
|
||||
|
||||
def testCreate(self):
|
||||
Pts = N.array( ((5,2),
|
||||
(3,4),
|
||||
(1,6),
|
||||
), N.float_ )
|
||||
B = fromPoints(Pts)
|
||||
#B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
|
||||
self.failUnless(B[0,0] == 1.0 and
|
||||
B[0,1] == 2.0 and
|
||||
B[1,0] == 5.0 and
|
||||
B[1,1] == 6.0
|
||||
)
|
||||
def testCreateInts(self):
|
||||
Pts = N.array( ((5,2),
|
||||
(3,4),
|
||||
(1,6),
|
||||
) )
|
||||
B = fromPoints(Pts)
|
||||
self.failUnless(B[0,0] == 1.0 and
|
||||
B[0,1] == 2.0 and
|
||||
B[1,0] == 5.0 and
|
||||
B[1,1] == 6.0
|
||||
)
|
||||
|
||||
def testSinglePoint(self):
|
||||
Pts = N.array( (5,2), N.float_ )
|
||||
B = fromPoints(Pts)
|
||||
self.failUnless(B[0,0] == 5.0 and
|
||||
B[0,1] == 2.0 and
|
||||
B[1,0] == 5.0 and
|
||||
B[1,1] == 2.0
|
||||
)
|
||||
|
||||
def testListTuples(self):
|
||||
Pts = [ (3, 6.5),
|
||||
(13, 43.2),
|
||||
(-4.32, -4),
|
||||
(65, -23),
|
||||
(-0.0001, 23.432),
|
||||
]
|
||||
B = fromPoints(Pts)
|
||||
self.failUnless(B[0,0] == -4.32 and
|
||||
B[0,1] == -23.0 and
|
||||
B[1,0] == 65.0 and
|
||||
B[1,1] == 43.2
|
||||
)
|
||||
class testMerge(unittest.TestCase):
|
||||
A = BBox( ((-23.5, 456), (56, 532.0)) )
|
||||
B = BBox( ((-20.3, 460), (54, 465 )) )# B should be completely inside A
|
||||
C = BBox( ((-23.5, 456), (58, 540.0)) )# up and to the right or A
|
||||
D = BBox( ((-26.5, 12), (56, 532.0)) )
|
||||
|
||||
def testInside(self):
|
||||
C = self.A.copy()
|
||||
C.Merge(self.B)
|
||||
self.failUnless(C == self.A)
|
||||
|
||||
def testFullOutside(self):
|
||||
C = self.B.copy()
|
||||
C.Merge(self.A)
|
||||
self.failUnless(C == self.A)
|
||||
|
||||
def testUpRight(self):
|
||||
A = self.A.copy()
|
||||
A.Merge(self.C)
|
||||
self.failUnless(A[0] == self.A[0] and A[1] == self.C[1])
|
||||
|
||||
def testDownLeft(self):
|
||||
A = self.A.copy()
|
||||
A.Merge(self.D)
|
||||
self.failUnless(A[0] == self.D[0] and A[1] == self.A[1])
|
||||
|
||||
class testBBarray(unittest.TestCase):
|
||||
BBarray = N.array( ( ((-23.5, 456), (56, 532.0)),
|
||||
((-20.3, 460), (54, 465 )),
|
||||
((-23.5, 456), (58, 540.0)),
|
||||
((-26.5, 12), (56, 532.0)),
|
||||
),
|
||||
dtype=N.float)
|
||||
print BBarray
|
||||
BB = asBBox( ((-26.5, 12.), ( 58. , 540.)) )
|
||||
|
||||
def testJoin(self):
|
||||
BB = fromBBArray(self.BBarray)
|
||||
self.failUnless(BB == self.BB, "Wrong BB was created. It was:\n%s \nit should have been:\n%s"%(BB, self.BB))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
115
wxPython/wx/lib/floatcanvas/Utilities/GUI.py
Normal file
115
wxPython/wx/lib/floatcanvas/Utilities/GUI.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
|
||||
Part of the floatcanvas.Utilities package.
|
||||
|
||||
This module contains assorted GUI-related utilities that can be used
|
||||
with FloatCanvas
|
||||
|
||||
So far, they are:
|
||||
|
||||
RubberBandBox: used to draw a RubberBand Box on the screen
|
||||
|
||||
"""
|
||||
import wx
|
||||
from floatcanvas import FloatCanvas
|
||||
|
||||
class RubberBandBox:
|
||||
"""
|
||||
Class to provide a rubber band box that can be drawn on a Window
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, Canvas, CallBack, Tol=5):
|
||||
|
||||
"""
|
||||
To initialize:
|
||||
|
||||
RubberBandBox(Canvas, CallBack)
|
||||
|
||||
Canvas: the FloatCanvas you want the Rubber band box to be used on
|
||||
|
||||
CallBack: is the method you want called when the mouse is
|
||||
released. That method will be called, passing in a rect
|
||||
parameter, where rect is: (Point, WH) of the rect in
|
||||
world coords.
|
||||
|
||||
Tol: The tolerance for the smallest rectangle allowed. defaults
|
||||
to 5. In pixels
|
||||
|
||||
Methods:
|
||||
|
||||
Enable() : Enables the Rubber Band Box (Binds the events)
|
||||
|
||||
Disable() : Enables the Rubber Band Box (Unbinds the events)
|
||||
|
||||
Attributes:
|
||||
|
||||
CallBack: The callback function, if it's replaced you need to
|
||||
call Enable() again.
|
||||
|
||||
"""
|
||||
|
||||
self.Canvas = Canvas
|
||||
self.CallBack = CallBack
|
||||
self.Tol = Tol
|
||||
|
||||
self.Drawing = False
|
||||
self.RBRect = None
|
||||
self.StartPointWorld = None
|
||||
|
||||
return None
|
||||
|
||||
def Enable(self):
|
||||
"""
|
||||
Called when you want the rubber band box to be enabled
|
||||
|
||||
"""
|
||||
|
||||
# bind events:
|
||||
self.Canvas.Bind(FloatCanvas.EVT_MOTION, self.OnMove )
|
||||
self.Canvas.Bind(FloatCanvas.EVT_LEFT_DOWN, self.OnLeftDown)
|
||||
self.Canvas.Bind(FloatCanvas.EVT_LEFT_UP, self.OnLeftUp )
|
||||
|
||||
def Disable(self):
|
||||
"""
|
||||
Called when you don't want the rubber band box to be enabled
|
||||
|
||||
"""
|
||||
|
||||
# unbind events:
|
||||
self.Canvas.Unbind(FloatCanvas.EVT_MOTION)
|
||||
self.Canvas.Unbind(FloatCanvas.EVT_LEFT_DOWN)
|
||||
self.Canvas.Unbind(FloatCanvas.EVT_LEFT_UP)
|
||||
|
||||
def OnMove(self, event):
|
||||
if self.Drawing:
|
||||
x, y = self.StartPoint
|
||||
Cornerx, Cornery = event.GetPosition()
|
||||
w, h = ( Cornerx - x, Cornery - y)
|
||||
if abs(w) > self.Tol and abs(h) > self.Tol:
|
||||
# draw the RB box
|
||||
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.RBRect:
|
||||
dc.DrawRectangle(*self.RBRect)
|
||||
self.RBRect = (x, y, w, h )
|
||||
dc.DrawRectangle(*self.RBRect)
|
||||
event.Skip() # skip so that other events can catch these
|
||||
|
||||
def OnLeftDown(self, event):
|
||||
# Start drawing
|
||||
self.Drawing = True
|
||||
self.StartPoint = event.GetPosition()
|
||||
self.StartPointWorld = event.Coords
|
||||
|
||||
def OnLeftUp(self, event):
|
||||
# Stop Drawing
|
||||
if self.Drawing:
|
||||
self.Drawing = False
|
||||
if self.RBRect:
|
||||
WH = event.Coords - self.StartPointWorld
|
||||
wx.CallAfter(self.CallBack, (self.StartPointWorld, WH))
|
||||
self.RBRect = None
|
||||
self.StartPointWorld = None
|
7
wxPython/wx/lib/floatcanvas/Utilities/__init__.py
Normal file
7
wxPython/wx/lib/floatcanvas/Utilities/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
__init__ for the floatcanvas Utilities package
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
@@ -29,8 +29,7 @@ It is double buffered, so re-draws after the window is uncovered by
|
||||
something else are very quick.
|
||||
|
||||
It relies on NumPy, which is needed for speed (maybe, I haven't profiled
|
||||
it). It will also use numarray, if you don't have Numeric, but it is
|
||||
slower.
|
||||
properly) and convenience.
|
||||
|
||||
Bugs and Limitations: Lots: patches, fixes welcome
|
||||
|
||||
@@ -60,12 +59,6 @@ If you are zoomed in, it checks the Bounding box of an object before
|
||||
drawing it. This makes it a great deal faster when there are a lot of
|
||||
objects and you are zoomed in so that only a few are shown.
|
||||
|
||||
One solution is to be able to pass some sort of object set to the DC
|
||||
directly. I've used DC.DrawPointList(Points), and it helped a lot with
|
||||
drawing lots of points. However, when zoomed in, the Bounding boxes need
|
||||
to be checked, so I may some day write C++ code that does the loop and
|
||||
checks the BBs.
|
||||
|
||||
Mouse Events:
|
||||
|
||||
There are a full set of custom mouse events. They are just like the
|
||||
@@ -80,19 +73,25 @@ clicked, mouse-over'd, etc.
|
||||
See the Demo for what it can do, and how to use it.
|
||||
|
||||
Copyright: Christopher Barker
|
||||
|
||||
License: Same as the version of wxPython you are using it with.
|
||||
|
||||
TRAC site for some docs and updates:
|
||||
http://morticia.cs.dal.ca/FloatCanvas/
|
||||
|
||||
SVN for latest code:
|
||||
svn://morticia.cs.dal.ca/FloatCanvas
|
||||
|
||||
Mailing List:
|
||||
http://mail.mithis.com/cgi-bin/mailman/listinfo/floatcanvas
|
||||
|
||||
Check for updates or answers to questions, send me an email.
|
||||
|
||||
Please let me know if you're using this!!!
|
||||
|
||||
Contact me at:
|
||||
|
||||
Chris.Barker@noaa.gov
|
||||
|
||||
"""
|
||||
|
||||
__version__ = "0.9.10"
|
||||
__version__ = "0.9.18"
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user