-import wx
-
-# Stuff to integrate FloatCanvas into wxPython Demo
try:
import Numeric
+ import RandomArray
haveNumeric = True
except ImportError:
- haveNumeric = False
-
+ try:
+ import numarray as Numeric
+ import numarray.random_array as RandomArray
+ haveNumeric = True
+ except ImportError:
+ haveNumeric = False
if not haveNumeric:
- errorText = """\
-The FloatCanvas requires the Numeric module:
-You can get it at:
+ errorText = """
+The FloatCanvas requires either the Numeric or Numarray module:
+You can get them at:
http://sourceforge.net/projects/numpy
-"""
- def runTest(frame, nb, log):
- dlg = wx.MessageDialog(
- frame, errorText, 'Sorry', wx.OK | wx.ICON_INFORMATION
- )
+NOTE: The Numeric module is substantially faster than numarray for this
+purpose, if you have lot's of objects
+"""
+
+ def runTest(frame, nb, log):
+ dlg = wx.MessageDialog(frame, errorText, 'Sorry', wx.OK |
+ wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
overview = ""
-
+
else:
- from wx.lib import floatcanvas
- import wx.lib.colourdb
+ StartUpDemo = "all"
+ if __name__ == "__main__": # parse options if run stand-alone
+ # check options:
+ import sys, getopt
+ optlist, args = getopt.getopt(sys.argv[1:],'l',["local","all","text","map","stext","hit","hitf","animate","speed","temp"])
+
+ for opt in optlist:
+ if opt[0] == "--all":
+ StartUpDemo = "all"
+ elif opt[0] == "--text":
+ StartUpDemo = "text"
+ elif opt[0] == "--map":
+ StartUpDemo = "map"
+ elif opt[0] == "--stext":
+ StartUpDemo = "stext"
+ elif opt[0] == "--hit":
+ StartUpDemo = "hit"
+ elif opt[0] == "--hitf":
+ StartUpDemo = "hitf"
+ elif opt[0] == "--animate":
+ StartUpDemo = "animate"
+ elif opt[0] == "--speed":
+ StartUpDemo = "speed"
+ elif opt[0] == "--temp":
+ StartUpDemo = "temp"
+ import wx
+ import time, random
- ID_ABOUT_MENU = wx.NewId()
- ID_EXIT_MENU = wx.NewId()
- ID_ZOOM_TO_FIT_MENU = wx.NewId()
- ID_DRAWTEST_MENU = wx.NewId()
- ID_LINETEST_MENU = wx.NewId()
- ID_DRAWMAP_MENU = wx.NewId()
- ID_DRAWMAP2_MENU = wx.NewId()
- ID_CLEAR_MENU = wx.NewId()
-
-
- colors = []
- LineStyles = floatcanvas.draw_object.LineStyleList.keys()
+ def runTest(frame, nb, log):
+ """
+ This method is used by the wxPython Demo Framework for integrating
+ this demo with the rest.
+ """
+ win = DrawFrame(None, -1, "FloatCanvas Drawing Window",wx.DefaultPosition,(500,500))
+ frame.otherWin = win
+ win.Show(True)
+ win.DrawTest()
+ try:
+ from floatcanvas import NavCanvas, FloatCanvas
+ except ImportError: # if it's not there locally, try the wxPython lib.
+ from wx.lib.floatcanvas import NavCanvas, FloatCanvas
+ import wxPython.lib.colourdb
class DrawFrame(wx.Frame):
"""
-
A frame used for the FloatCanvas Demo
"""
- def __init__(self, parent, id, title, position, size):
+
+ def __init__(self,parent, id,title,position,size):
wx.Frame.__init__(self,parent, id,title,position, size)
- # Set up the MenuBar
-
+ ## Set up the MenuBar
MenuBar = wx.MenuBar()
file_menu = wx.Menu()
- file_menu.Append(ID_EXIT_MENU, "&Close","Close this frame")
- self.Bind(wx.EVT_MENU, self.OnQuit, id=ID_EXIT_MENU)
+ item = file_menu.Append(-1, "&Close","Close this frame")
+ self.Bind(wx.EVT_MENU, self.OnQuit, item)
MenuBar.Append(file_menu, "&File")
draw_menu = wx.Menu()
- draw_menu.Append(ID_DRAWTEST_MENU, "&Draw Test","Run a test of drawing random components")
- self.Bind(wx.EVT_MENU, self.DrawTest, id=ID_DRAWTEST_MENU)
- draw_menu.Append(ID_LINETEST_MENU, "&Line Test","Run a test of drawing random lines")
- self.Bind(wx.EVT_MENU, self.LineTest, id=ID_LINETEST_MENU)
- draw_menu.Append(ID_DRAWMAP_MENU, "Draw &Map","Run a test of drawing a map")
- self.Bind(wx.EVT_MENU, self.DrawMap, id=ID_DRAWMAP_MENU)
- draw_menu.Append(ID_CLEAR_MENU, "&Clear","Clear the Canvas")
- self.Bind(wx.EVT_MENU, self.Clear, id=ID_CLEAR_MENU)
- MenuBar.Append(draw_menu, "&Draw")
+ item = draw_menu.Append(-1, "&Draw Test","Run a test of drawing random components")
+ self.Bind(wx.EVT_MENU, self.DrawTest, item)
+
+ item = draw_menu.Append(-1, "&Line Test","Run a test of drawing random lines")
+ self.Bind(wx.EVT_MENU, self.LineTest, item)
+
+ item = draw_menu.Append(-1, "Draw &Map","Run a test of drawing a map")
+ self.Bind(wx.EVT_MENU, self.DrawMap, item)
+ item = draw_menu.Append(-1, "&Text Test","Run a test of text drawing")
+ self.Bind(wx.EVT_MENU, self.TestText, item)
+ item = draw_menu.Append(-1, "&ScaledText Test","Run a test of text drawing")
+ self.Bind(wx.EVT_MENU, self.TestScaledText, item)
+ item = draw_menu.Append(-1, "&Clear","Clear the Canvas")
+ self.Bind(wx.EVT_MENU, self.Clear, item)
+ item = draw_menu.Append(-1, "&Hit Test","Run a test of the hit test code")
+ self.Bind(wx.EVT_MENU, self.TestHitTest, item)
+ item = draw_menu.Append(-1, "&Hit Test Foreground","Run a test of the hit test code with a foreground Object")
+ self.Bind(wx.EVT_MENU, self.TestHitTestForeground, item)
+ item = draw_menu.Append(-1, "&Animation","Run a test of Animation")
+ self.Bind(wx.EVT_MENU, self.TestAnimation, item)
+ item = draw_menu.Append(-1, "&Speed","Run a test of Drawing Speed")
+ self.Bind(wx.EVT_MENU, self.SpeedTest, item)
+ MenuBar.Append(draw_menu, "&Tests")
+
view_menu = wx.Menu()
- view_menu.Append(ID_ZOOM_TO_FIT_MENU, "Zoom to &Fit","Zoom to fit the window")
- self.Bind(wx.EVT_MENU, self.ZoomToFit, id=ID_ZOOM_TO_FIT_MENU)
+ item = view_menu.Append(-1, "Zoom to &Fit","Zoom to fit the window")
+ self.Bind(wx.EVT_MENU, self.ZoomToFit, item)
MenuBar.Append(view_menu, "&View")
help_menu = wx.Menu()
- help_menu.Append(ID_ABOUT_MENU, "&About",
+ item = help_menu.Append(-1, "&About",
"More information About this program")
- self.Bind(wx.EVT_MENU, self.OnAbout, id=ID_ABOUT_MENU)
+ self.Bind(wx.EVT_MENU, self.OnAbout, item)
MenuBar.Append(help_menu, "&Help")
self.SetMenuBar(MenuBar)
- self.CreateStatusBar()
- self.SetStatusText("")
-
- self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
-
- # Other event handlers:
- self.Bind(wx.EVT_RIGHT_DOWN, self.RightButtonEvent)
-
+ self.CreateStatusBar()
# Add the Canvas
- self.Canvas = floatcanvas.FloatCanvas(self,-1,(500,500),
- ProjectionFun = 'FlatEarth',
- Debug = 0,
- EnclosingFrame = self,
- BackgroundColor = "DARK SLATE BLUE",
- UseBackground = 0,
- UseToolbar = 1)
- self.Show(True)
-
- self.object_list = []
-
+ self.Canvas = NavCanvas.NavCanvas(self,
+ -1,
+ (500,500),
+ Debug = 1,
+ BackgroundColor = "DARK SLATE BLUE")
+
+ wx.EVT_CLOSE(self, self.OnCloseWindow)
+
+ FloatCanvas.EVT_MOTION(self.Canvas, self.OnMove )
+ #FloatCanvas.EVT_LEFT_UP(self.Canvas, self.OnLeftUp )
+
+ self.EventsAreBound = False
+
+ ## getting all the colors and linestyles for random objects
+ wxPython.lib.colourdb.updateColourDB()
+ self.colors = wxPython.lib.colourdb.getColourList()
+ #self.LineStyles = FloatCanvas.DrawObject.LineStyleList.keys()
+
+
return None
-
- def RightButtonEvent(self,event):
+
+ def BindAllMouseEvents(self):
+ if not self.EventsAreBound:
+ ## Here is how you catch FloatCanvas mouse events
+ FloatCanvas.EVT_LEFT_DOWN(self.Canvas, self.OnLeftDown )
+ FloatCanvas.EVT_LEFT_UP(self.Canvas, self.OnLeftUp )
+ FloatCanvas.EVT_LEFT_DCLICK(self.Canvas, self.OnLeftDouble )
+
+ FloatCanvas.EVT_MIDDLE_DOWN(self.Canvas, self.OnMiddleDown )
+ FloatCanvas.EVT_MIDDLE_UP(self.Canvas, self.OnMiddleUp )
+ FloatCanvas.EVT_MIDDLE_DCLICK(self.Canvas, self.OnMiddleDouble )
+
+ FloatCanvas.EVT_RIGHT_DOWN(self.Canvas, self.OnRightDown )
+ FloatCanvas.EVT_RIGHT_UP(self.Canvas, self.OnRightUp )
+ FloatCanvas.EVT_RIGHT_DCLICK(self.Canvas, self.OnRightDouble )
+
+ FloatCanvas.EVT_MOUSEWHEEL(self.Canvas, self.OnWheel )
+ self.EventsAreBound = True
+
+ def UnBindAllMouseEvents(self):
+ ## Here is how you catch FloatCanvas mouse events
+ FloatCanvas.EVT_LEFT_DOWN(self.Canvas, None )
+ FloatCanvas.EVT_LEFT_UP(self.Canvas, None )
+ FloatCanvas.EVT_LEFT_DCLICK(self.Canvas, None)
+
+ FloatCanvas.EVT_MIDDLE_DOWN(self.Canvas, None )
+ FloatCanvas.EVT_MIDDLE_UP(self.Canvas, None )
+ FloatCanvas.EVT_MIDDLE_DCLICK(self.Canvas, None )
+
+ FloatCanvas.EVT_RIGHT_DOWN(self.Canvas, None )
+ FloatCanvas.EVT_RIGHT_UP(self.Canvas, None )
+ FloatCanvas.EVT_RIGHT_DCLICK(self.Canvas, None )
+
+ FloatCanvas.EVT_MOUSEWHEEL(self.Canvas, None )
+
+ self.EventsAreBound = False
+
+ def PrintCoords(self,event):
+ print "coords are: %s"%(event.Coords,)
+ print "pixel coords are: %s\n"%(event.GetPosition(),)
+
+ def OnLeftDown(self, event):
+ print "Left Button has been clicked in DrawFrame"
+ self.PrintCoords(event)
+
+ def OnLeftUp(self, event):
+ print "Left up in DrawFrame"
+ self.PrintCoords(event)
+
+ def OnLeftDouble(self, event):
+ print "Left Double Click in DrawFrame"
+ self.PrintCoords(event)
+
+ def OnMiddleDown(self, event):
+ print "Middle Button clicked in DrawFrame"
+ self.PrintCoords(event)
+
+ def OnMiddleUp(self, event):
+ print "Middle Button Up in DrawFrame"
+ self.PrintCoords(event)
+
+ def OnMiddleDouble(self, event):
+ print "Middle Button Double clicked in DrawFrame"
+ self.PrintCoords(event)
+
+ def OnRightDown(self, event):
print "Right Button has been clicked in DrawFrame"
- print "coords are: %i, %i"%(event.GetX(),event.GetY())
- event.Skip()
-
+ self.PrintCoords(event)
+
+ def OnRightUp(self, event):
+ print "Right Button Up in DrawFrame"
+ self.PrintCoords(event)
+
+ def OnRightDouble(self, event):
+ print "Right Button Double clicked in DrawFrame"
+ self.PrintCoords(event)
+
+ def OnWheel(self, event):
+ print "Mouse Wheel Moved in DrawFrame"
+ self.PrintCoords(event)
+
+ def OnMove(self, event):
+ """
+ Updates the staus bar with the world coordinates
+ """
+ self.SetStatusText("%.2f, %.2f"%tuple(event.Coords))
+
def OnAbout(self, event):
+ print "OnAbout called"
+
dlg = wx.MessageDialog(self, "This is a small program to demonstrate\n"
"the use of the FloatCanvas\n",
"About Me", wx.OK | wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
- def SetMode(self,event):
- for id in [ID_ZOOM_IN_BUTTON,ID_ZOOM_OUT_BUTTON,ID_MOVE_MODE_BUTTON]:
- self.ToolBar.ToggleTool(id,0)
-
- self.ToolBar.ToggleTool(event.GetId(),1)
-
- if event.GetId() == ID_ZOOM_IN_BUTTON:
- self.Canvas.SetGUIMode("ZoomIn")
-
- elif event.GetId() == ID_ZOOM_OUT_BUTTON:
- self.Canvas.SetGUIMode("ZoomOut")
-
- elif event.GetId() == ID_MOVE_MODE_BUTTON:
- self.Canvas.SetGUIMode("Move")
-
def ZoomToFit(self,event):
self.Canvas.ZoomToBB()
def Clear(self,event = None):
- self.Canvas.RemoveObjects(self.object_list)
- self.object_list = []
+ self.UnBindAllMouseEvents()
+ self.Canvas.ClearAll()
+ self.Canvas.SetProjectionFun(None)
self.Canvas.Draw()
def OnQuit(self,event):
def OnCloseWindow(self, event):
self.Destroy()
- def DrawTest(self,event):
- wx.GetApp().Yield()
-
- import random
- import RandomArray
-
+ def DrawTest(self,event=None):
+ wx.GetApp().Yield(True)
+# import random
+# import RandomArray
Range = (-10,10)
-
+ colors = self.colors
+
+ self.BindAllMouseEvents()
Canvas = self.Canvas
- object_list = self.object_list
-
- # Random tests of everything:
+
+ Canvas.ClearAll()
+ Canvas.SetProjectionFun(None)
+
+ ## Random tests of everything:
# Rectangles
- for i in range(5):
+ for i in range(3):
x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
lw = random.randint(1,5)
cf = random.randint(0,len(colors)-1)
h = random.randint(1,5)
w = random.randint(1,5)
- object_list.append(Canvas.AddRectangle(x,y,h,w,LineWidth = lw,FillColor = colors[cf]))
+ Canvas.AddRectangle(x,y,h,w,LineWidth = lw,FillColor = colors[cf])
- # Ellipses
- for i in range(5):
+ # Ellipses
+ for i in range(3):
x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
lw = random.randint(1,5)
cf = random.randint(0,len(colors)-1)
h = random.randint(1,5)
w = random.randint(1,5)
- object_list.append(Canvas.AddEllipse(x,y,h,w,LineWidth = lw,FillColor = colors[cf]))
+ Canvas.AddEllipse(x,y,h,w,LineWidth = lw,FillColor = colors[cf])
- # Dots
- for i in range(5):
- x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
- D = random.randint(1,50)
- lw = random.randint(1,5)
- cf = random.randint(0,len(colors)-1)
- cl = random.randint(0,len(colors)-1)
- object_list.append(Canvas.AddDot(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf]))
+## # Dots -- Does anyone need this?
+## for i in range(5):
+## x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+## D = random.randint(1,50)
+## lw = random.randint(1,5)
+## cf = random.randint(0,len(colors)-1)
+## cl = random.randint(0,len(colors)-1)
+## Canvas.AddDot(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf])
# Circles
for i in range(5):
lw = random.randint(1,5)
cf = random.randint(0,len(colors)-1)
cl = random.randint(0,len(colors)-1)
- object_list.append(Canvas.AddCircle(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf]))
- self.object_list.append(self.Canvas.AddText("Circle # %i"%(i),x,y,Size = 12,BackGround = None,Position = "cc"))
+ Canvas.AddCircle(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf])
+ Canvas.AddText("Circle # %i"%(i),x,y,Size = 12,BackgroundColor = None,Position = "cc")
# Lines
for i in range(5):
lw = random.randint(1,10)
cf = random.randint(0,len(colors)-1)
cl = random.randint(0,len(colors)-1)
- self.object_list.append(self.Canvas.AddLine(points, LineWidth = lw, LineColor = colors[cl]))
+ Canvas.AddLine(points, LineWidth = lw, LineColor = colors[cl])
# Polygons
for i in range(3):
lw = random.randint(1,6)
cf = random.randint(0,len(colors)-1)
cl = random.randint(0,len(colors)-1)
- self.object_list.append(self.Canvas.AddPolygon(points,
- LineWidth = lw,
- LineColor = colors[cl],
- FillColor = colors[cf],
- FillStyle = 'Solid'))
-
-
+ Canvas.AddPolygon(points,
+ LineWidth = lw,
+ LineColor = colors[cl],
+ FillColor = colors[cf],
+ FillStyle = 'Solid')
+
## Pointset
for i in range(4):
points = []
points = RandomArray.uniform(Range[0],Range[1],(100,2))
cf = random.randint(0,len(colors)-1)
D = random.randint(1,4)
- self.object_list.append(self.Canvas.AddPointSet(points, Color = colors[cf], Diameter = D))
+ Canvas.AddPointSet(points, Color = colors[cf], Diameter = D)
# Text
- String = "Some text"
- for i in range(10):
+ String = "Unscaled text"
+ for i in range(3):
ts = random.randint(10,40)
cf = random.randint(0,len(colors)-1)
x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
- self.object_list.append(self.Canvas.AddText(String,x,y,Size = ts,ForeGround = colors[cf],Position = "cc"))
+ Canvas.AddText(String, x, y, Size = ts, Color = colors[cf], Position = "cc")
+
+ # Scaled Text
+ String = "Scaled text"
+ for i in range(3):
+ ts = random.random()*3 + 0.2
+ cf = random.randint(0,len(colors)-1)
+ x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+ Canvas.AddScaledText(String, x, y, Size = ts, Color = colors[cf], Position = "cc")
+
+ Canvas.ZoomToBB()
+
+ def TestAnimation(self,event=None):
+ """
+
+ In this test, a relatively complex background is drawn, and
+ a simple object placed in the foreground is moved over
+ it. This demonstrates how to use the InForeground attribute
+ to make an object in the foregorund draw fast, without
+ having to re-draw the whole background.
+
+ """
+ print "Running TestAnimation"
+ wx.GetApp().Yield(True)
+ Range = (-10,10)
+ self.Range = Range
+
+ self.UnBindAllMouseEvents()
+ Canvas = self.Canvas
+
+ Canvas.ClearAll()
+ Canvas.SetProjectionFun(None)
+
+ ## Random tests of everything:
+ colors = self.colors
+ # Rectangles
+ for i in range(3):
+ x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+ lw = random.randint(1,5)
+ cf = random.randint(0,len(colors)-1)
+ h = random.randint(1,5)
+ w = random.randint(1,5)
+ Canvas.AddRectangle(x,y,h,w,LineWidth = lw,FillColor = colors[cf])
+
+ # Ellipses
+ for i in range(3):
+ x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+ lw = random.randint(1,5)
+ cf = random.randint(0,len(colors)-1)
+ h = random.randint(1,5)
+ w = random.randint(1,5)
+ Canvas.AddEllipse(x,y,h,w,LineWidth = lw,FillColor = colors[cf])
+
+ # Circles
+ for i in range(5):
+ x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+ D = random.randint(1,5)
+ lw = random.randint(1,5)
+ cf = random.randint(0,len(colors)-1)
+ cl = random.randint(0,len(colors)-1)
+ Canvas.AddCircle(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf])
+ Canvas.AddText("Circle # %i"%(i),x,y,Size = 12,BackgroundColor = None,Position = "cc")
+
+ # Lines
+ for i in range(5):
+ points = []
+ for j in range(random.randint(2,10)):
+ point = (random.randint(Range[0],Range[1]),random.randint(Range[0],Range[1]))
+ points.append(point)
+ lw = random.randint(1,10)
+ cf = random.randint(0,len(colors)-1)
+ cl = random.randint(0,len(colors)-1)
+ Canvas.AddLine(points, LineWidth = lw, LineColor = colors[cl])
+
+ # Polygons
+ for i in range(3):
+ points = []
+ for j in range(random.randint(2,6)):
+ point = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+ points.append(point)
+ lw = random.randint(1,6)
+ cf = random.randint(0,len(colors)-1)
+ cl = random.randint(0,len(colors)-1)
+ Canvas.AddPolygon(points,
+ LineWidth = lw,
+ LineColor = colors[cl],
+ FillColor = colors[cf],
+ FillStyle = 'Solid')
+
+ # Scaled Text
+ String = "Scaled text"
+ for i in range(3):
+ ts = random.random()*3 + 0.2
+ cf = random.randint(0,len(colors)-1)
+ x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+ Canvas.AddScaledText(String, x, y, Size = ts, Color = colors[cf], Position = "cc")
+
+
+ # Now the Foreground Object:
+ C = Canvas.AddCircle(0,0,7,LineWidth = 2,LineColor = "Black",FillColor = "Red", InForeground = True)
+ T = Canvas.AddScaledText("Click to Move",0,0, Size = 0.8, Position = 'cc', InForeground = True)
+ C.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.MoveMe)
+ C.Text = T
+
+ self.Timer = wx.PyTimer(self.ShowFrame)
+ self.FrameDelay = 50 # milliseconds
+
+ Canvas.ZoomToBB()
+
+ def ShowFrame(self):
+ Object = self.MovingObject
+ Range = self.Range
+ if self.TimeStep < self.NumTimeSteps:
+ x,y = Object.XY
+ if x > Range[1] or x < Range[0]:
+ self.dx = -self.dx
+ if y > Range[1] or y < Range[0]:
+ self.dy = -self.dy
+ Object.Move( (self.dx,self.dy) )
+ Object.Text.Move( (self.dx,self.dy))
+ self.Canvas.Draw()
+ self.TimeStep += 1
+ wx.GetApp().Yield(True)
+ else:
+ self.Timer.Stop()
+
+
+ def MoveMe(self, Object):
+ self.MovingObject = Object
+ Range = self.Range
+ self.dx = random.uniform(Range[0]/4,Range[1]/4)
+ self.dy = random.uniform(Range[0]/4,Range[1]/4)
+ #import time
+ #start = time.time()
+ self.NumTimeSteps = 500
+ self.TimeStep = 1
+ self.Timer.Start(self.FrameDelay)
+ #print "Did %i frames in %f seconds"%(N, (time.time() - start) )
+ def TestHitTest(self,event=None):
+ wx.GetApp().Yield(True)
+
+ self.UnBindAllMouseEvents()
+ Canvas = self.Canvas
+
+ Canvas.ClearAll()
+ Canvas.SetProjectionFun(None)
+
+ #Add a HitAble rectangle
+ w, h = 60, 20
+
+ dx = 80
+ dy = 40
+ x,y = 20, 20
+
+ #Add one that is not HitAble
+ Canvas.AddRectangle(x, y, w, h, LineWidth = 2)
+ Canvas.AddText("Not Hit-able", x, y, Position = "bl")
+
+
+ x += dx
+ R = Canvas.AddRectangle(x, y, w, h,LineWidth = 2)
+ R.Name = "Line Rectangle"
+ R.HitFill = False
+ R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHit)
+ Canvas.AddText("Left Click Line", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+
+ x += dx
+ color = "Red"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color + "Rectangle"
+ R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHit)
+ Canvas.AddText("Left Click Fill", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x = 20
+ y += dy
+ color = "LightBlue"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color + " Rectangle"
+ R.Bind(FloatCanvas.EVT_FC_RIGHT_DOWN, self.RectGotHit)
+ Canvas.AddText("Right Click Fill", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "Grey"
+ R = Canvas.AddEllipse(x, y, w, h,LineWidth = 2,FillColor = color)
+ R.Name = color +" Ellipse"
+ R.Bind(FloatCanvas.EVT_FC_RIGHT_DOWN, self.RectGotHit)
+ Canvas.AddText("Right Click Fill", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "Brown"
+ R = Canvas.AddCircle(x+dx/2, y+dy/2, dx/4, LineWidth = 2, FillColor = color)
+ R.Name = color + " Circle"
+ R.HitFill = True
+ R.Bind(FloatCanvas.EVT_FC_LEFT_DCLICK, self.RectGotHit)
+ Canvas.AddText("Left D-Click Fill", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x = 20
+ y += dy
+ color = "Pink"
+ R = Canvas.AddCircle(x+dx/2, y+dy/2, dx/4, LineWidth = 2,FillColor = color)
+ R.Name = color + " Circle"
+ R.Bind(FloatCanvas.EVT_FC_LEFT_UP, self.RectGotHit)
+ Canvas.AddText("Left Up Fill", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "White"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color + " Rectangle"
+ R.Bind(FloatCanvas.EVT_FC_MIDDLE_DOWN, self.RectGotHit)
+ Canvas.AddText("Middle Down", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "AQUAMARINE"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color + " Rectangle"
+ R.Bind(FloatCanvas.EVT_FC_MIDDLE_UP, self.RectGotHit)
+ Canvas.AddText("Middle Up", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x = 20
+ y += dy
+ color = "CORAL"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color + " Rectangle"
+ R.Bind(FloatCanvas.EVT_FC_MIDDLE_DCLICK, self.RectGotHit)
+ Canvas.AddText("Middle DoubleClick", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "CYAN"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color + " Rectangle"
+ R.Bind(FloatCanvas.EVT_FC_RIGHT_UP, self.RectGotHit)
+ Canvas.AddText("Right Up", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "LIME GREEN"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color + " Rectangle"
+ R.Bind(FloatCanvas.EVT_FC_RIGHT_DCLICK, self.RectGotHit)
+ Canvas.AddText("Right Double Click", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x = 20
+ y += dy
+ color = "MEDIUM GOLDENROD"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color
+ R.Bind(FloatCanvas.EVT_FC_RIGHT_DOWN, self.RectGotHitRight)
+ R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHitLeft)
+ Canvas.AddText("L and R Click", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "SALMON"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color + " Rectangle"
+ R.Bind(FloatCanvas.EVT_FC_ENTER_OBJECT, self.RectMouseOver)
+ Canvas.AddText("Mouse Enter", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "MEDIUM VIOLET RED"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color
+ R.Bind(FloatCanvas.EVT_FC_LEAVE_OBJECT, self.RectMouseLeave)
+ Canvas.AddText("Mouse Leave", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x = 20
+ y += dy
+ color = "SKY BLUE"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color)
+ R.Name = color
+ R.Bind(FloatCanvas.EVT_FC_ENTER_OBJECT, self.RectMouseOver)
+ R.Bind(FloatCanvas.EVT_FC_LEAVE_OBJECT, self.RectMouseLeave)
+ Canvas.AddText("Enter and Leave", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "WHEAT"
+ R = Canvas.AddRectangle(x, y, w+12, h, LineColor = None, FillColor = color)
+ R.Name = color
+ R.Bind(FloatCanvas.EVT_FC_ENTER_OBJECT, self.RectMouseOver)
+ R.Bind(FloatCanvas.EVT_FC_LEAVE_OBJECT, self.RectMouseLeave)
+ Canvas.AddText("Mouse Enter&Leave", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "KHAKI"
+ R = Canvas.AddRectangle(x-12, y, w+12, h, LineColor = None, FillColor = color)
+ R.Name = color
+ R.Bind(FloatCanvas.EVT_FC_ENTER_OBJECT, self.RectMouseOver)
+ R.Bind(FloatCanvas.EVT_FC_LEAVE_OBJECT, self.RectMouseLeave)
+ Canvas.AddText("Mouse ENter&Leave", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x = 20
+ y += dy
+ L = Canvas.AddLine(( (x, y), (x+10, y+10), (x+w, y+h) ), LineWidth = 2, LineColor = "Red")
+ L.Name = "A Line"
+ L.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHitLeft)
+ Canvas.AddText("Left Down", x, y, Position = "bl")
+ Canvas.AddText(L.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "SEA GREEN"
+ Points = Numeric.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), Numeric.Float)
+ R = Canvas.AddPolygon(Points, LineWidth = 2, FillColor = color)
+ R.Name = color + " Polygon"
+ R.Bind(FloatCanvas.EVT_FC_RIGHT_DOWN, self.RectGotHitRight)
+ Canvas.AddText("RIGHT_DOWN", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x += dx
+ color = "Red"
+ Points = Numeric.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), Numeric.Float)
+ R = Canvas.AddPointSet(Points, Diameter = 4, Color = color)
+ R.Name = "PointSet"
+ R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.PointSetGotHit)
+ Canvas.AddText("LEFT_DOWN", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ x = 20
+ y += dy
+ T = Canvas.AddText("Hit-able Text", x, y, Size = 15, Color = "Red", Position = 'tl')
+ T.Name = "Hit-able Text"
+ T.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHitLeft)
+ Canvas.AddText("Left Down", x, y, Position = "bl")
+
+ x += dx
+ T = Canvas.AddScaledText("Scaled Text", x, y, Size = 1./2*h, Color = "Pink", Position = 'bl')
+ Canvas.AddPointSet( (x, y), Diameter = 3)
+ T.Name = "Scaled Text"
+ T.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHitLeft)
+ Canvas.AddText("Left Down", x, y, Position = "tl")
+
+ self.Canvas.ZoomToBB()
+
+ def TestHitTestForeground(self,event=None):
+ print "Running: TestHitTestForeground"
+ wx.GetApp().Yield(True)
+
+ self.UnBindAllMouseEvents()
+ Canvas = self.Canvas
+
+ Canvas.ClearAll()
+ Canvas.SetProjectionFun(None)
+
+ #Add a Hitable rectangle
+ w, h = 60, 20
+
+ dx = 80
+ dy = 40
+ x,y = 20, 20
+
+ color = "Red"
+ R = Canvas.AddRectangle(x, y, w, h, LineWidth = 2, FillColor = color, InForeground = False)
+ R.Name = color + "Rectangle"
+ R.HitFill = True
+ R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHit)
+ Canvas.AddText("Left Click Fill", x, y, Position = "bl")
+ Canvas.AddText(R.Name, x, y+h, Position = "tl")
+
+ ## A set of Rectangles that move together
+
+ ## NOTE: In a real app, it might be better to create a new
+ ## custom FloatCanvas DrawObject
+
+ self.MovingRects = []
+ x += dx
+ color = "LightBlue"
+ R = Canvas.AddRectangle(x, y, w/2, h/2, LineWidth = 2, FillColor = color, InForeground = True)
+ R.HitFill = True
+ R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectMoveLeft)
+ L = Canvas.AddText("Left", x + w/4, y + h/4, Position = "cc", InForeground = True)
+ self.MovingRects.extend( (R,L) )
+
+ x += w/2
+ R = Canvas.AddRectangle(x, y, w/2, h/2, LineWidth = 2, FillColor = color, InForeground = True)
+ R.HitFill = True
+ R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectMoveRight)
+ L = Canvas.AddText("Right", x + w/4, y + h/4, Position = "cc", InForeground = True)
+ self.MovingRects.extend( (R,L) )
+
+ x -= w/2
+ y += h/2
+ R = Canvas.AddRectangle(x, y, w/2, h/2, LineWidth = 2, FillColor = color, InForeground = True)
+ R.HitFill = True
+ R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectMoveUp)
+ L = Canvas.AddText("Up", x + w/4, y + h/4, Position = "cc", InForeground = True)
+ self.MovingRects.extend( (R,L) )
+
+
+ x += w/2
+ R = Canvas.AddRectangle(x, y, w/2, h/2, LineWidth = 2, FillColor = color, InForeground = True)
+ R.HitFill = True
+ R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectMoveDown)
+ L = Canvas.AddText("Down", x + w/4, y + h/4, Position = "cc", InForeground = True)
+ self.MovingRects.extend( (R,L) )
+
+ self.Canvas.ZoomToBB()
+
+ def RectMoveLeft(self,Object):
+ self.MoveRects("left")
+
+ def RectMoveRight(self,Object):
+ self.MoveRects("right")
+
+ def RectMoveUp(self,Object):
+ self.MoveRects("up")
+
+ def RectMoveDown(self,Object):
+ self.MoveRects("down")
+
+ def MoveRects(self, Dir):
+ for Object in self.MovingRects:
+ X,Y = Object.XY
+ if Dir == "left": X -= 10
+ elif Dir == "right": X += 10
+ elif Dir == "up": Y += 10
+ elif Dir == "down": Y -= 10
+ Object.SetXY(X,Y)
+ self.Canvas.Draw()
+
+
+ def PointSetGotHit(self, Object):
+ print Object.Name, "Got Hit\n"
+
+ def RectGotHit(self, Object):
+ print Object.Name, "Got Hit\n"
+
+ def RectGotHitRight(self, Object):
+ print Object.Name, "Got Hit With Right\n"
+
+ def RectGotHitLeft(self, Object):
+ print Object.Name, "Got Hit with Left\n"
+
+ def RectMouseOver(self, Object):
+ print "Mouse entered:", Object.Name
+
+ def RectMouseLeave(self, Object):
+ print "Mouse left ", Object.Name
+
+
+ def TestText(self, event= None):
+ wx.GetApp().Yield(True)
+
+ self.BindAllMouseEvents()
+ Canvas = self.Canvas
+ Canvas.ClearAll()
+ Canvas.SetProjectionFun(None)
+
+ x,y = (0, 0)
+
+ ## Add a non-visible rectangle, just to get a Bounding Box
+ ## Text objects have a zero-size bounding box, because it changes with zoom
+ Canvas.AddRectangle(-10,-10,20,20,LineWidth = 1, LineColor = None)
+
+ # Text
+ String = "Some text"
+## for i in range(10):
+## ts = random.randint(10,40)
+## cf = random.randint(0,len(colors)-1)
+## x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+ self.Canvas.AddText("Top Left",x,y,Size = 14,Color = "Yellow",BackgroundColor = "Blue", Position = "tl")
+ self.Canvas.AddText("Bottom Left",x,y,Size = 14,Color = "Cyan",BackgroundColor = "Black",Position = "bl")
+ self.Canvas.AddText("Top Right",x,y,Size = 14,Color = "Black",BackgroundColor = "Cyan",Position = "tr")
+ self.Canvas.AddText("Bottom Right",x,y,Size = 14,Color = "Blue",BackgroundColor = "Yellow",Position = "br")
+ Canvas.AddPointSet((x,y), Color = "White", Diameter = 2)
+
+ x,y = (0, 2)
+
+ Canvas.AddPointSet((x,y), Color = "White", Diameter = 2)
+ self.Canvas.AddText("Top Center",x,y,Size = 14,Color = "Black",Position = "tc")
+ self.Canvas.AddText("Bottom Center",x,y,Size = 14,Color = "White",Position = "bc")
+
+ x,y = (0, 4)
+
+ Canvas.AddPointSet((x,y), Color = "White", Diameter = 2)
+ self.Canvas.AddText("Center Right",x,y,Size = 14,Color = "Black",Position = "cr")
+ self.Canvas.AddText("Center Left",x,y,Size = 14,Color = "Black",Position = "cl")
+
+ x,y = (0, -2)
+
+ Canvas.AddPointSet((x,y), Color = "White", Diameter = 2)
+ self.Canvas.AddText("Center Center",x,y,Size = 14,Color = "Black",Position = "cc")
+
+ self.Canvas.AddText("40 Pixels",-10,8,Size = 40)
+ self.Canvas.AddText("20 Pixels",-10,5,Size = 20)
+ self.Canvas.AddText("10 Pixels",-10,3,Size = 10)
+
+ self.Canvas.AddText("MODERN Font", -10, 0, Family = wx.MODERN)
+ self.Canvas.AddText("DECORATIVE Font", -10, -1, Family = wx.DECORATIVE)
+ self.Canvas.AddText("ROMAN Font", -10, -2, Family = wx.ROMAN)
+ self.Canvas.AddText("SCRIPT Font", -10, -3, Family = wx.SCRIPT)
+ self.Canvas.AddText("ROMAN BOLD Font", -10, -4, Family = wx.ROMAN, Weight=wx.BOLD)
+ self.Canvas.AddText("ROMAN ITALIC BOLD Font", -10, -5, Family = wx.ROMAN, Weight=wx.BOLD, Style=wx.ITALIC)
+
+ # NOTE: this font exists on my Linux box..who knows were else you'll find it!
+ Font = wx.Font(20, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "zapf chancery")
+ self.Canvas.AddText("zapf chancery Font", -10, -6, Font = Font)
+
+ self.Canvas.ZoomToBB()
+
+ def TestScaledText(self, event= None):
+ wx.GetApp().Yield(True)
+
+ self.BindAllMouseEvents()
+ Canvas = self.Canvas
+ Canvas.ClearAll()
+ Canvas.SetProjectionFun(None)
+
+ x,y = (0, 0)
+
+ T = Canvas.AddScaledText("Top Left",x,y,Size = 5,Color = "Yellow",BackgroundColor = "Blue", Position = "tl")
+ T = Canvas.AddScaledText("Bottom Left",x,y,Size = 5,Color = "Cyan",BackgroundColor = "Black",Position = "bl")
+ T = Canvas.AddScaledText("Top Right",x,y,Size = 5,Color = "Black",BackgroundColor = "Cyan",Position = "tr")
+ T = Canvas.AddScaledText("Bottom Right",x,y,Size = 5,Color = "Blue",BackgroundColor = "Yellow",Position = "br")
+ Canvas.AddPointSet((x,y), Color = "Red", Diameter = 4)
+
+
+ x,y = (0, 20)
+
+ Canvas.AddScaledText("Top Center",x,y,Size = 7,Color = "Black",Position = "tc")
+ Canvas.AddScaledText("Bottom Center",x,y,Size = 7,Color = "White",Position = "bc")
+ Canvas.AddPointSet((x,y), Color = "White", Diameter = 4)
+
+ x,y = (0, -20)
+
+ Canvas.AddScaledText("Center Right",x,y,Size = 9,Color = "Black",Position = "cr")
+ Canvas.AddScaledText("Center Left",x,y,Size = 9,Color = "Black",Position = "cl")
+ Canvas.AddPointSet((x,y), Color = "White", Diameter = 4)
+
+ x = -200
+
+ self.Canvas.AddScaledText("MODERN Font", x, 0, Size = 7, Family = wx.MODERN, Color = (0,0,0))
+ self.Canvas.AddScaledText("DECORATIVE Font", x, -10, Size = 7, Family = wx.DECORATIVE, Color = (0,0,1))
+ self.Canvas.AddScaledText("ROMAN Font", x, -20, Size = 7, Family = wx.ROMAN)
+ self.Canvas.AddScaledText("SCRIPT Font", x, -30, Size = 7, Family = wx.SCRIPT)
+ self.Canvas.AddScaledText("ROMAN BOLD Font", x, -40, Size = 7, Family = wx.ROMAN, Weight=wx.BOLD)
+ self.Canvas.AddScaledText("ROMAN ITALIC BOLD Font", x, -50, Size = 7, Family = wx.ROMAN, Weight=wx.BOLD, Style=wx.ITALIC)
+ Canvas.AddPointSet((x,0), Color = "White", Diameter = 4)
+
+
+ # NOTE: this font exists on my Linux box..who knows were else you'll find it!
+ x,y = (-100, 50)
+ Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "zapf chancery")
+ T = self.Canvas.AddScaledText("zapf chancery Font", x, y, Size = 20, Font = Font, Position = 'bc')
+
+ x,y = (-50, -50)
+ Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "bookman")
+ T = self.Canvas.AddScaledText("Bookman Font", x, y, Size = 8, Font = Font)
+
self.Canvas.ZoomToBB()
def DrawMap(self,event = None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
import os, time
+ self.BindAllMouseEvents()
+
## Test of Actual Map Data
- self.Clear()
- start = time.clock()
+ self.Canvas.ClearAll()
+ self.Canvas.SetProjectionFun("FlatEarth")
+ #start = time.clock()
Shorelines = Read_MapGen(os.path.join("data",'world.dat'),stats = 0)
- print "It took %f seconds to load %i shorelines"%(time.clock() - start,len(Shorelines) )
- start = time.clock()
+ #print "It took %f seconds to load %i shorelines"%(time.clock() - start,len(Shorelines) )
+ #start = time.clock()
for segment in Shorelines:
- self.object_list.append(self.Canvas.AddLine(segment))
- print "It took %f seconds to add %i shorelines"%(time.clock() - start,len(Shorelines) )
- start = time.clock()
+ self.Canvas.AddLine(segment)
+ #print "It took %f seconds to add %i shorelines"%(time.clock() - start,len(Shorelines) )
+ #start = time.clock()
self.Canvas.ZoomToBB()
- print "It took %f seconds to draw %i shorelines"%(time.clock() - start,len(Shorelines) )
-
- ## def LineTest(self,event = None):
- ## wxGetApp().Yield()
- ## import os, time
- ## import random
- ## Range = (-10,10)
- ## ## Test of drawing lots of lines
- ## self.Clear()
- ## start = time.clock()
- ## linepoints = []
- ## linecolors = []
- ## linewidths = []
- ## linestyles = []
- ## for i in range(500):
- ## points = (random.randint(Range[0],Range[1]),
- ## random.randint(Range[0],Range[1]))
- ## linepoints.append(points)
- ## points = (random.randint(Range[0],Range[1]),
- ## random.randint(Range[0],Range[1]))
- ## linepoints.append(points)
- ## linewidths.append(random.randint(1,10) )
- ## linecolors.append(colors[random.randint(0,len(colors)-1) ])
- ## linestyles.append(LineStyles[random.randint(0, len(LineStyles)-1)])
-
- ## self.object_list.append(self.Canvas.AddLineSet(linepoints, LineWidths = linewidths, LineColors = linecolors, LineStyles = linestyles))
- ## print "It took %f seconds to add %i lines"%(time.clock() - start,len(linepoints) )
- ## start = time.clock()
- ## self.Canvas.ZoomToBB()
- ## print "It took %f seconds to draw %i lines"%(time.clock() - start,len(linepoints) )
+ #print "It took %f seconds to draw %i shorelines"%(time.clock() - start,len(Shorelines) )
+
def LineTest(self,event = None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
import os, time
- import random
+# import random
+ colors = self.colors
Range = (-10,10)
## Test of drawing lots of lines
- self.Clear()
- start = time.clock()
+ Canvas = self.Canvas
+ Canvas.ClearAll()
+ Canvas.SetProjectionFun(None)
+ #start = time.clock()
linepoints = []
linecolors = []
linewidths = []
linewidths.append(random.randint(1,10) )
linecolors.append(random.randint(0,len(colors)-1) )
for (points,color,width) in zip(linepoints,linecolors,linewidths):
- self.object_list.append(self.Canvas.AddLine((points[0:2],points[2:4]), LineWidth = width, LineColor = colors[color]))
- print "It took %f seconds to add %i lines"%(time.clock() - start,len(linepoints) )
+ Canvas.AddLine((points[0:2],points[2:4]), LineWidth = width, LineColor = colors[color])
+ #print "It took %f seconds to add %i lines"%(time.clock() - start,len(linepoints) )
+ #start = time.clock()
+ Canvas.ZoomToBB()
+ #print "It took %f seconds to draw %i lines"%(time.clock() - start,len(linepoints) )
+
+ def SpeedTest(self,event=None):
+ wx.GetApp().Yield(True)
+# import random
+# import RandomArray
+ BigRange = (-1000,1000)
+ colors = self.colors
+
+ self.UnBindAllMouseEvents()
+ Canvas = self.Canvas
+
+ Canvas.ClearAll()
+ Canvas.SetProjectionFun(None)
+
+ # Lots of Text
+ String = "Unscaled text"
+ coords = []
+ text = []
+ for i in range(5000):
+ x,y = (random.uniform(BigRange[0],BigRange[1]),random.uniform(BigRange[0],BigRange[1]))
+ coords.append( (x,y) )
+ print "Drawing the Numbers"
start = time.clock()
- self.Canvas.ZoomToBB()
- print "It took %f seconds to draw %i lines"%(time.clock() - start,len(linepoints) )
-
+ for i in xrange( len(coords) ):
+ Canvas.AddText("%i"%(i),
+ coords[i][0],
+ coords[i][1],
+ Size = 12,
+ Position = "cc",
+ BackgroundColor = "White")
+ print "It took %s seconds to add the numbers"%(time.clock() - start)
+
+
+## ObjectList = []
+
+## print "Building a list of lots of random objects"
+## ## Random tests of everything:
+## def MakeRange():
+## while True:
+## Range = ( random.randint(BigRange[0],BigRange[1]), random.randint(BigRange[0],BigRange[1]) )
+## if abs (Range[0] - Range[1]) < 10:
+## continue
+## if Range[0] > Range[1]:
+## Range = ( Range[1], Range[0] )
+## break
+## return Range
+
+## # Rectangles
+## for i in range(300):
+## Range = MakeRange()
+## x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+## lw = random.randint(1,5)
+## cf = random.randint(0,len(colors)-1)
+## h = random.randint(1, Range[1] - Range[0])
+## w = random.randint(1, Range[1] - Range[0])
+## ObjectList.append(FloatCanvas.Rectangle(x,y,h,w,LineWidth = lw,FillColor = colors[cf]))
+
+## # Ellipses
+## for i in range(300):
+## Range = MakeRange()
+## x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+## lw = random.randint(1,5)
+## cf = random.randint(0,len(colors)-1)
+## h = random.randint(1, Range[1] - Range[0])
+## w = random.randint(1, Range[1] - Range[0])
+## ObjectList.append(FloatCanvas.Ellipse(x,y,h,w,LineWidth = lw,FillColor = colors[cf]))
+
+
+## # Circles
+## for i in range(500):
+## Range = MakeRange()
+## x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+## D = random.randint(1, (Range[1] - Range[0]) / 2)
+## lw = random.randint(1,5)
+## cf = random.randint(0,len(colors)-1)
+## cl = random.randint(0,len(colors)-1)
+## ObjectList.append(FloatCanvas.Circle(x,y,D,LineWidth = lw,LineColor = colors[cl],FillColor = colors[cf]))
+## #ObjectList.append(FloatCanvas.Text("Circle # %i"%(i),x,y,Size = 12,BackgroundColor = None,Position = "cc"))
+
+## # Lines
+## for i in range(500):
+## Range = MakeRange()
+## points = []
+## for j in range(random.randint(2,100)):
+## point = (random.randint(Range[0],Range[1]),random.randint(Range[0],Range[1]))
+## points.append(point)
+## lw = random.randint(1,10)
+## cf = random.randint(0,len(colors)-1)
+## cl = random.randint(0,len(colors)-1)
+## ObjectList.append(FloatCanvas.Line(points, LineWidth = lw, LineColor = colors[cl]) )
+
+## # Polygons
+## for i in range(300):
+## Range = MakeRange()
+## points = []
+## for j in range(random.randint(2,60)):
+## point = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+## points.append(point)
+## lw = random.randint(1,6)
+## cf = random.randint(0,len(colors)-1)
+## cl = random.randint(0,len(colors)-1)
+## ObjectList.append(FloatCanvas.Polygon(points,
+## LineWidth = lw,
+## LineColor = colors[cl],
+## FillColor = colors[cf],
+## FillStyle = 'Solid') )
+## random.shuffle(ObjectList)
+## print "Adding lots of random objects"
+## start = time.clock()
+## for Object in ObjectList:
+## Canvas.AddObject(Object)
+## print "It took %s Seconds to add %i objects "%(time.clock() - start, len(ObjectList) )
+
+## ## Pointset
+## for i in range(100):
+## points = []
+## points = RandomArray.uniform(Range[0],Range[1],(100,2))
+## cf = random.randint(0,len(colors)-1)
+## D = random.randint(1,4)
+## Canvas.AddPointSet(points, Color = colors[cf], Diameter = D)
+
+
+## # Scaled Text
+## String = "Scaled text"
+## for i in range(30):
+## ts = random.random()*3 + 0.2
+## cf = random.randint(0,len(colors)-1)
+## x,y = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+## Canvas.AddScaledText(String, x, y, Size = ts, Color = colors[cf], Position = "cc")
+
+ Canvas.ZoomToBB()
+
+
+ def TempTest(self, event= None):
+ "Running the Temporary test"
+ wx.GetApp().Yield(True)
+
+ self.UnBindAllMouseEvents()
+ Canvas = self.Canvas
+ Canvas.ClearAll()
+ Canvas.SetProjectionFun(None)
+
+# import random
+# import RandomArray
+ Range = (-10,10)
+
+ # Create a random Polygon
+ points = []
+ for j in range(6):
+ point = (random.uniform(Range[0],Range[1]),random.uniform(Range[0],Range[1]))
+ points.append(point)
+ Poly = Canvas.AddPolygon(points,
+ LineWidth = 2,
+ LineColor = "Black",
+ FillColor = "LightBlue",
+ FillStyle = 'Solid')
+
+ Poly.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.SelectPoly)
+
+ self.SelectedPoly = None
+ self.SelectPoints = []
+ self.SelectedPoint = None
+
+ Canvas.ZoomToBB()
+
+ def SelectPoly(self, Object):
+ print "In SelectPoly"
+ Canvas = self.Canvas
+ if Object is self.SelectedPoly:
+ pass
+ else:
+ #fixme: Do something to unselect the old one
+ self.SelectedPoly = Object
+ Canvas.RemoveObjects(self.SelectPoints)
+ self.SelectPoints = []
+ # Draw points on the Vertices of the Selected Poly:
+ for i, point in enumerate(Object.Points):
+ P = Canvas.AddPointSet(point, Diameter = 6, Color = "Red")
+ P.VerticeNum = i
+ P.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.SelectPointHit)
+ self.SelectPoints.append(P)
+ #Canvas.ZoomToBB()
+ Canvas.Draw()
+
+ def SelectPointHit(self, Point):
+ print "Point Num: %i Hit"%Point.VerticeNum
+ self.SelectedPoint = Point
+
+
+
class DemoApp(wx.App):
"""
How the demo works:
Chris.Barker@noaa.gov
"""
+
+ def __init__(self, *args, **kwargs):
+ wx.App.__init__(self, *args, **kwargs)
def OnInit(self):
- global colors
- wx.lib.colourdb.updateColourDB()
- colors = wx.lib.colourdb.getColourList()
-
- frame = DrawFrame(None, -1, "FloatCanvas Demo App",
- wx.DefaultPosition, (700,700))
+ wx.InitAllImageHandlers()
+ frame = DrawFrame(None, -1, "FloatCanvas Demo App",wx.DefaultPosition,(700,700))
self.SetTopWindow(frame)
-
+ frame.Show()
+
+ ## check to see if the demo is set to start in a particular mode.
+ if StartUpDemo == "text":
+ frame.TestText()
+ if StartUpDemo == "stext":
+ frame.TestScaledText()
+ elif StartUpDemo == "all":
+ frame.DrawTest()
+ elif StartUpDemo == "map":
+ frame.DrawMap()
+ elif StartUpDemo == "hit":
+ frame.TestHitTest()
+ elif StartUpDemo == "hitf":
+ "starting TestHitTestForeground"
+ frame.TestHitTestForeground()
+ elif StartUpDemo == "animate":
+ "starting TestAnimation"
+ frame.TestAnimation()
+ elif StartUpDemo == "speed":
+ "starting SpeedTest"
+ frame.SpeedTest()
+ elif StartUpDemo == "temp":
+ "starting temp Test"
+ frame.TempTest()
+
return True
def Read_MapGen(filename,stats = 0,AllLines=0):
"""
import string
- from Numeric import array
file = open(filename,'rt')
data = file.readlines()
data = map(string.strip,data)
for line in data:
if line:
if line == "# -b": #New segment beginning
- if segment: Shorelines.append(array(segment))
+ if segment: Shorelines.append(Numeric.array(segment))
segment = []
else:
segment.append(map(float,string.split(line)))
- if segment: Shorelines.append(array(segment))
+ if segment: Shorelines.append(Numeric.array(segment))
if stats:
NumSegments = len(Shorelines)
Lines.append(point)
Lines.append(point)
Lines.append(segment[-1])
- #print Shorelines
- #for point in Lines: print point
return Lines
else:
return Shorelines
-
-
- #----------------------------------------------------------------------
-
- def runTest(frame, nb, log):
- """
- This method is used by the wxPython Demo Framework for integrating
- this demo with the rest.
- """
- global colors
- wx.lib.colourdb.updateColourDB()
- colors = wx.lib.colourdb.getColourList()
-
- win = DrawFrame(None, -1, "FloatCanvas Drawing Window",
- wx.DefaultPosition, (500,500))
- frame.otherWin = win
- win.Show(True)
-
-
-
## for the wxPython demo:
- overview = floatcanvas.FloatCanvas.__doc__
-
+ try:
+ import floatcanvas
+ except ImportError: # if it's not there locally, try the wxPython lib.
+ from wx.lib import floatcanvas
+ overview = floatcanvas.__doc__
if __name__ == "__main__":
if not haveNumeric:
print errorText
else:
- app = DemoApp(0)
-
- import wx.lib.colourdb
- wx.lib.colourdb.updateColourDB()
- colors = wx.lib.colourdb.getColourList()
-
+ app = DemoApp(False)# put in True if you want output to go to it's own window.
app.MainLoop()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# new stuff
('Recent Additions and Updates', [
'OGL',
+ 'FloatCanvas',
]),
# managed windows == things with a (optional) caption you can close
wxPython/wx/lib/masked
wxPython/wx/lib/mixins
wxPython/wx/lib/ogl
+wxPython/wx/lib/floatcanvas
wxPython/wx/py
wxPython/wx/py/tests
wxPython/wxPython
Source: "wx\lib\mixins\*.py"; DestDir: "{app}\wx\lib\mixins"; Components: core
Source: "wx\lib\masked\*.py"; DestDir: "{app}\wx\lib\masked"; Components: core
Source: "wx\lib\ogl\*.py"; DestDir: "{app}\wx\lib\ogl"; Components: core
+Source: "wx\lib\floatcanvas\*.py"; DestDir: "{app}\wx\lib\floatcanvas"; Components: core
Source: "wx\py\*.py"; DestDir: "{app}\wx\py"; Components: core
Source: "wx\py\*.txt"; DestDir: "{app}\wx\py"; Components: core
Source: "wx\py\*.ico"; DestDir: "{app}\wx\py"; Components: core
Type: files; Name: "{app}\wx\lib\editor\*.pyo";
Type: files; Name: "{app}\wx\lib\mixins\*.pyc";
Type: files; Name: "{app}\wx\lib\mixins\*.pyo";
+Type: files; Name: "{app}\wx\lib\masked\*.pyc";
+Type: files; Name: "{app}\wx\lib\masked\*.pyo";
+Type: files; Name: "{app}\wx\lib\ogl\*.pyc";
+Type: files; Name: "{app}\wx\lib\ogl\*.pyo";
+Type: files; Name: "{app}\wx\lib\floatcanvas\*.pyc";
+Type: files; Name: "{app}\wx\lib\floatcanvas\*.pyo";
+
Type: files; Name: "{app}\wx\py\*.pyc";
Type: files; Name: "{app}\wx\py\*.pyo";
Type: files; Name: "{app}\wx\py\tests\*.pyc";
were using these in your apps then please join wxPython-dev and assist
with a more modern reimplementation.
+Added a new version (0.8.3)of FloatCanvas from Chris Barker. It's now
+in a subpackage of wx.lib.
+
'wx.lib',
'wx.lib.colourchooser',
'wx.lib.editor',
+ 'wx.lib.floatcanvas',
'wx.lib.masked',
'wx.lib.mixins',
'wx.lib.ogl',
+++ /dev/null
-import wx
-
-from Numeric import array,Float,cos,pi,sum,minimum,maximum,Int32
-from time import clock, sleep
-import types
-import os
-
-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_TEST_BUTTON = wx.NewId()
-
-ID_ABOUT_MENU = wx.NewId()
-ID_EXIT_MENU = wx.NewId()
-ID_ZOOM_TO_FIT_MENU = wx.NewId()
-ID_DRAWTEST_MENU = wx.NewId()
-ID_DRAWMAP_MENU = wx.NewId()
-ID_CLEAR_MENU = wx.NewId()
-
-ID_TEST = wx.NewId()
-
-
-### These are some functions for bitmaps of icons.
-import cPickle, zlib
-
-def GetHandData():
- return cPickle.loads(zlib.decompress(
-'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
-\x01\xc8S\xb6t\x06A(\x1f\x0b\xa0\xa9\x8c\x9e\x1e6\x19\xa0\xa8\x1e\x88\xd4C\
-\x97\xd1\x83\xe8\x80 \x9c2zh\xa6\xc1\x11X\n\xab\x8c\x02\x8a\x0cD!\x92\x12\
-\x98\x8c\x1e\x8a\x8b\xd1d\x14\xf4\x90%\x90LC\xf6\xbf\x1e\xba\xab\x91%\xd0\
-\xdc\x86C\x06\xd9m\xe8!\xaa\x87S\x86\x1a1\xa7\x07\x00v\x0f[\x17' ))
-
-def GetHandBitmap():
- return wx.BitmapFromXPMData(GetHandData())
-
-#----------------------------------------------------------------------
-def GetPlusData():
- return cPickle.loads(zlib.decompress(
-'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
-\x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=l2`\r\
-\xe82HF\xe9a\xc8\xe8\xe9A\x9c@\x8a\x0c\x0e\xd3p\xbb\x00\x8f\xab\xe1>\xd5\xd3\
-\xc3\x15:P)l!\n\x91\xc2\x1a\xd6`)\xec\xb1\x00\x92\xc2\x11?\xb8e\x88\x8fSt\
-\x19=\x00\x82\x16[\xf7' ))
-
-def GetPlusBitmap():
- return wx.BitmapFromXPMData(GetPlusData())
-
-#----------------------------------------------------------------------
-def GetMinusData():
- return cPickle.loads(zlib.decompress(
-'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
-\x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=\xa2e\
-\x10\x16@\x99\xc82zz\x10\'\x90"\x83\xc34r\xdc\x86\xf0\xa9\x9e\x1e\xae\xd0\
-\x81Ja\x0bQ\x88\x14\xd6\xb0\x06Ka\x8f\x05\x90\x14\x8e\xf8\xc1-C|\x9c\xa2\xcb\
-\xe8\x01\x00\xed\x0f[\x87' ))
-
-def GetMinusBitmap():
- return wx.BitmapFromXPMData(GetMinusData())
-
-## This is a bunch of stuff for implimenting interactive use: catching
-## when objects are clicked on by the mouse, etc. I've made a start, so if
-## you are interesed in making that work, let me know, I may have gotten
-## it going by then
-
-#### I probably want a full set of events someday:
-## #### mouse over, right click, left click mouse up etc, etc.
-## ##FLOATCANVAS_EVENT_LEFT_DOWN = wx.NewEventType()
-## ##FLOATCANVAS_EVENT_LEFT_UP = wx.NewEventType()
-## ##FLOATCANVAS_EVENT_RIGHT_DOWN = wx.NewEventType()
-## ##FLOATCANVAS_EVENT_RIGHT_UP = wx.NewEventType()
-## ##FLOATCANVAS_EVENT_MOUSE_OVER = wx.NewEventType()
-
-##WXFLOATCANVASEVENT = wx.NewEventType()
-
-##def EVT_WXFLOATCANVASEVENT( window, function ):
-
-## """Your documentation here"""
-
-## window.Connect( -1, -1, WXFLOATCANVASEVENT, function )
-
-##class wxFloatCanvasObjectEvent(wx.PyCommandEvent):
-## def __init__(self, WindowID,Object):
-## wx.PyCommandEvent.__init__(self, WXFLOATCANVASEVENT, WindowID)
-## self.Object = Object
-
-## def Clone( self ):
-## self.__class__( self.GetId() )
-
-##class ColorGenerator:
-
-## """ An instance of this class generates a unique color each time
-## GetNextColor() is called. Someday I will use a proper Python
-## generator for this class.
-
-## The point of this generator is for the hit-test bitmap, each object
-## needs to be a unique color. Also, each system can be running a
-## different number of colors, and it doesn't appear to be possible to
-## have a wxMemDC with a different colordepth as the screen so this
-## generates colors far enough apart that they can be distinguished on
-## a 16bit screen. Anything less than 16bits won't work.
-## """
-
-## def __init__(self,depth = 16):
-## self.r = 0
-## self.g = 0
-## self.b = 0
-## if depth == 16:
-## self.step = 8
-## elif depth >= 24:
-## self.step = 1
-## else:
-## raise "ColorGenerator does not work with depth = %s"%depth
-
-## def GetNextColor(self):
-## step = self.step
-## ##r,g,b = self.r,self.g,self.b
-## self.r += step
-## if self.r > 255:
-## self.r = step
-## self.g += step
-## if self.g > 255:
-## self.g = step
-## self.b += step
-## if self.b > 255:
-## ## fixme: this should be a derived exception
-## raise "Too many objects for HitTest"
-## return (self.r,self.g,self.b)
-
-
-class draw_object:
- """
- This is the base class for all the objects that can be drawn.
-
- each object has the following properties; (incomplete)
-
- BoundingBox : is of the form: array((min_x,min_y),(max_x,max_y))
- Pen
- Brush
-
- """
-
- def __init__(self,Foreground = 0):
- self.Foreground = Foreground
-
- self._Canvas = None
-
- # I pre-define all these as class variables to provide an easier
- # interface, and perhaps speed things up by caching all the Pens
- # and Brushes, although that may not help, as I think wx now
- # does that on it's own. Send me a note if you know!
-
- BrushList = {
- ( None,"Transparent") : wx.TRANSPARENT_BRUSH,
- ("Blue","Solid") : wx.BLUE_BRUSH,
- ("Green","Solid") : wx.GREEN_BRUSH,
- ("White","Solid") : wx.WHITE_BRUSH,
- ("Black","Solid") : wx.BLACK_BRUSH,
- ("Grey","Solid") : wx.GREY_BRUSH,
- ("MediumGrey","Solid") : wx.MEDIUM_GREY_BRUSH,
- ("LightGrey","Solid") : wx.LIGHT_GREY_BRUSH,
- ("Cyan","Solid") : wx.CYAN_BRUSH,
- ("Red","Solid") : wx.RED_BRUSH
- }
- PenList = {
- (None,"Transparent",1) : wx.TRANSPARENT_PEN,
- ("Green","Solid",1) : wx.GREEN_PEN,
- ("White","Solid",1) : wx.WHITE_PEN,
- ("Black","Solid",1) : wx.BLACK_PEN,
- ("Grey","Solid",1) : wx.GREY_PEN,
- ("MediumGrey","Solid",1) : wx.MEDIUM_GREY_PEN,
- ("LightGrey","Solid",1) : wx.LIGHT_GREY_PEN,
- ("Cyan","Solid",1) : wx.CYAN_PEN,
- ("Red","Solid",1) : wx.RED_PEN
- }
-
- FillStyleList = {
- "Transparent" : wx.TRANSPARENT,
- "Solid" : wx.SOLID,
- "BiDiagonalHatch": wx.BDIAGONAL_HATCH,
- "CrossDiagHatch" : wx.CROSSDIAG_HATCH,
- "FDiagonal_Hatch": wx.FDIAGONAL_HATCH,
- "CrossHatch" : wx.CROSS_HATCH,
- "HorizontalHatch": wx.HORIZONTAL_HATCH,
- "VerticalHatch" : wx.VERTICAL_HATCH
- }
-
- LineStyleList = {
- "Solid" : wx.SOLID,
- "Transparent": wx.TRANSPARENT,
- "Dot" : wx.DOT,
- "LongDash" : wx.LONG_DASH,
- "ShortDash" : wx.SHORT_DASH,
- "DotDash" : wx.DOT_DASH,
- }
-
- def SetBrush(self,FillColor,FillStyle):
- if FillColor is None or FillStyle is None:
- self.Brush = wx.TRANSPARENT_BRUSH
- self.FillStyle = "Transparent"
- else:
- if not self.BrushList.has_key((FillColor,FillStyle)):
- self.BrushList[(FillColor,FillStyle)] = wx.Brush(FillColor,self.FillStyleList[FillStyle])
- self.Brush = self.BrushList[(FillColor,FillStyle)]
-
- def SetPen(self,LineColor,LineStyle,LineWidth):
- if (LineColor is None) or (LineStyle is None):
- self.Pen = wx.TRANSPARENT_PEN
- self.LineStyle = 'Transparent'
- else:
- if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
- self.PenList[(LineColor,LineStyle,LineWidth)] = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])
- self.Pen = self.PenList[(LineColor,LineStyle,LineWidth)]
-
- def SetPens(self,LineColors,LineStyles,LineWidths):
- """
- This method used when an object could have a list of pens, rather than just one
- It is used for LineSet, and perhaps others in the future.
-
- fixme: this is really kludgy, there has got to be a better way!
-
- """
-
- length = 1
- if type(LineColors) == types.ListType:
- length = len(LineColors)
- else:
- LineColors = [LineColors]
-
- if type(LineStyles) == types.ListType:
- length = len(LineStyles)
- else:
- LineStyles = [LineStyles]
-
- if type(LineWidths) == types.ListType:
- length = len(LineWidths)
- else:
- LineWidths = [LineWidths]
-
- if length > 1:
- if len(LineColors) == 1:
- LineColors = LineColors*length
- if len(LineStyles) == 1:
- LineStyles = LineStyles*length
- if len(LineWidths) == 1:
- LineWidths = LineWidths*length
-
- self.Pens = []
- for (LineColor,LineStyle,LineWidth) in zip(LineColors,LineStyles,LineWidths):
- if LineColor is None or LineStyle is None:
- self.Pens.append(wx.TRANSPARENT_PEN)
- # what's this for?> self.LineStyle = 'Transparent'
- if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
- Pen = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])
- self.Pens.append(Pen)
- else:
- self.Pens.append(self.PenList[(LineColor,LineStyle,LineWidth)])
- if length == 1:
- self.Pens = self.Pens[0]
-
- def PutInBackground(self):
- if self._Canvas and self.Foreground:
- self._Canvas._TopDrawList.remove(self)
- self._Canvas._DrawList.append(self)
- self._Canvas._BackgroundDirty = 1
- self.Foreground = 0
-
- def PutInForeground(self):
- if self._Canvas and (not self.Foreground):
- self._Canvas._TopDrawList.append(self)
- self._Canvas._DrawList.remove(self)
- self._Canvas._BackgroundDirty = 1
- self.Foreground = 1
-
-
-class Polygon(draw_object):
-
- """
-
- The Polygon class takes a list of 2-tuples, or a NX2 NumPy array of
- point coordinates. so that Points[N][0] is the x-coordinate of
- point N and Points[N][1] is the y-coordinate or Points[N,0] is the
- x-coordinate of point N and Points[N,1] is the y-coordinate for
- arrays.
-
- """
- def __init__(self,Points,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground = 0):
- draw_object.__init__(self,Foreground)
- self.Points = array(Points,Float)
- self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-
- self.LineColor = LineColor
- self.LineStyle = LineStyle
- self.LineWidth = LineWidth
- self.FillColor = FillColor
- self.FillStyle = FillStyle
-
- self.SetPen(LineColor,LineStyle,LineWidth)
- self.SetBrush(FillColor,FillStyle)
-
-
- def _Draw(self,dc,WorldToPixel,ScaleFunction):
- Points = WorldToPixel(self.Points)
- dc.SetPen(self.Pen)
- dc.SetBrush(self.Brush)
- #dc.DrawPolygon(map(lambda x: (x[0],x[1]), Points.tolist()))
- dc.DrawPolygon(Points)
-
-class PolygonSet(draw_object):
- """
- The PolygonSet class takes a Geometry.Polygon object.
- so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
-
- it creates a set of line segments, from (x1,y1) to (x2,y2)
-
- """
-
- def __init__(self,PolySet,LineColors,LineStyles,LineWidths,FillColors,FillStyles,Foreground = 0):
- draw_object.__init__(self, Foreground)
-
- ##fixme: there should be some error checking for everything being the right length.
-
-
- self.Points = array(Points,Float)
- self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-
- self.LineColors = LineColors
- self.LineStyles = LineStyles
- self.LineWidths = LineWidths
- self.FillColors = FillColors
- self.FillStyles = FillStyles
-
- self.SetPens(LineColors,LineStyles,LineWidths)
-
- def _Draw(self,dc,WorldToPixel,ScaleFunction):
- Points = WorldToPixel(self.Points)
- Points.shape = (-1,4)
- dc.DrawLineList(Points,self.Pens)
-
-
-class Line(draw_object):
- """
- The Line class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
- so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
- or Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
-
- It will draw a straight line if there are two points, and a polyline if there are more than two.
-
- """
- def __init__(self,Points,LineColor,LineStyle,LineWidth,Foreground = 0):
- draw_object.__init__(self, Foreground)
-
- self.Points = array(Points,Float)
- self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-
- self.LineColor = LineColor
- self.LineStyle = LineStyle
- self.LineWidth = LineWidth
-
- self.SetPen(LineColor,LineStyle,LineWidth)
-
- def SetPoints(self,Points):
- self.Points = Points
- self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
- if self._Canvas:
- # It looks like this shouldn't be private
- self._Canvas.BoundingBoxDirty = 1
-
- def _Draw(self,dc,WorldToPixel,ScaleFunction):
- Points = WorldToPixel(self.Points)
- dc.SetPen(self.Pen)
- #dc.DrawLines(map(lambda x: (x[0],x[1]), Points.tolist()))
- dc.DrawLines(Points)
-
-
-class LineSet(draw_object):
- """
- The LineSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
- so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
-
- it creates a set of line segments, from (x1,y1) to (x2,y2)
-
- """
-
- def __init__(self,Points,LineColors,LineStyles,LineWidths,Foreground = 0):
- draw_object.__init__(self, Foreground)
-
- NumLines = len(Points) / 2
- ##fixme: there should be some error checking for everything being the right length.
-
-
- self.Points = array(Points,Float)
- self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-
- self.LineColors = LineColors
- self.LineStyles = LineStyles
- self.LineWidths = LineWidths
-
- self.SetPens(LineColors,LineStyles,LineWidths)
-
- def _Draw(self,dc,WorldToPixel,ScaleFunction):
- Points = WorldToPixel(self.Points)
- Points.shape = (-1,4)
- dc.DrawLineList(Points,self.Pens)
-
-
-class PointSet(draw_object):
- """
- The PointSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
- so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
- or Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
-
- Each point will be drawn the same color and Diameter. The Diameter is in screen points,
- not world coordinates.
-
- """
- def __init__(self,Points,Color,Diameter,Foreground = 0):
- draw_object.__init__(self,Foreground)
-
- self.Points = array(Points,Float)
- self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
- self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-
- self.Color = Color
- self.Diameter = Diameter
-
- self.SetPen(Color,"Solid",1)
- self.SetBrush(Color,"Solid")
-
- def SetPoints(self,Points):
- self.Points = Points
- self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
- if self._Canvas:
- # It looks like this shouldn't be private
- self._Canvas.BoundingBoxDirty = 1
-
- def _Draw(self,dc,WorldToPixel,ScaleFunction):
- dc.SetPen(self.Pen)
- Points = WorldToPixel(self.Points)
- if self.Diameter <= 1:
- dc.DrawPointList(Points)
- elif self.Diameter <= 2:
- # A Little optimization for a diameter2 - point
- dc.DrawPointList(Points)
- dc.DrawPointList(Points + (1,0))
- dc.DrawPointList(Points + (0,1))
- dc.DrawPointList(Points + (1,1))
- else:
- dc.SetBrush(self.Brush)
- radius = int(round(self.Diameter/2))
- for (x,y) in Points:
- dc.DrawEllipse((x - radius), (y - radius), self.Diameter, self.Diameter)
-
-
-
-class Dot(draw_object):
- """
- The Dot class takes an x.y coordinate pair, and the Diameter of the circle.
- The Diameter is in pixels, so it won't change with zoom.
-
- Also Fill and line data
-
- """
- def __init__(self,x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground = 0):
- draw_object.__init__(self,Foreground)
-
- self.X = x
- self.Y = y
- self.Diameter = Diameter
- # NOTE: the bounding box does not include the diameter of the dot, as that is in pixel coords.
- # If this is a problem, perhaps you should use a circle, instead!
- self.BoundingBox = array(((x,y),(x,y)),Float)
-
- self.LineColor = LineColor
- self.LineStyle = LineStyle
- self.LineWidth = LineWidth
- self.FillColor = FillColor
- self.FillStyle = FillStyle
-
- self.SetPen(LineColor,LineStyle,LineWidth)
- self.SetBrush(FillColor,FillStyle)
-
- def _Draw(self,dc,WorldToPixel,ScaleFunction):
- dc.SetPen(self.Pen)
- dc.SetBrush(self.Brush)
- radius = int(round(self.Diameter/2))
- (X,Y) = WorldToPixel((self.X,self.Y))
- dc.DrawEllipse((X - radius), (Y - radius), self.Diameter, self.Diameter)
-
-
-class Rectangle(draw_object):
- def __init__(self,x,y,width,height,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground = 0):
- draw_object.__init__(self,Foreground)
-
- self.X = x
- self.Y = y
- self.Width = width
- self.Height = height
- self.BoundingBox = array(((x,y),(x+width,y+height)),Float)
- self.LineColor = LineColor
- self.LineStyle = LineStyle
- self.LineWidth = LineWidth
- self.FillColor = FillColor
- self.FillStyle = FillStyle
-
- self.SetPen(LineColor,LineStyle,LineWidth)
- self.SetBrush(FillColor,FillStyle)
-
- def _Draw(self,dc,WorldToPixel,ScaleFunction):
- (X,Y) = WorldToPixel((self.X,self.Y))
- (Width,Height) = ScaleFunction((self.Width,self.Height))
-
- dc.SetPen(self.Pen)
- dc.SetBrush(self.Brush)
- dc.DrawRectangle(X,Y, Width,Height)
-
-class Ellipse(draw_object):
- def __init__(self,x,y,width,height,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground = 0):
- draw_object.__init__(self,Foreground)
-
- self.X = x
- self.Y = y
- self.Width = width
- self.Height = height
- self.BoundingBox = array(((x,y),(x+width,y+height)),Float)
- self.LineColor = LineColor
- self.LineStyle = LineStyle
- self.LineWidth = LineWidth
- self.FillColor = FillColor
- self.FillStyle = FillStyle
-
- self.SetPen(LineColor,LineStyle,LineWidth)
- self.SetBrush(FillColor,FillStyle)
-
- def _Draw(self,dc,WorldToPixel,ScaleFunction):
- (X,Y) = WorldToPixel((self.X,self.Y))
- (Width,Height) = ScaleFunction((self.Width,self.Height))
-
- dc.SetPen(self.Pen)
- dc.SetBrush(self.Brush)
- dc.DrawEllipse(X,Y, Width,Height)
-
-class Circle(draw_object):
- def __init__(self,x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground = 0):
- draw_object.__init__(self,Foreground)
-
- self.X = x
- self.Y = y
- self.Diameter = Diameter
- self.BoundingBox = array(((x-Diameter/2,y-Diameter/2),(x+Diameter/2,y+Diameter/2)),Float)
- self.LineColor = LineColor
- self.LineStyle = LineStyle
- self.LineWidth = LineWidth
- self.FillColor = FillColor
- self.FillStyle = FillStyle
-
- self.SetPen(LineColor,LineStyle,LineWidth)
- self.SetBrush(FillColor,FillStyle)
-
- def _Draw(self,dc,WorldToPixel,ScaleFunction):
- (X,Y) = WorldToPixel((self.X,self.Y))
- (Diameter,dummy) = ScaleFunction((self.Diameter,self.Diameter))
-
- dc.SetPen(self.Pen)
- dc.SetBrush(self.Brush)
- dc.DrawEllipse(X-Diameter/2,Y-Diameter/2, Diameter,Diameter)
-
-class Text(draw_object):
- """
-
- This class creates a text object, placed at the coordinates,
- x,y. the "Position" argument is a two charactor string, indicating
- where in relation to the coordinates the string should be oriented.
-
- The first letter is: t, c, or b, for top, center and bottom
- The second letter is: l, c, or r, for left, center and right
-
- I've provided arguments for Family, Style, and Weight of font, but
- have not yet implimented them, so all text is: wx.SWISS, wx.NORMAL, wx.NORMAL.
- I'd love it if someone would impliment that!
-
- The size is fixed, and does not scale with the drawing.
-
- """
-
- def __init__(self,String,x,y,Size,ForeGround,BackGround,Family,Style,Weight,Underline,Position,Foreground = 0):
- draw_object.__init__(self,Foreground)
-
- self.String = String
- self.Size = Size
-
- self.ForeGround = ForeGround
- if BackGround is None:
- self.BackGround = None
- else:
- self.BackGround = BackGround
- self.Family = Family
- self.Style = Style
- self.Weight = Weight
- self.Underline = Underline
- self.Position = Position
- # fixme: this should use the passed in parameters!
- self.Font = wx.Font(Size, wx.MODERN, wx.NORMAL, wx.NORMAL, Underline, )
-
- self.BoundingBox = array(((x,y),(x,y)),Float)
-
- self.X = x
- self.Y = y
- self.x_shift = None
- self.y_shift = None
-
- def _Draw(self,dc,WorldToPixel,ScaleFunction):
- (X,Y) = WorldToPixel((self.X,self.Y))
- dc.SetFont(self.Font)
- dc.SetTextForeground(self.ForeGround)
- if self.BackGround:
- dc.SetBackgroundMode(wx.SOLID)
- dc.SetTextBackground(self.BackGround)
- else:
- dc.SetBackgroundMode(wx.TRANSPARENT)
-
- # compute the shift, and adjust the coordinates, if neccesary
- # This had to be put in here, becsuse there is no wx.DC during __init__
- if self.x_shift is None or self.y_shift is None:
- if self.Position == 'tl':
- x_shift,y_shift = 0,0
- else:
- (w,h) = dc.GetTextExtent(self.String)
- if self.Position[0] == 't':
- y_shift = 0
- elif self.Position[0] == 'c':
- y_shift = h/2
- elif self.Position[0] == 'b':
- y_shift = h
- else:
- ##fixme: this should be a real derived exception
- raise "Invalid value for Text Object Position Attribute"
- if self.Position[1] == 'l':
- x_shift = 0
- elif self.Position[1] == 'c':
- x_shift = w/2
- elif self.Position[1] == 'r':
- x_shift = w
- else:
- ##fixme: this should be a real derived exception
- raise "Invalid value for Text Object Position Attribute"
- self.x_shift = x_shift
- self.y_shift = y_shift
- dc.DrawText(self.String, X-self.x_shift, Y-self.y_shift)
-
-
-#---------------------------------------------------------------------------
-
-class FloatCanvas(wx.Panel):
- """
- FloatCanvas.py
-
- This is a high level window for drawing maps and anything else in an
- arbitrary coordinate system.
-
- The goal is to provide a convenient way to draw stuff on the screen
- without having to deal with handling OnPaint events, converting to pixel
- coordinates, knowing about wxWindows brushes, pens, and colors, etc. It
- also provides virtually unlimited zooming and scrolling
-
- I am using it for two things:
- 1) general purpose drawing in floating point coordinates
- 2) displaying map data in Lat-long coordinates
-
- If the projection is set to None, it will draw in general purpose
- floating point coordinates. If the projection is set to 'FlatEarth', it
- will draw a FlatEarth projection, centered on the part of the map that
- you are viewing. You can also pass in your own projection function.
-
- 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
-
- Bugs and Limitations:
- Lots: patches, fixes welcome
-
- For Map drawing: It ignores the fact that the world is, in fact, a
- sphere, so it will do strange things if you are looking at stuff near
- the poles or the date line. so far I don't have a need to do that, so I
- havn't bothered to add any checks for that yet.
-
- Zooming:
- I have set no zoom limits. What this means is that if you zoom in really
- far, you can get integer overflows, and get wierd results. It
- doesn't seem to actually cause any problems other than wierd output, at
- least when I have run it.
-
- Speed:
- I have done a couple of things to improve speed in this app. The one
- thing I have done is used NumPy Arrays to store the coordinates of the
- points of the objects. This allowed me to use array oriented functions
- when doing transformations, and should provide some speed improvement
- for objects with a lot of points (big polygons, polylines, pointsets).
-
- The real slowdown comes when you have to draw a lot of objects, because
- you have to call the wx.DC.DrawSomething call each time. This is plenty
- fast for tens of objects, OK for hundreds of objects, but pretty darn
- slow for thousands of objects.
-
- The 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. I havn't got a LineSet type object, so I havn't
- used DC.DrawLineList yet. I'd like to get a full set of DrawStuffList()
- methods implimented, and then I'd also have a full set of Object sets
- that could take advantage of them. I hope to get to it some day.
-
- Copyright: Christopher Barker
-
- License: Same as wxPython
-
- 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,
- ProjectionFun = None,
- BackgroundColor = "WHITE",
- Debug = 0,
- EnclosingFrame = None,
- UseToolbar = 1,
- UseBackground = 0,
- UseHitTest = 0):
-
- wx.Panel.__init__( self, parent, id, wx.DefaultPosition, size)
-
- if ProjectionFun == 'FlatEarth':
- self.ProjectionFun = self.FlatEarthProjection
- elif type(ProjectionFun) == types.FunctionType:
- self.ProjectionFun = ProjectionFun
- elif ProjectionFun is None:
- self.ProjectionFun = lambda x=None: array( (1,1), Float)
- else:
- raise('Projectionfun must be either: "FlatEarth", None, or a function that takes the ViewPortCenter and returns a MapProjectionVector')
-
- self.UseBackground = UseBackground
- self.UseHitTest = UseHitTest
-
- self.NumBetweenBlits = 40
-
- ## you can have a toolbar with buttons for zoom-in, zoom-out and
- ## move. If you don't use the toolbar, you should provide your
- ## own way of navigating the canvas
- if UseToolbar:
- ## 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.DrawPanel = wx.Window(self,-1,wx.DefaultPosition,wx.DefaultSize,wx.SUNKEN_BORDER)
- box.Add(self.DrawPanel,1,wx.GROW)
-
- box.Fit(self)
- self.SetAutoLayout(True)
- self.SetSizer(box)
- else:
- self.DrawPanel = self
-
- self.DrawPanel.BackgroundBrush = wx.Brush(BackgroundColor,wx.SOLID)
-
- self.Debug = Debug
-
- self.EnclosingFrame = EnclosingFrame
-
- wx.EVT_PAINT(self.DrawPanel, self.OnPaint)
- wx.EVT_SIZE(self.DrawPanel, self.OnSize)
-
- wx.EVT_LEFT_DOWN(self.DrawPanel, self.LeftButtonEvent)
- wx.EVT_LEFT_UP(self.DrawPanel, self.LeftButtonEvent)
- wx.EVT_RIGHT_DOWN(self.DrawPanel, self.RightButtonEvent)
- wx.EVT_MOTION(self.DrawPanel, self.LeftButtonEvent)
-
-
- self._DrawList = []
- if self.UseBackground:
- self._TopDrawList = []
- self.BoundingBox = None
- self.BoundingBoxDirty = 0
- self.ViewPortCenter= array( (0,0), Float)
-
- self.MapProjectionVector = array( (1,1), Float) # No Projection to start!
- self.TransformVector = array( (1,-1), Float) # default Transformation
-
- self.Scale = 1
-
- self.GUIMode = None
- self.StartRBBox = None
- self.PrevRBBox = None
- self.StartMove = None
- self.PrevMoveBox = None
- # called just to make sure everything is initialized
- if wx.Platform != "__WXMAC__":
- self.OnSize(None)
-
-
- def BuildToolbar(self):
- tb = wx.ToolBar(self,-1)
- self.ToolBar = tb
-
- tb.SetToolBitmapSize((23,23))
-
- tb.AddTool(ID_ZOOM_IN_BUTTON, GetPlusBitmap(), isToggle=True,shortHelpString = "Zoom In")
- wx.EVT_TOOL(self, ID_ZOOM_IN_BUTTON, self.SetMode)
-
- tb.AddTool(ID_ZOOM_OUT_BUTTON, GetMinusBitmap(), isToggle=True,shortHelpString = "Zoom Out")
- wx.EVT_TOOL(self, ID_ZOOM_OUT_BUTTON, self.SetMode)
-
- tb.AddTool(ID_MOVE_MODE_BUTTON, GetHandBitmap(), isToggle=True,shortHelpString = "Move")
- wx.EVT_TOOL(self, ID_MOVE_MODE_BUTTON, self.SetMode)
-
- 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)
-
- tb.Realize()
- return tb
-
- def SetMode(self,event):
- for id in [ID_ZOOM_IN_BUTTON,ID_ZOOM_OUT_BUTTON,ID_MOVE_MODE_BUTTON]:
- self.ToolBar.ToggleTool(id,0)
- self.ToolBar.ToggleTool(event.GetId(),1)
- if event.GetId() == ID_ZOOM_IN_BUTTON:
- self.SetGUIMode("ZoomIn")
- elif event.GetId() == ID_ZOOM_OUT_BUTTON:
- self.SetGUIMode("ZoomOut")
- elif event.GetId() == ID_MOVE_MODE_BUTTON:
- self.SetGUIMode("Move")
-
-
- def SetGUIMode(self,Mode):
- if Mode in ["ZoomIn","ZoomOut","Move",None]:
- self.GUIMode = Mode
- else:
- raise "Not a valid Mode"
-
- def FlatEarthProjection(self,CenterPoint):
- return array((cos(pi*CenterPoint[1]/180),1),Float)
-
- def LeftButtonEvent(self,event):
- if self.EnclosingFrame:
- if event.Moving:
- position = self.PixelToWorld((event.GetX(),event.GetY()))
- self.EnclosingFrame.SetStatusText("%8.3f, %8.3f"%tuple(position))
- if self.GUIMode:
- if self.GUIMode == "ZoomIn":
- if event.LeftDown():
- self.StartRBBox = (event.GetX(),event.GetY())
- self.PrevRBBox = None
- elif event.Dragging() and event.LeftIsDown() and self.StartRBBox:
- x0,y0 = self.StartRBBox
- x1,y1 = event.GetX(),event.GetY()
- w, h = abs(x1-x0),abs(y1-y0)
- w = max(w,int(h*self.AspectRatio))
- h = int(w/self.AspectRatio)
- x_c, y_c = (x0+x1)/2 , (y0+y1)/2
- dc = wx.ClientDC(self.DrawPanel)
- dc.BeginDrawing()
- 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)
- dc.DrawRectangle(x_c-w/2,y_c-h/2,w,h)
- self.PrevRBBox = (x_c-w/2,y_c-h/2,w,h)
- dc.EndDrawing()
-
- elif event.LeftUp() and self.StartRBBox :
- self.PrevRBBox = None
- EndRBBox = (event.GetX(),event.GetY())
- 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.PixelToWorld(EndRBBox)
- StartRBBox = self.PixelToWorld(StartRBBox)
- BB = array(((min(EndRBBox[0],StartRBBox[0]), min(EndRBBox[1],StartRBBox[1])),
- (max(EndRBBox[0],StartRBBox[0]), max(EndRBBox[1],StartRBBox[1]))),Float)
- self.ZoomToBB(BB)
- else:
- Center = self.PixelToWorld(StartRBBox)
- self.Zoom(1.5,Center)
- self.StartRBBox = None
-
- if self.GUIMode == "ZoomOut":
- if event.LeftDown():
- Center = self.PixelToWorld((event.GetX(),event.GetY()))
- self.Zoom(1/1.5,Center)
- elif self.GUIMode == "Move":
- if event.LeftDown():
- self.StartMove = array((event.GetX(),event.GetY()))
- self.PrevMoveBox = None
- elif event.Dragging() and event.LeftIsDown() and self.StartMove:
- x_1,y_1 = event.GetX(),event.GetY()
- w, h = self.PanelSize
- x_tl, y_tl = x_1 - self.StartMove[0], y_1 - self.StartMove[1]
- dc = wx.ClientDC(self.DrawPanel)
- dc.BeginDrawing()
- dc.SetPen(wx.Pen('WHITE', 1,))
- dc.SetBrush(wx.TRANSPARENT_BRUSH)
- dc.SetLogicalFunction(wx.XOR)
- if self.PrevMoveBox:
- dc.DrawRectangle(*self.PrevMoveBox)
- dc.DrawRectangle(x_tl,y_tl,w,h)
- self.PrevMoveBox = (x_tl,y_tl,w,h)
- dc.EndDrawing()
-
- elif event.LeftUp() and self.StartMove:
- self.PrevMoveBox = None
- StartMove = self.StartMove
- EndMove = array((event.GetX(),event.GetY()))
- if sum((StartMove-EndMove)**2) > 16:
- self.Move(StartMove-EndMove,'Pixel')
- self.StartMove = None
-
- def RightButtonEvent(self,event):
- if self.GUIMode:
- if self.GUIMode == "ZoomIn":
- Center = self.PixelToWorld((event.GetX(),event.GetY()))
- self.Zoom(1/1.5,Center)
- elif self.GUIMode == "ZoomOut":
- Center = self.PixelToWorld((event.GetX(),event.GetY()))
- self.Zoom(1.5,Center)
- else:
- event.Skip()
- event.Skip()
-
- def MakeNewBuffers(self):
- # Make new offscreen bitmap:
- self._Buffer = wx.EmptyBitmap(self.PanelSize[0],self.PanelSize[1])
- if self.UseBackground:
- self._BackBuffer = wx.EmptyBitmap(self.PanelSize[0],self.PanelSize[1])
- self._BackgroundDirty = 1
- else:
- pass
-
- def OnSize(self,event):
- self.PanelSize = array(self.DrawPanel.GetClientSizeTuple(),Int32)
- try:
- self.AspectRatio = self.PanelSize[0]/self.PanelSize[1]
- except ZeroDivisionError:
- self.AspectRatio = 1.0
- self.MakeNewBuffers()
- self.Draw()
-
- def OnPaint(self, event):
- #dc = wx.BufferedPaintDC(self.DrawPanel, self._Buffer)
- dc = wx.PaintDC(self.DrawPanel)
- dc.DrawBitmap(self._Buffer, 0,0)
-
- def Draw(self):
- """
- The Draw method gets pretty complicated because of all the buffers
-
- There is a main buffer set up to double buffer the screen, so
- you can get quick re-draws when the window gets uncovered.
-
- If self.UseBackground is set, and an object is set up with the
- "ForeGround" flag, then it gets drawn to the screen after blitting
- the background. This is done so that you can have a complicated
- background, but have something changing on the foreground,
- without having to wait for the background to get re-drawn. This
- can be used to support simple animation, for instance.
-
- """
- if self.Debug: start = clock()
- ScreenDC = wx.ClientDC(self.DrawPanel)
- ViewPortWorld = ( self.PixelToWorld((0,0)), self.PixelToWorld(self.PanelSize) )
- ViewPortBB = array( ( minimum.reduce(ViewPortWorld), maximum.reduce(ViewPortWorld) ) )
- if self.UseBackground:
- dc = wx.MemoryDC()
- dc.SelectObject(self._BackBuffer)
- dc.SetBackground(self.DrawPanel.BackgroundBrush)
- if self._DrawList:
- if self._BackgroundDirty:
- dc.BeginDrawing()
- dc.Clear()
- i = 0
- for Object in self._DrawList:
- if self.BBCheck(Object.BoundingBox,ViewPortBB):
- #print "object is in Bounding Box"
- i+=1
- Object._Draw(dc,self.WorldToPixel,self.ScaleFunction)
- if i % self.NumBetweenBlits == 0:
- w,h = self.PanelSize
- ScreenDC.Blit(0, 0, w,h, dc, 0, 0)
- dc.EndDrawing()
- else:
- dc.Clear()
- self._BackgroundDirty = 0
- dc.SelectObject(self._Buffer)
- dc.BeginDrawing()
- ##Draw Background on Main Buffer:
- dc.DrawBitmap(self._BackBuffer,0,0)
- #Draw the OnTop stuff
- i = 0
- for Object in self._TopDrawList:
- i+=1
- Object._Draw(dc,self.WorldToPixel,self.ScaleFunction)
- if i % self.NumBetweenBlits == 0:
- w, h = self.PanelSize
- ScreenDC.Blit(0, 0, w,h, dc, 0, 0)
- dc.EndDrawing()
- else: # not using a Background DC
- dc = wx.MemoryDC()
- dc.SelectObject(self._Buffer)
- dc.SetBackground(self.DrawPanel.BackgroundBrush)
- if self._DrawList:
- dc.BeginDrawing()
- dc.Clear()
- i = 0
- for Object in self._DrawList:
- if self.BBCheck(Object.BoundingBox,ViewPortBB):
- #print "object is in Bounding Box"
- i+=1
- Object._Draw(dc,self.WorldToPixel,self.ScaleFunction)
- if i % self.NumBetweenBlits == 0:
- w, h = self.PanelSize
- ScreenDC.Blit(0, 0, w, h, dc, 0, 0)
- dc.EndDrawing()
- else:
- dc.Clear()
- # now refresh the screen
- #ScreenDC.DrawBitmap(self._Buffer,0,0) #NOTE: uisng DrawBitmap didn't work right on MSW
- w, h = self.PanelSize
- ScreenDC.Blit(0, 0, w, h, dc, 0, 0)
-
- # If the canvas is in the middle of a zoom or move, the Rubber Band box needs to be re-drawn
- if self.PrevRBBox:
- ScreenDC.SetPen(wx.Pen('WHITE', 2,wx.SHORT_DASH))
- ScreenDC.SetBrush(wx.TRANSPARENT_BRUSH)
- ScreenDC.SetLogicalFunction(wx.XOR)
- ScreenDC.DrawRectangle(*self.PrevRBBox)
- elif self.PrevMoveBox:
- ScreenDC.SetPen(wx.Pen('WHITE', 1,))
- ScreenDC.SetBrush(wx.TRANSPARENT_BRUSH)
- ScreenDC.SetLogicalFunction(wx.XOR)
- ScreenDC.DrawRectangle(*self.PrevMoveBox)
- if self.Debug: print "Drawing took %f seconds of CPU time"%(clock()-start)
-
- def BBCheck(self, BB1, BB2):
- """
-
- BBCheck(BB1, BB2) returns True is the Bounding boxes intesect, False otherwise
-
- """
- if ( (BB1[1,0] > BB2[0,0]) and (BB1[0,0] < BB2[1,0]) and
- (BB1[1,1] > BB2[0,1]) and (BB1[0,1] < BB2[1,1]) ):
- return True
- else:
- return False
-
- def Move(self,shift,CoordType):
- """
- move the image in the window.
-
- shift is an (x,y) tuple, specifying the amount to shift in each direction
-
- It can be in any of three coordinates: Panel, Pixel, World,
- specified by the CoordType parameter
-
- Panel coordinates means you want to shift the image by some
- fraction of the size of the displaed image
-
- Pixel coordinates means you want to shift the image by some number of pixels
-
- World coordinates meand you want to shift the image by an amount
- in Floating point world coordinates
-
- """
-
- shift = array(shift,Float)
- if CoordType == 'Panel':# convert from panel coordinates
- shift = shift * array((-1,1),Float) *self.PanelSize/self.TransformVector
- elif CoordType == 'Pixel': # convert from pixel coordinates
- shift = shift/self.TransformVector
- elif CoordType == 'World': # No conversion
- pass
- else:
- raise 'CoordType must be either "Panel", "Pixel", or "World"'
-
- self.ViewPortCenter = self.ViewPortCenter + shift
- self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
- self.TransformVector = array((self.Scale,-self.Scale),Float)* self.MapProjectionVector
- self._BackgroundDirty = 1
- self.Draw()
-
- def Zoom(self,factor,center = None):
-
- """
- Zoom(factor, center) changes the amount of zoom of the image by factor.
- If factor is greater than one, the image gets larger.
- If factor is less than one, the image gets smaller.
-
- Center is a tuple of (x,y) coordinates of the center of the viewport, after zooming.
- If center is not given, the center will stay the same.
-
- """
- self.Scale = self.Scale*factor
- if center:
- self.ViewPortCenter = array(center,Float)
- self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
- self.TransformVector = array((self.Scale,-self.Scale),Float)* self.MapProjectionVector
- self._BackgroundDirty = 1
- self.Draw()
-
- def ZoomToFit(self,event):
- self.ZoomToBB()
-
- def ZoomToBB(self,NewBB = None,DrawFlag = 1):
-
- """
-
- Zooms the image to the bounding box given, or to the bounding
- box of all the objects on the canvas, if none is given.
-
- """
-
- if NewBB:
- BoundingBox = NewBB
- else:
- if self.BoundingBoxDirty:
- self._ResetBoundingBox()
- BoundingBox = self.BoundingBox
- if BoundingBox:
- self.ViewPortCenter = array(((BoundingBox[0,0]+BoundingBox[1,0])/2,
- (BoundingBox[0,1]+BoundingBox[1,1])/2 ),Float)
- self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
- # Compute the new Scale
- BoundingBox = BoundingBox * self.MapProjectionVector
- try:
- self.Scale = min((self.PanelSize[0] / (BoundingBox[1,0]-BoundingBox[0,0])),
- (self.PanelSize[1] / (BoundingBox[1,1]-BoundingBox[0,1])))*0.95
- except ZeroDivisionError: # this will happen if the BB has zero width or height
- try: #width
- self.Scale = (self.PanelSize[0] / (BoundingBox[1,0]-BoundingBox[0,0]))*0.95
- except ZeroDivisionError:
- try: # height
- self.Scale = (self.PanelSize[1] / (BoundingBox[1,1]-BoundingBox[0,1]))*0.95
- except ZeroDivisionError: #zero size! (must be a single point)
- self.Scale = 1
-
- self.TransformVector = array((self.Scale,-self.Scale),Float)* self.MapProjectionVector
- if DrawFlag:
- self._BackgroundDirty = 1
- self.Draw()
-
- def RemoveObjects(self,Objects):
- for Object in Objects:
- self.RemoveObject(Object,ResetBB = 0)
- self.BoundingBoxDirty = 1
-
- def RemoveObject(self,Object,ResetBB = 1):
- if Object.Foreground:
- self._TopDrawList.remove(Object)
- else:
- self._DrawList.remove(Object)
- self._BackgroundDirty = 1
-
- if ResetBB:
- self.BoundingBoxDirty = 1
-
- def Clear(self, ResetBB = True):
- self._DrawList = []
- if self.UseBackground:
- self._TopDrawList = []
- self._BackgroundDirty = 1
- if ResetBB:
- self._ResetBoundingBox()
-
- def _AddBoundingBox(self,NewBB):
- if self.BoundingBox is None:
- self.BoundingBox = NewBB
- self.ZoomToBB(NewBB,DrawFlag = 0)
- else:
- self.BoundingBox = array(((min(self.BoundingBox[0,0],NewBB[0,0]),
- min(self.BoundingBox[0,1],NewBB[0,1])),
- (max(self.BoundingBox[1,0],NewBB[1,0]),
- max(self.BoundingBox[1,1],NewBB[1,1]))),Float)
- def _ResetBoundingBox(self):
- # NOTE: could you remove an item without recomputing the entire bounding box?
- self.BoundingBox = None
- if self._DrawList:
- self.BoundingBox = self._DrawList[0].BoundingBox
- for Object in self._DrawList[1:]:
- self._AddBoundingBox(Object.BoundingBox)
- if self.UseBackground:
- for Object in self._TopDrawList:
- self._AddBoundingBox(Object.BoundingBox)
- if self.BoundingBox is None:
- self.ViewPortCenter= array( (0,0), Float)
- self.TransformVector = array( (1,-1), Float)
- self.MapProjectionVector = array( (1,1), Float)
- self.Scale = 1
- self.BoundingBoxDirty = 0
-
- def PixelToWorld(self,Points):
- """
- Converts coordinates from Pixel coordinates to world coordinates.
-
- Points is a tuple of (x,y) coordinates, or a list of such tuples, or a NX2 Numpy array of x,y coordinates.
-
- """
- return (((array(Points,Float) - (self.PanelSize/2))/self.TransformVector) + self.ViewPortCenter)
-
- def WorldToPixel(self,Coordinates):
- """
- This function will get passed to the drawing functions of the objects,
- to transform from world to pixel coordinates.
- Coordinates should be a NX2 array of (x,y) coordinates, or
- a 2-tuple, or sequence of 2-tuples.
- """
- return (((array(Coordinates,Float) - self.ViewPortCenter)*self.TransformVector)+(self.PanelSize/2)).astype('i')
-
- def ScaleFunction(self,Lengths):
- """
- This function will get passed to the drawing functions of the objects,
- to Change a length from world to pixel coordinates.
-
- Lengths should be a NX2 array of (x,y) coordinates, or
- a 2-tuple, or sequence of 2-tuples.
- """
- return (array(Lengths,Float)*self.TransformVector).astype('i')
-
-
- ## This is a set of methods that add objects to the Canvas. It kind
- ## of seems like a lot of duplication, but I wanted to be able to
- ## instantiate the draw objects separatley form adding them, but
- ## also to be able to do add one oin one step. I'm open to better
- ## ideas...
- def AddRectangle(self,x,y,width,height,
- LineColor = "Black",
- LineStyle = "Solid",
- LineWidth = 1,
- FillColor = None,
- FillStyle = "Solid",
- Foreground = 0):
- Object = Rectangle(x,y,width,height,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground)
- self.AddObject(Object)
- return Object
-
- def AddEllipse(self,x,y,width,height,
- LineColor = "Black",
- LineStyle = "Solid",
- LineWidth = 1,
- FillColor = None,
- FillStyle = "Solid",
- Foreground = 0):
- Object = Ellipse(x,y,width,height,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground)
- self.AddObject(Object)
- return Object
-
- def AddCircle(self,x,y,Diameter,
- LineColor = "Black",
- LineStyle = "Solid",
- LineWidth = 1,
- FillColor = None,
- FillStyle = "Solid",
- Foreground = 0):
- Object = Circle(x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground)
- self.AddObject(Object)
- return Object
-
- def AddDot(self,x,y,Diameter,
- LineColor = "Black",
- LineStyle = "Solid",
- LineWidth = 1,
- FillColor = None,
- FillStyle = "Solid",
- Foreground = 0):
- Object = Dot(x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground)
- self.AddObject(Object)
- return Object
-
- def AddPolygon(self,Points,
- LineColor = "Black",
- LineStyle = "Solid",
- LineWidth = 1,
- FillColor = None,
- FillStyle = "Solid",
- Foreground = 0):
-
- Object = Polygon(Points,LineColor,LineStyle,LineWidth,FillColor,FillStyle,Foreground)
- self.AddObject(Object)
- return Object
-
- def AddLine(self,Points,
- LineColor = "Black",
- LineStyle = "Solid",
- LineWidth = 1,
- Foreground = 0):
-
- Object = Line(Points,LineColor,LineStyle,LineWidth,Foreground)
- self.AddObject(Object)
- return Object
-
- def AddLineSet(self,Points,
- LineColors = "Black",
- LineStyles = "Solid",
- LineWidths = 1,
- Foreground = 0):
-
- Object = LineSet(Points,LineColors,LineStyles,LineWidths,Foreground)
- self.AddObject(Object)
- return Object
-
- def AddPointSet(self,Points,
- Color = "Black",
- Diameter = 1,
- Foreground = 0):
-
- Object = PointSet(Points,Color,Diameter,Foreground)
- self.AddObject(Object)
- return Object
-
- def AddText(self,String,x,y,
- Size = 20,
- ForeGround = 'Black',
- BackGround = None,
- Family = 'Swiss',
- Style = 'Normal',
- Weight = 'Normal',
- Underline = 0,
- Position = 'tl',
- Foreground = 0):
- Object = Text(String,x,y,Size,ForeGround,BackGround,Family,Style,Weight,Underline,Position,Foreground)
- self.AddObject(Object)
- return Object
-
- def AddObject(self,obj):
- # put in a reference to the Canvas, so remove and other stuff can work
- obj._Canvas = self
- if obj.Foreground and self.UseBackground:
- self._TopDrawList.append(obj)
- else:
- self._DrawList.append(obj)
- self._backgrounddirty = 1
- self._AddBoundingBox(obj.BoundingBox)
- return None
-
-
-
-
-
-
-
--- /dev/null
+
+try:
+ from Numeric import array,asarray,Float,cos,pi,sum,minimum,maximum,Int32,zeros
+except ImportError:
+ from numarray import array, asarray, Float, cos, pi, sum, minimum, maximum, Int32, zeros
+
+from time import clock, sleep
+
+import wx
+
+import types
+import os
+
+import Resources
+
+## A global variable to hold the Pixels per inch that wxWindows thinks is in use
+## This is used for scaling fonts.
+## This can't be computed on module __init__, because a wx.App might not have iniitalized yet.
+global ScreenPPI
+
+## a custom Exceptions:
+
+class FloatCanvasException(Exception):
+ pass
+
+## All the mouse events
+#EVT_FC_ENTER_WINDOW = wx.NewEventType()
+#EVT_FC_LEAVE_WINDOW = wx.NewEventType()
+EVT_FC_LEFT_DOWN = wx.NewEventType()
+EVT_FC_LEFT_UP = wx.NewEventType()
+EVT_FC_LEFT_DCLICK = wx.NewEventType()
+EVT_FC_MIDDLE_DOWN = wx.NewEventType()
+EVT_FC_MIDDLE_UP = wx.NewEventType()
+EVT_FC_MIDDLE_DCLICK = wx.NewEventType()
+EVT_FC_RIGHT_DOWN = wx.NewEventType()
+EVT_FC_RIGHT_UP = wx.NewEventType()
+EVT_FC_RIGHT_DCLICK = wx.NewEventType()
+EVT_FC_MOTION = wx.NewEventType()
+EVT_FC_MOUSEWHEEL = wx.NewEventType()
+## these two are for the hit-test stuff, I never make them real Events
+EVT_FC_ENTER_OBJECT = wx.NewEventType()
+EVT_FC_LEAVE_OBJECT = wx.NewEventType()
+
+#def EVT_ENTER_WINDOW( window, function ):
+# window.Connect( -1, -1, EVT_FC_ENTER_WINDOW, function )
+#def EVT_LEAVE_WINDOW( window, function ):
+# window.Connect( -1, -1,EVT_FC_LEAVE_WINDOW , function )
+def EVT_LEFT_DOWN( window, function ):
+ window.Connect( -1, -1,EVT_FC_LEFT_DOWN , function )
+def EVT_LEFT_UP( window, function ):
+ window.Connect( -1, -1,EVT_FC_LEFT_UP , function )
+def EVT_LEFT_DCLICK ( window, function ):
+ window.Connect( -1, -1,EVT_FC_LEFT_DCLICK , function )
+def EVT_MIDDLE_DOWN ( window, function ):
+ window.Connect( -1, -1,EVT_FC_MIDDLE_DOWN , function )
+def EVT_MIDDLE_UP ( window, function ):
+ window.Connect( -1, -1,EVT_FC_MIDDLE_UP , function )
+def EVT_MIDDLE_DCLICK ( window, function ):
+ window.Connect( -1, -1,EVT_FC_MIDDLE_DCLICK , function )
+def EVT_RIGHT_DOWN ( window, function ):
+ window.Connect( -1, -1,EVT_FC_RIGHT_DOWN , function )
+def EVT_RIGHT_UP( window, function ):
+ window.Connect( -1, -1,EVT_FC_RIGHT_UP , function )
+def EVT_RIGHT_DCLICK( window, function ):
+ window.Connect( -1, -1,EVT_FC_RIGHT_DCLICK , function )
+def EVT_MOTION( window, function ):
+ window.Connect( -1, -1,EVT_FC_MOTION , function )
+def EVT_MOUSEWHEEL( window, function ):
+ window.Connect( -1, -1,EVT_FC_MOUSEWHEEL , function )
+
+class MouseEvent(wx.PyCommandEvent):
+
+ """
+
+ This event class takes a regular wxWindows mouse event as a parameter,
+ and wraps it so that there is access to all the original methods. This
+ is similar to subclassing, but you can't subclass a wxWindows event
+
+ The goal is to be able to it just like a regular mouse event.
+
+ It adds the method:
+
+ GetCoords() , which returns and (x,y) tuple in world coordinates.
+
+ Another differnce is that it is a CommandEvent, which propagates up
+ the window hierarchy until it is handled.
+
+ """
+
+ def __init__(self, EventType, NativeEvent, WinID, Coords = None):
+ wx.PyCommandEvent.__init__(self)
+
+ self.SetEventType( EventType )
+ self._NativeEvent = NativeEvent
+ self.Coords = Coords
+
+ def SetCoords(self,Coords):
+ self.Coords = Coords
+
+ def GetCoords(self):
+ return self.Coords
+
+ def __getattr__(self, name):
+ #return eval(self.NativeEvent.__getattr__(name) )
+ return getattr(self._NativeEvent, name)
+
+#### ColorGEnerator class is now obsolete. I'm using a python generator function instead.
+##class ColorGenerator:
+
+## """
+
+## An instance of this class generates a unique color each time
+## GetNextColor() is called. Someday I will use a proper Python
+## generator for this class.
+
+## The point of this generator is for the hit-test bitmap, each object
+## needs to be a unique color. Also, each system can be running a
+## different number of colors, and it doesn't appear to be possible to
+## have a wxMemDC with a different colordepth as the screen so this
+## generates colors far enough apart that they can be distinguished on
+## a 16bit screen. Anything less than 16bits won't work. It could, but
+## I havn't written the code that way. You also wouldn't get many
+## distict colors
+
+## """
+
+## def __init__(self):
+## import sys
+## ## figure out the color depth of the screen
+## ## for some bizare reason, thisdoesn't work on OS-X
+## if sys.platform == 'darwin':
+## depth = 24
+## else:
+## b = wx.EmptyBitmap(1,1)
+## depth = b.GetDepth()
+## self.r = 0
+## self.g = 0
+## self.b = 0
+## if depth == 16:
+## self.step = 8
+## elif depth >= 24:
+## self.step = 1
+## else:
+## raise FloatCanvasException("ColorGenerator does not work with depth = %s"%depth )
+
+## def GetNextColor(self):
+## step = self.step
+## ##r,g,b = self.r,self.g,self.b
+## self.r += step
+## if self.r > 255:
+## self.r = step
+## self.g += step
+## if self.g > 255:
+## self.g = step
+## self.b += step
+## if self.b > 255:
+## ## fixme: this should be a derived exception
+## raise FloatCanvasException("Too many objects in colorgenerator for HitTest")
+## return (self.r,self.g,self.b)
+
+## def Reset(self):
+## self.r = 0
+## self.g = 0
+## self.b = 0
+
+def cycleidxs(indexcount, maxvalue, step):
+ if indexcount == 0:
+ yield ()
+ else:
+ for idx in xrange(0, maxvalue, step):
+ for tail in cycleidxs(indexcount - 1, maxvalue, step):
+ yield (idx, ) + tail
+
+def colorGenerator():
+ import sys
+ if sys.platform == 'darwin':
+ depth = 24
+ else:
+ b = wx.EmptyBitmap(1,1)
+ depth = b.GetDepth()
+ if depth == 16:
+ step = 8
+ elif depth >= 24:
+ step = 1
+ else:
+ raise "ColorGenerator does not work with depth = %s" % depth
+ return cycleidxs(indexcount=3, maxvalue=256, step=step)
+
+
+#### I don't know if the Set objects are useful, beyond the pointset object
+#### The problem is that when zoomed in, the BB is checked to see whether to draw the object.
+#### A Set object can defeat this
+
+##class ObjectSetMixin:
+## """
+## A mix-in class for draw objects that are sets of objects
+
+## It contains methods for setting lists of pens and brushes
+
+## """
+## def SetPens(self,LineColors,LineStyles,LineWidths):
+## """
+## This method used when an object could have a list of pens, rather than just one
+## It is used for LineSet, and perhaps others in the future.
+
+## fixme: this should be in a mixin
+
+## fixme: this is really kludgy, there has got to be a better way!
+
+## """
+
+## length = 1
+## if type(LineColors) == types.ListType:
+## length = len(LineColors)
+## else:
+## LineColors = [LineColors]
+
+## if type(LineStyles) == types.ListType:
+## length = len(LineStyles)
+## else:
+## LineStyles = [LineStyles]
+
+## if type(LineWidths) == types.ListType:
+## length = len(LineWidths)
+## else:
+## LineWidths = [LineWidths]
+
+## if length > 1:
+## if len(LineColors) == 1:
+## LineColors = LineColors*length
+## if len(LineStyles) == 1:
+## LineStyles = LineStyles*length
+## if len(LineWidths) == 1:
+## LineWidths = LineWidths*length
+
+## self.Pens = []
+## for (LineColor,LineStyle,LineWidth) in zip(LineColors,LineStyles,LineWidths):
+## if LineColor is None or LineStyle is None:
+## self.Pens.append(wx.TRANSPARENT_PEN)
+## # what's this for?> self.LineStyle = 'Transparent'
+## if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
+## Pen = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])
+## self.Pens.append(Pen)
+## else:
+## self.Pens.append(self.PenList[(LineColor,LineStyle,LineWidth)])
+## if length == 1:
+## self.Pens = self.Pens[0]
+
+
+
+class DrawObject:
+ """
+ This is the base class for all the objects that can be drawn.
+
+ """
+
+ def __init__(self,InForeground = False):
+ self.InForeground = InForeground
+
+ self._Canvas = None
+
+ self.HitColor = None
+ self.CallBackFuncs = {}
+
+ ## these are the defaults
+ self.HitAble = False
+ self.HitLine = True
+ self.HitFill = True
+ self.MinHitLineWidth = 3
+ self.HitLineWidth = 3 ## this gets re-set by the subclasses if necessary
+
+ # I pre-define all these as class variables to provide an easier
+ # interface, and perhaps speed things up by caching all the Pens
+ # and Brushes, although that may not help, as I think wx now
+ # does that on it's own. Send me a note if you know!
+
+ BrushList = {
+ ( None,"Transparent") : wx.TRANSPARENT_BRUSH,
+ ("Blue","Solid") : wx.BLUE_BRUSH,
+ ("Green","Solid") : wx.GREEN_BRUSH,
+ ("White","Solid") : wx.WHITE_BRUSH,
+ ("Black","Solid") : wx.BLACK_BRUSH,
+ ("Grey","Solid") : wx.GREY_BRUSH,
+ ("MediumGrey","Solid") : wx.MEDIUM_GREY_BRUSH,
+ ("LightGrey","Solid") : wx.LIGHT_GREY_BRUSH,
+ ("Cyan","Solid") : wx.CYAN_BRUSH,
+ ("Red","Solid") : wx.RED_BRUSH
+ }
+ PenList = {
+ (None,"Transparent",1) : wx.TRANSPARENT_PEN,
+ ("Green","Solid",1) : wx.GREEN_PEN,
+ ("White","Solid",1) : wx.WHITE_PEN,
+ ("Black","Solid",1) : wx.BLACK_PEN,
+ ("Grey","Solid",1) : wx.GREY_PEN,
+ ("MediumGrey","Solid",1) : wx.MEDIUM_GREY_PEN,
+ ("LightGrey","Solid",1) : wx.LIGHT_GREY_PEN,
+ ("Cyan","Solid",1) : wx.CYAN_PEN,
+ ("Red","Solid",1) : wx.RED_PEN
+ }
+
+ FillStyleList = {
+ "Transparent" : wx.TRANSPARENT,
+ "Solid" : wx.SOLID,
+ "BiDiagonalHatch": wx.BDIAGONAL_HATCH,
+ "CrossDiagHatch" : wx.CROSSDIAG_HATCH,
+ "FDiagonal_Hatch": wx.FDIAGONAL_HATCH,
+ "CrossHatch" : wx.CROSS_HATCH,
+ "HorizontalHatch": wx.HORIZONTAL_HATCH,
+ "VerticalHatch" : wx.VERTICAL_HATCH
+ }
+
+ LineStyleList = {
+ "Solid" : wx.SOLID,
+ "Transparent": wx.TRANSPARENT,
+ "Dot" : wx.DOT,
+ "LongDash" : wx.LONG_DASH,
+ "ShortDash" : wx.SHORT_DASH,
+ "DotDash" : wx.DOT_DASH,
+ }
+
+ def Bind(self, Event, CallBackFun):
+ self.CallBackFuncs[Event] = CallBackFun
+ self.HitAble = True
+ self._Canvas.UseHitTest = True
+ if not self._Canvas._HTdc:
+ self._Canvas.MakeNewHTdc()
+ if not self.HitColor:
+ if not self._Canvas.HitColorGenerator:
+ self._Canvas.HitColorGenerator = colorGenerator()
+ self._Canvas.HitColorGenerator.next() # first call to prevent the background color from being used.
+ self.HitColor = self._Canvas.HitColorGenerator.next()
+ self.SetHitPen(self.HitColor,self.HitLineWidth)
+ self.SetHitBrush(self.HitColor)
+ # put the object in the hit dict, indexed by it's color
+ if not self._Canvas.HitDict:
+ self._Canvas.MakeHitDict()
+ self._Canvas.HitDict[Event][self.HitColor] = (self) # put the object in the hit dict, indexed by it's color
+
+
+ def UnBindAll(self):
+ ## fixme: this only removes one from each list, there could be more.
+ if self._Canvas.HitDict:
+ for List in self._Canvas.HitDict.itervalues():
+ try:
+ List.remove(self)
+ except ValueError:
+ pass
+ self.HitAble = False
+
+ def SetBrush(self,FillColor,FillStyle):
+ if FillColor is None or FillStyle is None:
+ self.Brush = wx.TRANSPARENT_BRUSH
+ self.FillStyle = "Transparent"
+ else:
+ self.Brush = self.BrushList.setdefault( (FillColor,FillStyle), wx.Brush(FillColor,self.FillStyleList[FillStyle] ) )
+
+ def SetPen(self,LineColor,LineStyle,LineWidth):
+ if (LineColor is None) or (LineStyle is None):
+ self.Pen = wx.TRANSPARENT_PEN
+ self.LineStyle = 'Transparent'
+ else:
+ self.Pen = self.PenList.setdefault( (LineColor,LineStyle,LineWidth), wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle]) )
+
+ def SetHitBrush(self,HitColor):
+ if not self.HitFill:
+ self.HitBrush = wx.TRANSPARENT_BRUSH
+ else:
+ self.HitBrush = self.BrushList.setdefault( (HitColor,"solid"), wx.Brush(HitColor,self.FillStyleList["Solid"] ) )
+
+ def SetHitPen(self,HitColor,LineWidth):
+ if not self.HitLine:
+ self.HitPen = wx.TRANSPARENT_PEN
+ else:
+ self.HitPen = self.PenList.setdefault( (HitColor, "solid", LineWidth), wx.Pen(HitColor, LineWidth, self.LineStyleList["Solid"]) )
+
+ def PutInBackground(self):
+ if self._Canvas and self.InForeground:
+ self._Canvas._ForeDrawList.remove(self)
+ self._Canvas._DrawList.append(self)
+ self._Canvas._BackgroundDirty = True
+ self.InForeground = False
+
+ def PutInForeground(self):
+ if self._Canvas and (not self.InForeground):
+ self._Canvas._ForeDrawList.append(self)
+ self._Canvas._DrawList.remove(self)
+ self._Canvas._BackgroundDirty = True
+ self.InForeground = True
+
+class XYObjectMixin:
+ """
+
+ This is a mixin class that provides some methods suitable for use
+ with objects that have a single (x,y) coordinate pair.
+
+ """
+
+ def Move(self, Delta ):
+ """
+
+ Move(Delta): moves the object by delta, where delta is a
+ (dx,dy) pair. Ideally a Numpy array of shape (2,)
+
+ """
+
+ Delta = asarray(Delta, Float)
+ self.XY += Delta
+ self.BoundingBox = self.BoundingBox + Delta
+ if self._Canvas:
+ self._Canvas.BoundingBoxDirty = True
+
+class PointsObjectMixin:
+ """
+
+ This is a mixin class that provides some methods suitable for use
+ with objects that have a set of (x,y) coordinate pairs.
+
+ """
+
+## This is code for the XYMixin object, it needs to be adapeted and tested.
+## def Move(self, Delta ):
+## """
+
+## Move(Delta): moves the object by delta, where delta is an (dx,
+## dy) pair. Ideally a Numpy array or shape (2,)
+
+## """
+
+## Delta = array(Delta, Float)
+## self.XY += Delta
+## self.BoundingBox = self.BoundingBox + Delta##array((self.XY, (self.XY + self.WH)), Float)
+## if self._Canvas:
+## self._Canvas.BoundingBoxDirty = True
+
+ def SetPoints(self,Points):
+ self.Points = Points
+ self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+ if self._Canvas:
+ self._Canvas.BoundingBoxDirty = True
+
+
+
+class Polygon(DrawObject,PointsObjectMixin):
+
+ """
+
+ The Polygon class takes a list of 2-tuples, or a NX2 NumPy array of
+ point coordinates. so that Points[N][0] is the x-coordinate of
+ point N and Points[N][1] is the y-coordinate or Points[N,0] is the
+ x-coordinate of point N and Points[N,1] is the y-coordinate for
+ arrays.
+
+ """
+ def __init__(self,
+ Points,
+ LineColor = "Black",
+ LineStyle = "Solid",
+ LineWidth = 1,
+ FillColor = None,
+ FillStyle = "Solid",
+ InForeground = False):
+ DrawObject.__init__(self,InForeground)
+ self.Points = array(Points,Float) # this DOES need to make a copy
+ self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+
+ self.LineColor = LineColor
+ self.LineStyle = LineStyle
+ self.LineWidth = LineWidth
+ self.FillColor = FillColor
+ self.FillStyle = FillStyle
+
+ self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
+
+ self.SetPen(LineColor,LineStyle,LineWidth)
+ self.SetBrush(FillColor,FillStyle)
+
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel = None, HTdc=None):
+ Points = WorldToPixel(self.Points)
+ dc.SetPen(self.Pen)
+ dc.SetBrush(self.Brush)
+ dc.DrawPolygon(Points)
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ HTdc.SetBrush(self.HitBrush)
+ HTdc.DrawPolygon(Points)
+
+##class PolygonSet(DrawObject):
+## """
+## The PolygonSet class takes a Geometry.Polygon object.
+## so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
+
+## it creates a set of line segments, from (x1,y1) to (x2,y2)
+
+## """
+
+## def __init__(self,PolySet,LineColors,LineStyles,LineWidths,FillColors,FillStyles,InForeground = False):
+## DrawObject.__init__(self, InForeground)
+
+## ##fixme: there should be some error checking for everything being the right length.
+
+
+## self.Points = array(Points,Float)
+## self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+
+## self.LineColors = LineColors
+## self.LineStyles = LineStyles
+## self.LineWidths = LineWidths
+## self.FillColors = FillColors
+## self.FillStyles = FillStyles
+
+## self.SetPens(LineColors,LineStyles,LineWidths)
+
+## #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):
+## def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+## Points = WorldToPixel(self.Points)
+## Points.shape = (-1,4)
+## dc.DrawLineList(Points,self.Pens)
+
+
+class Line(DrawObject,PointsObjectMixin):
+ """
+ The Line class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
+ so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
+ or Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
+
+ It will draw a straight line if there are two points, and a polyline if there are more than two.
+
+ """
+ def __init__(self,Points,
+ LineColor = "Black",
+ LineStyle = "Solid",
+ LineWidth = 1,
+ InForeground = False):
+ DrawObject.__init__(self, InForeground)
+
+
+ self.Points = array(Points,Float)
+ self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+
+ self.LineColor = LineColor
+ self.LineStyle = LineStyle
+ self.LineWidth = LineWidth
+
+ self.SetPen(LineColor,LineStyle,LineWidth)
+
+ self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
+
+
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+ Points = WorldToPixel(self.Points)
+ dc.SetPen(self.Pen)
+ dc.DrawLines(Points)
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ HTdc.DrawLines(Points)
+
+##class LineSet(DrawObject, ObjectSetMixin):
+## """
+## The LineSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
+## so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
+
+## it creates a set of line segments, from (x1,y1) to (x2,y2)
+
+## """
+
+## def __init__(self,Points,LineColors,LineStyles,LineWidths,InForeground = False):
+## DrawObject.__init__(self, InForeground)
+
+## NumLines = len(Points) / 2
+## ##fixme: there should be some error checking for everything being the right length.
+
+
+## self.Points = array(Points,Float)
+## self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
+
+## self.LineColors = LineColors
+## self.LineStyles = LineStyles
+## self.LineWidths = LineWidths
+
+## self.SetPens(LineColors,LineStyles,LineWidths)
+
+## #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):
+## def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+## Points = WorldToPixel(self.Points)
+## Points.shape = (-1,4)
+## dc.DrawLineList(Points,self.Pens)
+
+class PointSet(DrawObject):
+ """
+ The PointSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
+ so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
+ or Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
+
+ Each point will be drawn the same color and Diameter. The Diameter is in screen points,
+ not world coordinates.
+
+ At this point, the hit-test code does not distingish between the
+ points, you will only know that one of the poins got hit, not which
+ one.
+
+ In the case of points, the HitLineWidth is used as diameter.
+
+ """
+ def __init__(self, Points, Color = "Black", Diameter = 1, InForeground = False):
+ DrawObject.__init__(self,InForeground)
+
+ self.Points = array(Points,Float)
+ self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
+ self.BoundingBox = array(((min(self.Points[:,0]),
+ min(self.Points[:,1])),
+ (max(self.Points[:,0]),
+ max(self.Points[:,1]))),Float)
+
+ self.Color = Color
+ self.Diameter = Diameter
+
+ self.HitLineWidth = self.MinHitLineWidth
+ self.SetPen(Color,"Solid",1)
+ self.SetBrush(Color,"Solid")
+
+ def SetPoints(self,Points):
+ self.Points = array(Points, Float)
+ self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
+ self.BoundingBox = array(((min(self.Points[:,0]),
+ min(self.Points[:,1]) ),
+ (max(self.Points[:,0]),
+ max(self.Points[:,1]) ) ) )
+ if self._Canvas:
+ self._Canvas.BoundingBoxDirty = True
+
+ def DrawD2(self, dc, Points):
+ # A Little optimization for a diameter2 - point
+ dc.DrawPointList(Points)
+ dc.DrawPointList(Points + (1,0))
+ dc.DrawPointList(Points + (0,1))
+ dc.DrawPointList(Points + (1,1))
+
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+ dc.SetPen(self.Pen)
+ Points = WorldToPixel(self.Points)
+ if self.Diameter <= 1:
+ dc.DrawPointList(Points)
+ elif self.Diameter <= 2:
+ self.DrawD2(dc, Points)
+ else:
+ dc.SetBrush(self.Brush)
+ radius = int(round(self.Diameter/2))
+ for xy in Points:
+ dc.DrawEllipsePointSize( (xy - radius), (self.Diameter, self.Diameter) )
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ if self.Diameter <= 1:
+ HTdc.DrawPointList(Points)
+ elif self.Diameter <= 2:
+ self.DrawD2(HTdc, Points)
+ else:
+ HTdc.SetBrush(self.HitBrush)
+ radius = int(round(self.Diameter/2))
+ for xy in Points:
+ HTdc.DrawEllipsePointSize( (xy - radius), (self.Diameter, self.Diameter) )
+
+#### Does anyone need this?
+##class Dot(DrawObject):
+## """
+## The Dot class takes an x.y coordinate pair, and the Diameter of the circle.
+## The Diameter is in pixels, so it won't change with zoom.
+
+## Also Fill and line data
+
+## """
+## def __init__(self,x,y,Diameter,LineColor,LineStyle,LineWidth,FillColor,FillStyle,InForeground = False):
+## DrawObject.__init__(self,InForeground)
+
+## self.X = x
+## self.Y = y
+## self.Diameter = Diameter
+## # NOTE: the bounding box does not include the diameter of the dot, as that is in pixel coords.
+## # If this is a problem, perhaps you should use a circle, instead!
+## self.BoundingBox = array(((x,y),(x,y)),Float)
+
+## self.LineColor = LineColor
+## self.LineStyle = LineStyle
+## self.LineWidth = LineWidth
+## self.FillColor = FillColor
+## self.FillStyle = FillStyle
+
+## self.SetPen(LineColor,LineStyle,LineWidth)
+## self.SetBrush(FillColor,FillStyle)
+
+## def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+## #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):
+## dc.SetPen(self.Pen)
+## dc.SetBrush(self.Brush)
+## radius = int(round(self.Diameter/2))
+## (X,Y) = WorldToPixel((self.X,self.Y))
+## dc.DrawEllipse((X - radius), (Y - radius), self.Diameter, self.Diameter)
+
+class RectEllipse(DrawObject, XYObjectMixin):
+ def __init__(self,x,y,width,height,
+ LineColor = "Black",
+ LineStyle = "Solid",
+ LineWidth = 1,
+ FillColor = None,
+ FillStyle = "Solid",
+ InForeground = False):
+
+ DrawObject.__init__(self,InForeground)
+
+ self.XY = array( (x, y), Float)
+ self.WH = array( (width, height), Float )
+ self.BoundingBox = array(((x,y), (self.XY + self.WH)), Float)
+ self.LineColor = LineColor
+ self.LineStyle = LineStyle
+ self.LineWidth = LineWidth
+ self.FillColor = FillColor
+ self.FillStyle = FillStyle
+
+ self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
+
+ self.SetPen(LineColor,LineStyle,LineWidth)
+ self.SetBrush(FillColor,FillStyle)
+
+
+ def SetUpDraw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
+ dc.SetPen(self.Pen)
+ dc.SetBrush(self.Brush)
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ HTdc.SetBrush(self.HitBrush)
+ return ( WorldToPixel(self.XY),
+ ScaleWorldToPixel(self.WH) )
+
+ def SetXY(self, x, y):
+ self.XY = array( (x, y), Float)
+ self.BoundingBox = array((self.XY, (self.XY + self.WH) ), Float)
+ if self._Canvas:
+ self._Canvas.BoundingBoxDirty = True
+
+
+class Rectangle(RectEllipse):
+# def __init__(*args, **kwargs):
+# RectEllipse.__init__(*args, **kwargs)
+# raise "an error"
+
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+ ( XY, WH ) = self.SetUpDraw(dc,
+ WorldToPixel,
+ ScaleWorldToPixel,
+ HTdc)
+ dc.DrawRectanglePointSize(XY, WH)
+ if HTdc and self.HitAble:
+ HTdc.DrawRectanglePointSize(XY, WH)
+
+class Ellipse(RectEllipse):
+# def __init__(*args, **kwargs):
+# RectEllipse.__init__(*args, **kwargs)
+
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+ ( XY, WH ) = self.SetUpDraw(dc,
+ WorldToPixel,
+ ScaleWorldToPixel,
+ HTdc)
+ dc.DrawEllipsePointSize(XY, WH)
+ if HTdc and self.HitAble:
+ HTdc.DrawEllipsePointSize(XY, WH)
+
+class Circle(Ellipse):
+ def __init__(self, x ,y, Diameter, **kwargs):
+ RectEllipse.__init__(self ,
+ x-Diameter/2.,
+ y-Diameter/2.,
+ Diameter,
+ Diameter,
+ **kwargs)
+
+class TextObjectMixin:
+ """
+
+ A mix in class that holds attributes and methods that are needed by
+ the Text objects
+
+ """
+
+ ## I'm caching fonts, because on GTK, getting a new font can take a
+ ## while. However, it gets cleared after every full draw as hanging
+ ## on to a bunch of large fonts takes a massive amount of memory.
+
+ FontList = {}
+
+ def SetFont(self, Size, Family, Style, Weight, Underline, FaceName):
+ self.Font = self.FontList.setdefault( (Size,
+ Family,
+ Style,
+ Weight,
+ Underline,
+ FaceName),
+ wx.Font(Size,
+ Family,
+ Style,
+ Weight,
+ Underline,
+ FaceName) )
+ return self.Font
+
+ ## store the function that shift the coords for drawing text. The
+ ## "c" parameter is the correction for world coordinates, rather
+ ## than pixel coords as the y axis is reversed
+ ShiftFunDict = {'tl': lambda x, y, w, h, world=0: (x, y) ,
+ 'tc': lambda x, y, w, h, world=0: (x - w/2, y) ,
+ 'tr': lambda x, y, w, h, world=0: (x - w, y) ,
+ 'cl': lambda x, y, w, h, world=0: (x, y - h/2 + world*h) ,
+ 'cc': lambda x, y, w, h, world=0: (x - w/2, y - h/2 + world*h) ,
+ 'cr': lambda x, y, w, h, world=0: (x - w, y - h/2 + world*h) ,
+ 'bl': lambda x, y, w, h, world=0: (x, y - h + 2*world*h) ,
+ 'bc': lambda x, y, w, h, world=0: (x - w/2, y - h + 2*world*h) ,
+ 'br': lambda x, y, w, h, world=0: (x - w, y - h + 2*world*h)}
+
+class Text(DrawObject, TextObjectMixin):
+ """
+ This class creates a text object, placed at the coordinates,
+ x,y. the "Position" argument is a two charactor string, indicating
+ where in relation to the coordinates the string should be oriented.
+
+ The first letter is: t, c, or b, for top, center and bottom The
+ second letter is: l, c, or r, for left, center and right The
+ position refers to the position relative to the text itself. It
+ defaults to "tl" (top left).
+
+ Size is the size of the font in pixels, or in points for printing
+ (if it ever gets implimented). Those will be the same, If you assume
+ 72 PPI.
+
+ Family:
+ Font family, a generic way of referring to fonts without
+ specifying actual facename. One of:
+ wx.DEFAULT: Chooses a default font.
+ wx.DECORATIVE: A decorative font.
+ wx.ROMAN: A formal, serif font.
+ wx.SCRIPT: A handwriting font.
+ wx.SWISS: A sans-serif font.
+ wx.MODERN: A fixed pitch font.
+ NOTE: these are only as good as the wxWindows defaults, which aren't so good.
+ Style:
+ One of wx.NORMAL, wx.SLANT and wx.ITALIC.
+ Weight:
+ One of wx.NORMAL, wx.LIGHT and wx.BOLD.
+ Underline:
+ The value can be True or False. At present this may have an an
+ effect on Windows only.
+
+ Alternatively, you can set the kw arg: Font, to a wx.Font, and the above will be ignored.
+
+ The size is fixed, and does not scale with the drawing.
+
+ The hit-test is done on the entire text extent
+
+ """
+
+ def __init__(self,String,x,y,
+ Size = 12,
+ Color = "Black",
+ BackgroundColor = None,
+ Family = wx.MODERN,
+ Style = wx.NORMAL,
+ Weight = wx.NORMAL,
+ Underline = False,
+ Position = 'tl',
+ InForeground = False,
+ Font = None):
+
+ DrawObject.__init__(self,InForeground)
+
+ self.String = String
+ # Input size in in Pixels, compute points size from PPI info.
+ # fixme: for printing, we'll have to do something a little different
+ self.Size = int(round(72.0 * Size / ScreenPPI))
+
+ self.Color = Color
+ self.BackgroundColor = BackgroundColor
+
+ if not Font:
+ FaceName = ''
+ else:
+ FaceName = Font.GetFaceName()
+ Family = Font.GetFamily()
+ Size = Font.GetPointSize()
+ Style = Font.GetStyle()
+ Underlined = Font.GetUnderlined()
+ Weight = Font.GetWeight()
+ self.SetFont(Size, Family, Style, Weight, Underline, FaceName)
+
+ self.BoundingBox = array(((x,y),(x,y)),Float)
+
+ self.XY = ( x,y )
+
+ # use a memDC -- ScreenDC doesn't work with 2.5.1 and GTK2
+ #dc = wx.MemoryDC()
+ #bitmap = wx.EmptyBitmap(1, 1)
+ #dc.SelectObject(bitmap)
+ #dc.SetFont(self.Font)
+ #(self.TextWidth, self.TextHeight) = dc.GetTextExtent(self.String)
+ (self.TextWidth, self.TextHeight) = (None, None)
+ self.ShiftFun = self.ShiftFunDict[Position]
+
+ def SetXY(self, x, y):
+ self.XY = ( x,y )
+ self.BoundingBox = array((self.XY, self.XY),Float)
+ if self._Canvas:
+ self._Canvas.BoundingBoxDirty = True
+
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+ XY = WorldToPixel(self.XY)
+ dc.SetFont(self.Font)
+ dc.SetTextForeground(self.Color)
+ if self.BackgroundColor:
+ dc.SetBackgroundMode(wx.SOLID)
+ dc.SetTextBackground(self.BackgroundColor)
+ else:
+ dc.SetBackgroundMode(wx.TRANSPARENT)
+ if self.TextWidth is None or self.TextHeight is None:
+ (self.TextWidth, self.TextHeight) = dc.GetTextExtent(self.String)
+ XY = self.ShiftFun(XY[0], XY[1], self.TextWidth, self.TextHeight)
+ dc.DrawTextPoint(self.String, XY)
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ HTdc.SetBrush(self.HitBrush)
+ HTdc.DrawRectanglePointSize(XY, (self.TextWidth, self.TextHeight) )
+
+class ScaledText(DrawObject, TextObjectMixin, XYObjectMixin):
+ """
+ This class creates a text object that is scaled when zoomed. It is
+ placed at the coordinates, x,y. the "Position" argument is a two
+ charactor string, indicating where in relation to the coordinates
+ the string should be oriented.
+
+ The first letter is: t, c, or b, for top, center and bottom The
+ second letter is: l, c, or r, for left, center and right The
+ position refers to the position relative to the text itself. It
+ defaults to "tl" (top left).
+
+ Size is the size of the font in world coordinates.
+
+ Family:
+ Font family, a generic way of referring to fonts without
+ specifying actual facename. One of:
+ wx.DEFAULT: Chooses a default font.
+ wx.DECORATI: A decorative font.
+ wx.ROMAN: A formal, serif font.
+ wx.SCRIPT: A handwriting font.
+ wx.SWISS: A sans-serif font.
+ wx.MODERN: A fixed pitch font.
+ NOTE: these are only as good as the wxWindows defaults, which aren't so good.
+ Style:
+ One of wx.NORMAL, wx.SLANT and wx.ITALIC.
+ Weight:
+ One of wx.NORMAL, wx.LIGHT and wx.BOLD.
+ Underline:
+ The value can be True or False. At present this may have an an
+ effect on Windows only.
+
+ Alternatively, you can set the kw arg: Font, to a wx.Font, and the
+ above will be ignored. The size of the font you specify will be
+ ignored, but the rest of it's attributes will be preserved.
+
+ The size will scale as the drawing is zoomed.
+
+ Bugs/Limitations:
+
+ As fonts are scaled, the do end up a little different, so you don't
+ get exactly the same picture as you scale up and doen, but it's
+ pretty darn close.
+
+ On wxGTK1 on my Linux system, at least, using a font of over about
+ 3000 pts. brings the system to a halt. It's the Font Server using
+ huge amounts of memory. My work around is to max the font size to
+ 3000 points, so it won't scale past there. GTK2 uses smarter font
+ drawing, so that may not be an issue in future versions, so feel
+ free to test. Another smarter way to do it would be to set a global
+ zoom limit at that point.
+
+ The hit-test is done on the entire text extent. This could be made
+ optional, but I havn't gotten around to it.
+
+ """
+
+ def __init__(self, String, x, y , Size,
+ Color = "Black",
+ BackgroundColor = None,
+ Family = wx.MODERN,
+ Style = wx.NORMAL,
+ Weight = wx.NORMAL,
+ Underline = False,
+ Position = 'tl',
+ Font = None,
+ InForeground = False):
+
+ DrawObject.__init__(self,InForeground)
+
+ self.String = String
+ self.XY = array( (x, y), Float)
+ self.Size = Size
+ self.Color = Color
+ self.BackgroundColor = BackgroundColor
+ self.Family = Family
+ self.Style = Style
+ self.Weight = Weight
+ self.Underline = Underline
+ if not Font:
+ self.FaceName = ''
+ else:
+ self.FaceName = Font.GetFaceName()
+ self.Family = Font.GetFamily()
+ self.Style = Font.GetStyle()
+ self.Underlined = Font.GetUnderlined()
+ self.Weight = Font.GetWeight()
+
+ # Experimental max font size value on wxGTK2: this works OK on
+ # my system If it's any larger, there is a crash, with the
+ # message: The application 'FloatCanvasDemo.py' lost its
+ # connection to the display :0.0; most likely the X server was
+ # shut down or you killed/destroyed the application.
+ self.MaxSize = 2750
+
+ self.ShiftFun = self.ShiftFunDict[Position]
+
+ ## Compute the BB
+ ## this isn't exact, as fonts don't scale exactly.
+ dc = wx.MemoryDC()
+ bitmap = wx.EmptyBitmap(1, 1)
+ dc.SelectObject(bitmap) #wxMac needs a Bitmap selected for GetTextExtent to work.
+ DrawingSize = 40 # pts This effectively determines the resolution that the BB is computed to.
+ ScaleFactor = float(Size) / DrawingSize
+ dc.SetFont(self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underline, self.FaceName) )
+ (w,h) = dc.GetTextExtent(self.String)
+ w = w * ScaleFactor
+ h = h * ScaleFactor
+ x, y = self.ShiftFun(x, y, w, h, world = 1)
+ self.BoundingBox = array(((x, y-h ),(x + w, y)),Float)
+
+ # the new coords are set to the corner of the BB:
+ #self.X = self.BoundingBox[0,0]
+ #self.Y = self.BoundingBox[1,1]
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+ (X,Y) = WorldToPixel( (self.XY) )
+
+ # compute the font size:
+ Size = abs( ScaleWorldToPixel( (self.Size, self.Size) )[1] ) # only need a y coordinate length
+ ## Check to see if the font size is large enough to blow up the X font server
+ ## If so, limit it. Would it be better just to not draw it?
+ ## note that this limit is dependent on how much memory you have, etc.
+ if Size > self.MaxSize:
+ Size = self.MaxSize
+ dc.SetFont(self.SetFont(Size, self.Family, self.Style, self.Weight, self.Underline, self.FaceName))
+ dc.SetTextForeground(self.Color)
+ if self.BackgroundColor:
+ dc.SetBackgroundMode(wx.SOLID)
+ dc.SetTextBackground(self.BackgroundColor)
+ else:
+ dc.SetBackgroundMode(wx.TRANSPARENT)
+ (w,h) = dc.GetTextExtent(self.String)
+ # compute the shift, and adjust the coordinates, if neccesary
+ # This had to be put in here, because it changes with Zoom, as
+ # fonts don't scale exactly.
+ xy = self.ShiftFun(X, Y, w, h)
+
+ dc.DrawTextPoint(self.String, xy)
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ HTdc.SetBrush(self.HitBrush)
+ HTdc.DrawRectanglePointSize(xy, (w, h) )
+
+
+#---------------------------------------------------------------------------
+class FloatCanvas(wx.Panel):
+ """
+ FloatCanvas.py
+
+ This is a high level window for drawing maps and anything else in an
+ arbitrary coordinate system.
+
+ The goal is to provide a convenient way to draw stuff on the screen
+ without having to deal with handling OnPaint events, converting to pixel
+ coordinates, knowing about wxWindows brushes, pens, and colors, etc. It
+ also provides virtually unlimited zooming and scrolling
+
+ I am using it for two things:
+ 1) general purpose drawing in floating point coordinates
+ 2) displaying map data in Lat-long coordinates
+
+ If the projection is set to None, it will draw in general purpose
+ floating point coordinates. If the projection is set to 'FlatEarth', it
+ will draw a FlatEarth projection, centered on the part of the map that
+ you are viewing. You can also pass in your own projection function.
+
+ 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 havn't profiled it)
+
+ Bugs and Limitations:
+ Lots: patches, fixes welcome
+
+ For Map drawing: It ignores the fact that the world is, in fact, a
+ sphere, so it will do strange things if you are looking at stuff near
+ the poles or the date line. so far I don't have a need to do that, so I
+ havn't bothered to add any checks for that yet.
+
+ Zooming:
+ I have set no zoom limits. What this means is that if you zoom in really
+ far, you can get integer overflows, and get wierd results. It
+ doesn't seem to actually cause any problems other than wierd output, at
+ least when I have run it.
+
+ Speed:
+ I have done a couple of things to improve speed in this app. The one
+ thing I have done is used NumPy Arrays to store the coordinates of the
+ points of the objects. This allowed me to use array oriented functions
+ when doing transformations, and should provide some speed improvement
+ for objects with a lot of points (big polygons, polylines, pointsets).
+
+ The real slowdown comes when you have to draw a lot of objects, because
+ you have to call the wx.DC.DrawSomething call each time. This is plenty
+ fast for tens of objects, OK for hundreds of objects, but pretty darn
+ slow for thousands of objects.
+
+ The 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. I havn't got a LineSet type object, so I havn't
+ used DC.DrawLineList yet. I'd like to get a full set of DrawStuffList()
+ methods implimented, and then I'd also have a full set of Object sets
+ that could take advantage of them. I hope to get to it some day.
+
+ Mouse Events:
+
+ At this point, there are a full set of custom mouse events. They are
+ just like the rebulsr mouse events, but include an extra attribute:
+ Event.GetCoords(), that returns the (x,y) position in world
+ coordinates, as a length-2 NumPy vector of Floats.
+
+ 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,
+ ProjectionFun = None,
+ BackgroundColor = "WHITE",
+ Debug = False):
+
+ wx.Panel.__init__( self, parent, id, wx.DefaultPosition, size)
+
+ global ScreenPPI ## A global variable to hold the Pixels per inch that wxWindows thinks is in use.
+ dc = wx.ScreenDC()
+ ScreenPPI = dc.GetPPI()[0] # Assume square pixels
+ del dc
+
+ self.HitColorGenerator = None
+ self.UseHitTest = None
+
+ self.NumBetweenBlits = 500
+
+ self.BackgroundBrush = wx.Brush(BackgroundColor,wx.SOLID)
+
+ self.Debug = Debug
+
+ wx.EVT_PAINT(self, self.OnPaint)
+ wx.EVT_SIZE(self, self.OnSize)
+
+ wx.EVT_LEFT_DOWN(self, self.LeftDownEvent )
+ wx.EVT_LEFT_UP(self, self.LeftUpEvent )
+ wx.EVT_LEFT_DCLICK(self, self.LeftDoubleClickEvent )
+ wx.EVT_MIDDLE_DOWN(self, self.MiddleDownEvent )
+ wx.EVT_MIDDLE_UP(self, self.MiddleUpEvent )
+ wx.EVT_MIDDLE_DCLICK(self, self.MiddleDoubleClickEvent )
+ wx.EVT_RIGHT_DOWN(self, self.RightDownEvent)
+ wx.EVT_RIGHT_UP(self, self.RightUpEvent )
+ wx.EVT_RIGHT_DCLICK(self, self.RightDoubleCLickEvent )
+ wx.EVT_MOTION(self, self.MotionEvent )
+ wx.EVT_MOUSEWHEEL(self, self.WheelEvent )
+
+ ## CHB: I'm leaving these out for now.
+ #wx.EVT_ENTER_WINDOW(self, self. )
+ #wx.EVT_LEAVE_WINDOW(self, self. )
+
+ ## create the Hit Test Dicts:
+ self.HitDict = None
+
+
+ self._DrawList = []
+ self._ForeDrawList = []
+ self._ForegroundBuffer = None
+ self.BoundingBox = None
+ self.BoundingBoxDirty = False
+ self.ViewPortCenter= array( (0,0), Float)
+
+ self.SetProjectionFun(ProjectionFun)
+
+ self.MapProjectionVector = array( (1,1), Float) # No Projection to start!
+ self.TransformVector = array( (1,-1), Float) # default Transformation
+
+ self.Scale = 1
+
+ self.GUIMode = None
+ self.StartRBBox = None
+ self.PrevRBBox = None
+ self.StartMove = None
+ self.PrevMoveXY = None
+ self.ObjectUnderMouse = None
+
+ # called just to make sure everything is initialized
+ self.OnSize(None)
+
+ self.InHereNum = 0
+
+ def SetProjectionFun(self,ProjectionFun):
+ if ProjectionFun == 'FlatEarth':
+ self.ProjectionFun = self.FlatEarthProjection
+ elif type(ProjectionFun) == types.FunctionType:
+ self.ProjectionFun = ProjectionFun
+ elif ProjectionFun is None:
+ self.ProjectionFun = lambda x=None: array( (1,1), Float)
+ else:
+ raise FloatCanvasException('Projectionfun must be either: "FlatEarth", None, or a function that takes the ViewPortCenter and returns a MapProjectionVector')
+
+ def FlatEarthProjection(self,CenterPoint):
+ return array((cos(pi*CenterPoint[1]/180),1),Float)
+
+ def SetMode(self,Mode):
+ if Mode in ["ZoomIn","ZoomOut","Move","Mouse",None]:
+ self.GUIMode = Mode
+ else:
+ raise FloatCanvasException('"%s" is Not a valid Mode'%Mode)
+
+ def MakeHitDict(self):
+ ##fixme: Should this just be None if nothing has been bound?
+ self.HitDict = {EVT_FC_LEFT_DOWN: {},
+ EVT_FC_LEFT_UP: {},
+ EVT_FC_LEFT_DCLICK: {},
+ EVT_FC_MIDDLE_DOWN: {},
+ EVT_FC_MIDDLE_UP: {},
+ EVT_FC_MIDDLE_DCLICK: {},
+ EVT_FC_RIGHT_DOWN: {},
+ EVT_FC_RIGHT_UP: {},
+ EVT_FC_RIGHT_DCLICK: {},
+ EVT_FC_ENTER_OBJECT: {},
+ EVT_FC_LEAVE_OBJECT: {},
+ }
+
+ def RaiseMouseEvent(self, Event, EventType):
+ """
+ This is called in various other places to raise a Mouse Event
+ """
+ #print "in Raise Mouse Event", Event
+ pt = self.PixelToWorld( Event.GetPosition() )
+ evt = MouseEvent(EventType, Event, self.GetId(), pt)
+ self.GetEventHandler().ProcessEvent(evt)
+
+ def HitTest(self, event, HitEvent):
+ if self.HitDict:
+ # check if there are any objects in the dict for this event
+ if self.HitDict[ HitEvent ]:
+ xy = event.GetPosition()
+ if self._ForegroundHTdc:
+ hitcolor = self._ForegroundHTdc.GetPixelPoint( xy )
+ else:
+ hitcolor = self._HTdc.GetPixelPoint( xy )
+ color = ( hitcolor.Red(), hitcolor.Green(), hitcolor.Blue() )
+ if color in self.HitDict[ HitEvent ]:
+ Object = self.HitDict[ HitEvent ][color]
+ ## Add the hit coords to the Object
+ Object.HitCoords = self.PixelToWorld( xy )
+ Object.CallBackFuncs[HitEvent](Object)
+ return True
+ return False
+
+ def MouseOverTest(self, event):
+ ##fixme: Can this be cleaned up?
+ if self.HitDict:
+ xy = event.GetPosition()
+ if self._ForegroundHTdc:
+ hitcolor = self._ForegroundHTdc.GetPixelPoint( xy )
+ else:
+ hitcolor = self._HTdc.GetPixelPoint( xy )
+ color = ( hitcolor.Red(), hitcolor.Green(), hitcolor.Blue() )
+ OldObject = self.ObjectUnderMouse
+ ObjectCallbackCalled = False
+ if color in self.HitDict[ EVT_FC_ENTER_OBJECT ]:
+ Object = self.HitDict[ EVT_FC_ENTER_OBJECT][color]
+ if (OldObject is None):
+ try:
+ Object.CallBackFuncs[EVT_FC_ENTER_OBJECT](Object)
+ ObjectCallbackCalled = True
+ except KeyError:
+ pass # this means the enter event isn't bound for that object
+ elif OldObject == Object: # the mouse is still on the same object
+ pass
+ ## Is the mouse on a differnt object as it was...
+ elif not (Object == OldObject):
+ # call the leave object callback
+ try:
+ OldObject.CallBackFuncs[EVT_FC_LEAVE_OBJECT](OldObject)
+ ObjectCallbackCalled = True
+ except KeyError:
+ pass # this means the leave event isn't bound for that object
+ try:
+ Object.CallBackFuncs[EVT_FC_ENTER_OBJECT](Object)
+ ObjectCallbackCalled = True
+ except KeyError:
+ pass # this means the enter event isn't bound for that object
+ ## set the new object under mouse
+ self.ObjectUnderMouse = Object
+ elif color in self.HitDict[ EVT_FC_LEAVE_OBJECT ]:
+ Object = self.HitDict[ EVT_FC_LEAVE_OBJECT][color]
+ self.ObjectUnderMouse = Object
+ else:
+ # no objects under mouse bound to mouse-over events
+ self.ObjectUnderMouse = None
+ if OldObject:
+ try:
+ OldObject.CallBackFuncs[EVT_FC_LEAVE_OBJECT](OldObject)
+ ObjectCallbackCalled = True
+ except KeyError:
+ pass # this means the leave event isn't bound for that object
+ return ObjectCallbackCalled
+
+
+ ## fixme: There is a lot of repeated code here
+ ## Is there a better way?
+ def LeftDoubleClickEvent(self,event):
+ if self.GUIMode == "Mouse":
+ EventType = EVT_FC_LEFT_DCLICK
+ if not self.HitTest(event, EventType):
+ self.RaiseMouseEvent(event, EventType)
+
+
+ def MiddleDownEvent(self,event):
+ if self.GUIMode == "Mouse":
+ EventType = EVT_FC_MIDDLE_DOWN
+ if not self.HitTest(event, EventType):
+ self.RaiseMouseEvent(event, EventType)
+
+ def MiddleUpEvent(self,event):
+ if self.GUIMode == "Mouse":
+ EventType = EVT_FC_MIDDLE_UP
+ if not self.HitTest(event, EventType):
+ self.RaiseMouseEvent(event, EventType)
+
+ def MiddleDoubleClickEvent(self,event):
+ if self.GUIMode == "Mouse":
+ EventType = EVT_FC_MIDDLE_DCLICK
+ if not self.HitTest(event, EventType):
+ self.RaiseMouseEvent(event, EventType)
+
+ def RightUpEvent(self,event):
+ if self.GUIMode == "Mouse":
+ EventType = EVT_FC_RIGHT_UP
+ if not self.HitTest(event, EventType):
+ self.RaiseMouseEvent(event, EventType)
+
+ def RightDoubleCLickEvent(self,event):
+ if self.GUIMode == "Mouse":
+ EventType = EVT_FC_RIGHT_DCLICK
+ if not self.HitTest(event, EventType):
+ self.RaiseMouseEvent(event, EventType)
+
+ def WheelEvent(self,event):
+ if self.GUIMode == "Mouse":
+ self.RaiseMouseEvent(event, EVT_FC_MOUSEWHEEL)
+
+
+ def LeftDownEvent(self,event):
+ if self.GUIMode:
+ if self.GUIMode == "ZoomIn":
+ self.StartRBBox = array( event.GetPosition() )
+ self.PrevRBBox = None
+ self.CaptureMouse()
+ elif self.GUIMode == "ZoomOut":
+ Center = self.PixelToWorld( event.GetPosition() )
+ self.Zoom(1/1.5,Center)
+ elif self.GUIMode == "Move":
+ self.StartMove = array( event.GetPosition() )
+ self.PrevMoveXY = (0,0)
+ elif self.GUIMode == "Mouse":
+ ## check for a hit
+ if not self.HitTest(event, EVT_FC_LEFT_DOWN):
+ self.RaiseMouseEvent(event,EVT_FC_LEFT_DOWN)
+ else:
+ pass
+
+ def LeftUpEvent(self,event):
+ if self.HasCapture():
+ self.ReleaseMouse()
+ if self.GUIMode:
+ if self.GUIMode == "ZoomIn":
+ 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.PixelToWorld(EndRBBox)
+ StartRBBox = self.PixelToWorld(StartRBBox)
+ BB = array(((min(EndRBBox[0],StartRBBox[0]),
+ min(EndRBBox[1],StartRBBox[1])),
+ (max(EndRBBox[0],StartRBBox[0]),
+ max(EndRBBox[1],StartRBBox[1]))),Float)
+ self.ZoomToBB(BB)
+ else:
+ Center = self.PixelToWorld(StartRBBox)
+ self.Zoom(1.5,Center)
+ self.StartRBBox = None
+ elif self.GUIMode == "Move":
+ if not self.StartMove is None:
+ StartMove = self.StartMove
+ EndMove = array((event.GetX(),event.GetY()))
+ if sum((StartMove-EndMove)**2) > 16:
+ self.Move(StartMove-EndMove,'Pixel')
+ self.StartMove = None
+ elif self.GUIMode == "Mouse":
+ EventType = EVT_FC_LEFT_UP
+ if not self.HitTest(event, EventType):
+ self.RaiseMouseEvent(event, EventType)
+ else:
+ pass
+
+ def MotionEvent(self,event):
+ if self.GUIMode:
+ if self.GUIMode == "ZoomIn":
+ if event.Dragging() and event.LeftIsDown() and not (self.StartRBBox is None):
+ xy0 = self.StartRBBox
+ xy1 = array( event.GetPosition() )
+ wh = abs(xy1 - xy0)
+ wh[0] = max(wh[0], int(wh[1]*self.AspectRatio))
+ wh[1] = int(wh[0] / self.AspectRatio)
+ xy_c = (xy0 + xy1) / 2
+ dc = wx.ClientDC(self)
+ 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()
+ elif self.GUIMode == "Move":
+ if event.Dragging() and event.LeftIsDown() and not self.StartMove is None:
+ xy1 = array( event.GetPosition() )
+ wh = self.PanelSize
+ xy_tl = xy1 - self.StartMove
+ dc = wx.ClientDC(self)
+ dc.BeginDrawing()
+ x1,y1 = self.PrevMoveXY
+ x2,y2 = xy_tl
+ w,h = self.PanelSize
+ 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.BackgroundBrush)
+ dc.DrawRectangle(xa, ya, wa, ha)
+ dc.DrawRectangle(xb, yb, wb, hb)
+ self.PrevMoveXY = xy_tl
+ if self._ForegroundBuffer:
+ dc.DrawBitmapPoint(self._ForegroundBuffer,xy_tl)
+ else:
+ dc.DrawBitmapPoint(self._Buffer,xy_tl)
+ dc.EndDrawing()
+ elif self.GUIMode == "Mouse":
+ ## Only do something if there are mouse over events bound
+ if self.HitDict and (self.HitDict[ EVT_FC_ENTER_OBJECT ] or self.HitDict[ EVT_FC_LEAVE_OBJECT ] ):
+ if not self.MouseOverTest(event):
+ self.RaiseMouseEvent(event,EVT_FC_MOTION)
+ else:
+ pass
+ self.RaiseMouseEvent(event,EVT_FC_MOTION)
+ else:
+ pass
+
+ def RightDownEvent(self,event):
+ if self.GUIMode:
+ if self.GUIMode == "ZoomIn":
+ Center = self.PixelToWorld((event.GetX(),event.GetY()))
+ self.Zoom(1/1.5,Center)
+ elif self.GUIMode == "ZoomOut":
+ Center = self.PixelToWorld((event.GetX(),event.GetY()))
+ self.Zoom(1.5,Center)
+ elif self.GUIMode == "Mouse":
+ EventType = EVT_FC_RIGHT_DOWN
+ if not self.HitTest(event, EventType):
+ self.RaiseMouseEvent(event, EventType)
+ else:
+ pass
+
+ def MakeNewBuffers(self):
+ self._BackgroundDirty = True
+ # Make new offscreen bitmap:
+ self._Buffer = wx.EmptyBitmap(*self.PanelSize)
+ #dc = wx.MemoryDC()
+ #dc.SelectObject(self._Buffer)
+ #dc.Clear()
+ if self._ForeDrawList:
+ self._ForegroundBuffer = wx.EmptyBitmap(*self.PanelSize)
+ else:
+ self._ForegroundBuffer = None
+ if self.UseHitTest:
+ self.MakeNewHTdc()
+ else:
+ self._HTdc = None
+ self._ForegroundHTdc = None
+
+ def MakeNewHTdc(self):
+ ## Note: While it's considered a "bad idea" to keep a
+ ## MemoryDC around I'm doing it here because a wx.Bitmap
+ ## doesn't have a GetPixel method so a DC is needed to do
+ ## the hit-test. It didn't seem like a good idea to re-create
+ ## a wx.MemoryDC on every single mouse event, so I keep it
+ ## around instead
+ self._HTdc = wx.MemoryDC()
+ self._HTBitmap = wx.EmptyBitmap(*self.PanelSize)
+ self._HTdc.SelectObject( self._HTBitmap )
+ self._HTdc.SetBackground(wx.BLACK_BRUSH)
+ if self._ForeDrawList:
+ self._ForegroundHTdc = wx.MemoryDC()
+ self._ForegroundHTBitmap = wx.EmptyBitmap(*self.PanelSize)
+ self._ForegroundHTdc.SelectObject( self._ForegroundHTBitmap )
+ self._ForegroundHTdc.SetBackground(wx.BLACK_BRUSH)
+ else:
+ self._ForegroundHTdc = None
+
+ def OnSize(self,event):
+ self.PanelSize = array(self.GetClientSizeTuple(),Int32)
+ self.HalfPanelSize = self.PanelSize / 2 # lrk: added for speed in WorldToPixel
+ if self.PanelSize[0] == 0 or self.PanelSize[1] == 0:
+ self.AspectRatio = 1.0
+ else:
+ self.AspectRatio = float(self.PanelSize[0]) / self.PanelSize[1]
+ self.MakeNewBuffers()
+ self.Draw()
+
+ def OnPaint(self, event):
+ dc = wx.PaintDC(self)
+ if self._ForegroundBuffer:
+ dc.DrawBitmap(self._ForegroundBuffer,0,0)
+ else:
+ dc.DrawBitmap(self._Buffer,0,0)
+
+ def Draw(self, Force=False):
+ """
+ There is a main buffer set up to double buffer the screen, so
+ you can get quick re-draws when the window gets uncovered.
+
+ If there are any objects in self._ForeDrawList, then the
+ background gets drawn to a new buffer, and the foreground
+ objects get drawn on top of it. The final result if blitted to
+ the screen, and stored for future Paint events. This is done so
+ that you can have a complicated background, but have something
+ changing on the foreground, without having to wait for the
+ background to get re-drawn. This can be used to support simple
+ animation, for instance.
+
+ """
+ #print "In Draw"
+ if self.Debug: start = clock()
+ ScreenDC = wx.ClientDC(self)
+ ViewPortWorld = ( self.PixelToWorld((0,0)),
+ self.PixelToWorld(self.PanelSize) )
+ ViewPortBB = array( ( minimum.reduce(ViewPortWorld),
+ maximum.reduce(ViewPortWorld) ) )
+ dc = wx.MemoryDC()
+ dc.SelectObject(self._Buffer)
+ if self._BackgroundDirty or Force:
+ #print "Background is Dirty"
+ dc.SetBackground(self.BackgroundBrush)
+ dc.Clear()
+ if self._HTdc:
+ self._HTdc.Clear()
+ self._DrawObjects(dc, self._DrawList, ScreenDC, ViewPortBB, self._HTdc)
+ self._BackgroundDirty = False
+
+ if self._ForeDrawList:
+ ## If an object was just added to the Foreground, there might not yet be a buffer
+ if self._ForegroundBuffer is None:
+ self._ForegroundBuffer = wx.EmptyBitmap(self.PanelSize[0],
+ self.PanelSize[1])
+
+ dc = wx.MemoryDC() ## I got some strange errors (linewidths wrong) if I didn't make a new DC here
+ dc.SelectObject(self._ForegroundBuffer)
+ dc.DrawBitmap(self._Buffer,0,0)
+ if self._ForegroundHTdc is None:
+ self._ForegroundHTdc = wx.MemoryDC()
+ self._ForegroundHTdc.SelectObject( wx.EmptyBitmap(
+ self.PanelSize[0],
+ self.PanelSize[1]) )
+ if self._HTdc:
+ ## blit the background HT buffer to the foreground HT buffer
+ self._ForegroundHTdc.Blit(0, 0,
+ self.PanelSize[0], self.PanelSize[1],
+ self._HTdc, 0, 0)
+ self._DrawObjects(dc,
+ self._ForeDrawList,
+ ScreenDC,
+ ViewPortBB,
+ self._ForegroundHTdc)
+ ScreenDC.Blit(0, 0, self.PanelSize[0],self.PanelSize[1], dc, 0, 0)
+## wx.GetApp().Yield(True)
+ # If the canvas is in the middle of a zoom or move, the Rubber Band box needs to be re-drawn
+ # This seeems out of place, but it works.
+ if self.PrevRBBox:
+ ScreenDC.SetPen(wx.Pen('WHITE', 2,wx.SHORT_DASH))
+ ScreenDC.SetBrush(wx.TRANSPARENT_BRUSH)
+ ScreenDC.SetLogicalFunction(wx.XOR)
+ ScreenDC.DrawRectanglePointSize(*self.PrevRBBox)
+ if self.Debug: print "Drawing took %f seconds of CPU time"%(clock()-start)
+
+ ## Clear the font cache
+ ## IF you don't do this, the X font server starts to take up Massive amounts of memory
+ ## This is mostly a problem with very large fonts, that you get with scaled text when zoomed in.
+ DrawObject.FontList = {}
+
+ def _ShouldRedraw(DrawList, ViewPortBB): # lrk: adapted code from BBCheck
+ # lrk: Returns the objects that should be redrawn
+
+ BB2 = ViewPortBB
+ redrawlist = []
+ for Object in DrawList:
+ BB1 = Object.BoundingBox
+ if (BB1[1,0] > BB2[0,0] and BB1[0,0] < BB2[1,0] and
+ BB1[1,1] > BB2[0,1] and BB1[0,1] < BB2[1,1]):
+ redrawlist.append(Object)
+ return redrawlist
+ _ShouldRedraw = staticmethod(_ShouldRedraw)
+
+
+## def BBCheck(self, BB1, BB2):
+## """
+
+## BBCheck(BB1, BB2) returns True is the Bounding boxes intesect, False otherwise
+
+## """
+## if ( (BB1[1,0] > BB2[0,0]) and (BB1[0,0] < BB2[1,0]) and
+## (BB1[1,1] > BB2[0,1]) and (BB1[0,1] < BB2[1,1]) ):
+## return True
+## else:
+## return False
+
+ def Move(self,shift,CoordType):
+ """
+ move the image in the window.
+
+ shift is an (x,y) tuple, specifying the amount to shift in each direction
+
+ It can be in any of three coordinates: Panel, Pixel, World,
+ specified by the CoordType parameter
+
+ Panel coordinates means you want to shift the image by some
+ fraction of the size of the displaed image
+
+ Pixel coordinates means you want to shift the image by some number of pixels
+
+ World coordinates mean you want to shift the image by an amount
+ in Floating point world coordinates
+
+ """
+
+ shift = array(shift,Float)
+ if CoordType == 'Panel':# convert from panel coordinates
+ shift = shift * array((-1,1),Float) *self.PanelSize/self.TransformVector
+ elif CoordType == 'Pixel': # convert from pixel coordinates
+ shift = shift/self.TransformVector
+ elif CoordType == 'World': # No conversion
+ pass
+ else:
+ raise FloatCanvasException('CoordType must be either "Panel", "Pixel", or "World"')
+
+ self.ViewPortCenter = self.ViewPortCenter + shift
+ self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
+ self.TransformVector = array((self.Scale,-self.Scale),Float) * self.MapProjectionVector
+ self._BackgroundDirty = True
+ self.Draw()
+
+ def Zoom(self,factor,center = None):
+
+ """
+ Zoom(factor, center) changes the amount of zoom of the image by factor.
+ If factor is greater than one, the image gets larger.
+ If factor is less than one, the image gets smaller.
+
+ Center is a tuple of (x,y) coordinates of the center of the viewport, after zooming.
+ If center is not given, the center will stay the same.
+
+ """
+ self.Scale = self.Scale*factor
+ if not center is None:
+ self.ViewPortCenter = array(center,Float)
+ self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
+ self.TransformVector = array((self.Scale,-self.Scale),Float) * self.MapProjectionVector
+ self._BackgroundDirty = True
+ self.Draw()
+
+ def ZoomToBB(self, NewBB = None, DrawFlag = True):
+
+ """
+
+ Zooms the image to the bounding box given, or to the bounding
+ box of all the objects on the canvas, if none is given.
+
+ """
+
+ if not NewBB is None:
+ BoundingBox = NewBB
+ else:
+ if self.BoundingBoxDirty:
+ self._ResetBoundingBox()
+ BoundingBox = self.BoundingBox
+ if not BoundingBox is None:
+ self.ViewPortCenter = array(((BoundingBox[0,0]+BoundingBox[1,0])/2,
+ (BoundingBox[0,1]+BoundingBox[1,1])/2 ),Float)
+ self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
+ # Compute the new Scale
+ BoundingBox = BoundingBox * self.MapProjectionVector
+ try:
+ self.Scale = min(abs(self.PanelSize[0] / (BoundingBox[1,0]-BoundingBox[0,0])),
+ abs(self.PanelSize[1] / (BoundingBox[1,1]-BoundingBox[0,1])) )*0.95
+ except ZeroDivisionError: # this will happen if the BB has zero width or height
+ try: #width == 0
+ self.Scale = (self.PanelSize[0] / (BoundingBox[1,0]-BoundingBox[0,0]))*0.95
+ except ZeroDivisionError:
+ try: # height == 0
+ self.Scale = (self.PanelSize[1] / (BoundingBox[1,1]-BoundingBox[0,1]))*0.95
+ except ZeroDivisionError: #zero size! (must be a single point)
+ self.Scale = 1
+
+ self.TransformVector = array((self.Scale,-self.Scale),Float)* self.MapProjectionVector
+ if DrawFlag:
+ self._BackgroundDirty = True
+ self.Draw()
+ else:
+ # Reset the shifting and scaling to defaults when there is no BB
+ self.ViewPortCenter= array( (0,0), Float)
+ self.MapProjectionVector = array( (1,1), Float) # No Projection to start!
+ self.TransformVector = array( (1,-1), Float) # default Transformation
+ self.Scale = 1
+
+ def RemoveObjects(self, Objects):
+ for Object in Objects:
+ self.RemoveObject(Object, ResetBB = False)
+ self.BoundingBoxDirty = True
+
+ def RemoveObject(self, Object, ResetBB = True):
+ ##fixme: Using the list.remove method is kind of slow
+ if Object.InForeground:
+ self._ForeDrawList.remove(Object)
+ else:
+ self._DrawList.remove(Object)
+ self._BackgroundDirty = True
+ if ResetBB:
+ self.BoundingBoxDirty = True
+
+ def ClearAll(self, ResetBB = True):
+ self._DrawList = []
+ self._ForeDrawList = []
+ self._BackgroundDirty = True
+ self.HitColorGenerator = None
+ self.UseHitTest = False
+ if ResetBB:
+ self._ResetBoundingBox()
+ self.MakeNewBuffers()
+ self.HitDict = None
+
+## No longer called
+## def _AddBoundingBox(self,NewBB):
+## if self.BoundingBox is None:
+## self.BoundingBox = NewBB
+## self.ZoomToBB(NewBB,DrawFlag = False)
+## else:
+## self.BoundingBox = array( ( (min(self.BoundingBox[0,0],NewBB[0,0]),
+## min(self.BoundingBox[0,1],NewBB[0,1])),
+## (max(self.BoundingBox[1,0],NewBB[1,0]),
+## max(self.BoundingBox[1,1],NewBB[1,1]))),
+## Float)
+
+ def _getboundingbox(bboxarray): # lrk: added this
+
+ upperleft = minimum.reduce(bboxarray[:,0])
+ lowerright = maximum.reduce(bboxarray[:,1])
+ return array((upperleft, lowerright), Float)
+
+ _getboundingbox = staticmethod(_getboundingbox)
+
+ def _ResetBoundingBox(self):
+ if self._DrawList or self._ForeDrawList:
+ bboxarray = zeros((len(self._DrawList)+len(self._ForeDrawList), 2, 2),Float)
+ i = -1 # just in case _DrawList is empty
+ for (i, BB) in enumerate(self._DrawList):
+ bboxarray[i] = BB.BoundingBox
+ for (j, BB) in enumerate(self._ForeDrawList):
+ bboxarray[i+j+1] = BB.BoundingBox
+ self.BoundingBox = self._getboundingbox(bboxarray)
+ else:
+ self.BoundingBox = None
+ self.ViewPortCenter= array( (0,0), Float)
+ self.TransformVector = array( (1,-1), Float)
+ self.MapProjectionVector = array( (1,1), Float)
+ self.Scale = 1
+ self.BoundingBoxDirty = False
+
+ def PixelToWorld(self,Points):
+ """
+ Converts coordinates from Pixel coordinates to world coordinates.
+
+ Points is a tuple of (x,y) coordinates, or a list of such tuples, or a NX2 Numpy array of x,y coordinates.
+
+ """
+ return (((asarray(Points,Float) - (self.PanelSize/2))/self.TransformVector) + self.ViewPortCenter)
+
+ def WorldToPixel(self,Coordinates):
+ """
+ This function will get passed to the drawing functions of the objects,
+ to transform from world to pixel coordinates.
+ Coordinates should be a NX2 array of (x,y) coordinates, or
+ a 2-tuple, or sequence of 2-tuples.
+ """
+ #Note: this can be called by users code for various reasons, so asarray is needed.
+ return (((asarray(Coordinates,Float) - self.ViewPortCenter)*self.TransformVector)+(self.HalfPanelSize)).astype('i')
+
+ def ScaleWorldToPixel(self,Lengths):
+ """
+ This function will get passed to the drawing functions of the objects,
+ to Change a length from world to pixel coordinates.
+
+ Lengths should be a NX2 array of (x,y) coordinates, or
+ a 2-tuple, or sequence of 2-tuples.
+ """
+ return ( (asarray(Lengths,Float)*self.TransformVector) ).astype('i')
+
+ def ScalePixelToWorld(self,Lengths):
+ """
+ This function computes a pair of x.y lengths,
+ to change then from pixel to world coordinates.
+
+ Lengths should be a NX2 array of (x,y) coordinates, or
+ a 2-tuple, or sequence of 2-tuples.
+ """
+
+ return (asarray(Lengths,Float) / self.TransformVector)
+
+ def AddObject(self,obj):
+ # put in a reference to the Canvas, so remove and other stuff can work
+ obj._Canvas = self
+ if obj.InForeground:
+ self._ForeDrawList.append(obj)
+ self.UseForeground = True
+ else:
+ self._DrawList.append(obj)
+ self._BackgroundDirty = True
+ self.BoundingBoxDirty = True
+ return True
+
+ def _DrawObjects(self, dc, DrawList, ScreenDC, ViewPortBB, HTdc = None):
+ """
+ This is a convenience function;
+ This function takes the list of objects and draws them to specified
+ device context.
+ """
+ dc.SetBackground(self.BackgroundBrush)
+ dc.BeginDrawing()
+ #i = 0
+ PanelSize0, PanelSize1 = self.PanelSize # for speed
+ WorldToPixel = self.WorldToPixel # for speed
+ ScaleWorldToPixel = self.ScaleWorldToPixel # for speed
+ Blit = ScreenDC.Blit # for speed
+ NumBetweenBlits = self.NumBetweenBlits # for speed
+ for i, Object in enumerate(self._ShouldRedraw(DrawList, ViewPortBB)):
+ Object._Draw(dc, WorldToPixel, ScaleWorldToPixel, HTdc)
+ if i % NumBetweenBlits == 0:
+ Blit(0, 0, PanelSize0, PanelSize1, dc, 0, 0)
+ dc.EndDrawing()
+
+## ## This is a way to automatically add a AddObject method for each
+## ## object type This code has been replaced by Leo's code above, so
+## ## that it happens at module init, rather than as needed. The
+## ## primary advantage of this is that dir(FloatCanvas) will have
+## ## them, and docstrings are preserved. Probably more useful
+## ## exceptions if there is a problem, as well.
+## def __getattr__(self, name):
+## if name[:3] == "Add":
+## func=globals()[name[3:]]
+## def AddFun(*args, **kwargs):
+## Object = func(*args, **kwargs)
+## self.AddObject(Object)
+## return Object
+## ## add it to FloatCanvas' dict for future calls.
+## self.__dict__[name] = AddFun
+## return AddFun
+## else:
+## raise AttributeError("FloatCanvas has no attribute '%s'"%name)
+
+def _makeFloatCanvasAddMethods(): ## lrk's code for doing this in module __init__
+ classnames = ["Circle", "Ellipse", "Rectangle", "ScaledText", "Polygon",
+ "Line", "Text", "PointSet"]
+ for classname in classnames:
+ klass = globals()[classname]
+ def getaddshapemethod(klass=klass):
+ def addshape(self, *args, **kwargs):
+ Object = klass(*args, **kwargs)
+ self.AddObject(Object)
+ return Object
+ return addshape
+ addshapemethod = getaddshapemethod()
+ methodname = "Add" + classname
+ setattr(FloatCanvas, methodname, addshapemethod)
+ docstring = "Creates %s and adds its reference to the canvas.\n" % classname
+ docstring += "Argument protocol same as %s class" % classname
+ if klass.__doc__:
+ docstring += ", whose docstring is:\n%s" % klass.__doc__
+ FloatCanvas.__dict__[methodname].__doc__ = docstring
+
+_makeFloatCanvasAddMethods()
+
+
--- /dev/null
+"""
+A Panel that includes the FloatCanvas and Navigation controls
+
+"""
+
+#from Numeric import array,Float,cos,pi,sum,minimum,maximum,Int32
+
+#from time import clock, sleep
+
+import wx
+
+#import types
+#import os
+
+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
+
+ This is a high level window that encloses the FloatCanvas in a panel
+ and adds a Navigation toolbar.
+
+ Copyright: wxWindows Software Foundation (Assigned by: 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)
+
+ ## 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.Fit(self)
+ self.SetSizer(box)
+
+ # default to Mouse mode
+ self.ToolBar.ToggleTool(ID_POINTER_BUTTON,1)
+ self.Canvas.SetMode("Mouse")
+
+ 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)
+ self.ToolBar = tb
+
+ tb.SetToolBitmapSize((23,23))
+
+ 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.GetPlusBitmap(), isToggle=True, shortHelpString = "Zoom In")
+ wx.EVT_TOOL(self, ID_ZOOM_IN_BUTTON, self.SetToolMode)
+
+ tb.AddTool(ID_ZOOM_OUT_BUTTON, Resources.GetMinusBitmap(), 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)
+
+ 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)
+
+ tb.Realize()
+ tb.SetSizeHints(tb.GetSize())
+ 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 ZoomToFit(self,Event):
+ self.Canvas.ZoomToBB()
+
--- /dev/null
+"""
+Resources.py Various resources needed by the FloatCanvas package
+
+Includes, icons, etc.
+
+"""
+
+
+### These are some functions for bitmaps of icons.
+import wx, cPickle, zlib
+
+def GetHandData():
+ return cPickle.loads(zlib.decompress(
+'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
+\x01\xc8S\xb6t\x06A(\x1f\x0b\xa0\xa9\x8c\x9e\x1e6\x19\xa0\xa8\x1e\x88\xd4C\
+\x97\xd1\x83\xe8\x80 \x9c2zh\xa6\xc1\x11X\n\xab\x8c\x02\x8a\x0cD!\x92\x12\
+\x98\x8c\x1e\x8a\x8b\xd1d\x14\xf4\x90%\x90LC\xf6\xbf\x1e\xba\xab\x91%\xd0\
+\xdc\x86C\x06\xd9m\xe8!\xaa\x87S\x86\x1a1\xa7\x07\x00v\x0f[\x17' ))
+
+def GetHandBitmap():
+ return wx.BitmapFromXPMData(GetHandData())
+
+#----------------------------------------------------------------------
+def GetPlusData():
+ return cPickle.loads(zlib.decompress(
+'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
+\x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=l2`\r\
+\xe82HF\xe9a\xc8\xe8\xe9A\x9c@\x8a\x0c\x0e\xd3p\xbb\x00\x8f\xab\xe1>\xd5\xd3\
+\xc3\x15:P)l!\n\x91\xc2\x1a\xd6`)\xec\xb1\x00\x92\xc2\x11?\xb8e\x88\x8fSt\
+\x19=\x00\x82\x16[\xf7' ))
+
+def GetPlusBitmap():
+ return wx.BitmapFromXPMData(GetPlusData())
+
+#----------------------------------------------------------------------
+def GetMinusData():
+ return cPickle.loads(zlib.decompress(
+'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
+\x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=\xa2e\
+\x10\x16@\x99\xc82zz\x10\'\x90"\x83\xc34r\xdc\x86\xf0\xa9\x9e\x1e\xae\xd0\
+\x81Ja\x0bQ\x88\x14\xd6\xb0\x06Ka\x8f\x05\x90\x14\x8e\xf8\xc1-C|\x9c\xa2\xcb\
+\xe8\x01\x00\xed\x0f[\x87' ))
+
+def GetMinusBitmap():
+ return wx.BitmapFromXPMData(GetMinusData())
+
+## NOTE: this was created using a newer version of img2py than the above
+import cStringIO
+def GetPointerData():
+ return zlib.decompress(
+'x\xda\xeb\x0c\xf0s\xe7\xe5\x92\xe2b``\xe0\xf5\xf4p\t\x02\xd2\xa2 \xcc\xc1\
+\x06$W\x8a/\x9d\x06\xa4X\x8a\x9d<C8\x80\xa0\x86#\xa5\x03\xc8\xcf\xf4tq\x0c\
+\xa9\x98s\xf4\x92\xa3\x10\x83 \x0f\x8b\xc3\xea?\xa6\xf9\xb3\xae\xaf\x9b\xbcj\
+\xef\x9a3-\x13\xba\x99o\xb1\xf07l\xcfYg5\xdbd\xf3\xdf\x0c\x91\xb2\x8b\x1e\
+\x81+fM\xf3[u \xe8\xb7N\xcd{\xbf\xfdG\x97\xf0\xaa~}P\xf0\xdb\xd8\xe2\xa9\xdf\
+\xec]\x8c\r\xbb\xdb\xbcN9\x08Y\x1d0\\\xb6\xf7\x9f\xd0\xaam\xbe\x0b\xeb\xdb\
+\x97\xfc\xbd\xfc8\xe3\x94\xfd\xdb\x7fs\xa4\xa7\x17\xf0\x9d;\x03\xb4\x94\xc1\
+\xd3\xd5\xcfe\x9dSB\x13\x00\xcbEE&' )
+
+def GetPointerBitmap():
+ return wx.BitmapFromImage(GetPointerImage())
+
+def GetPointerImage():
+ stream = cStringIO.StringIO(GetPointerData())
+ return wx.ImageFromStream(stream)
+
--- /dev/null
+"""
+This is the floatcanvas package. It provides two primary modules, and a
+support module.
+
+FloatCanvas.py contains the main FloatCanvas class, and it's supporting
+classes. NavCanvas.py contains a wrapper for the FloatCanvas that
+provides the canvas and a toolbar with tools that allow you to navigate
+the canvas (zooming, panning, etc.) Resources.py is a module that
+contains a few resources required by the FloatCanvas (icons, etc)
+
+The FloatCanvas is a high level window for drawing maps and anything
+else in an arbitrary coordinate system.
+
+The goal is to provide a convenient way to draw stuff on the screen
+without having to deal with handling OnPaint events, converting to pixel
+coordinates, knowing about wxWindows brushes, pens, and colors, etc. It
+also provides virtually unlimited zooming and scrolling
+
+I am using it for two things:
+1) general purpose drawing in floating point coordinates
+2) displaying map data in Lat-long coordinates
+
+If the projection is set to None, it will draw in general purpose
+floating point coordinates. If the projection is set to 'FlatEarth', it
+will draw a FlatEarth projection, centered on the part of the map that
+you are viewing. You can also pass in your own projection function.
+
+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.
+
+Bugs and Limitations: Lots: patches, fixes welcome
+
+For Map drawing: It ignores the fact that the world is, in fact, a
+sphere, so it will do strange things if you are looking at stuff near
+the poles or the date line. so far I don't have a need to do that, so I
+havn't bothered to add any checks for that yet.
+
+Zooming: I have set no zoom limits. What this means is that if you zoom
+in really far, you can get integer overflows, and get weird results. It
+doesn't seem to actually cause any problems other than weird output, at
+least when I have run it.
+
+Speed: I have done a couple of things to improve speed in this app. The
+one thing I have done is used NumPy Arrays to store the coordinates of
+the points of the objects. This allowed me to use array oriented
+functions when doing transformations, and should provide some speed
+improvement for objects with a lot of points (big polygons, polylines,
+pointsets).
+
+The real slowdown comes when you have to draw a lot of objects, because
+you have to call the wx.DC.DrawSomething call each time. This is plenty
+fast for tens of objects, OK for hundreds of objects, but pretty darn
+slow for thousands of objects.
+
+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:
+
+At this point, there are a full set of custom mouse events. They are
+just like the regular mouse events, but include an extra attribute:
+Event.GetCoords(), that returns the (x,y) position in world coordinates,
+as a length-2 NumPy vector of Floats.
+
+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.
+
+Check for updates at:
+http://home.comcast.net/~chrishbarker/FloatCanvas/
+
+Please let me know if you're using this!!!
+
+Contact me at:
+
+Chris.Barker@noaa.gov
+
+"""
+
+__version__ = "0.8.3"
+
+