More wxPytohn 2.5 info

I started writing a Migration Guide for wxPython 2.5 today. I thought I would share it with you guys to help give you a little more info about what changes are being done. There is still lots to be written, but some of that is still up in the air so I'll write about them in the Migration Guide when things settle a bit more.

MigrationGuide.txt (8.2 KB)

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn wrote:

I started writing a Migration Guide for wxPython 2.5 today. I thought I
would share it with you guys to help give you a little more info about
what changes are being done.

Thanks Robin, this is great!

New wxDC Methods

About these. Can you pass in any len-2 sequence where a wxPoint is
expected? For example:

    DrawPoint(self, point)

could I pass in a NumPy array like: DrawPoint(self, array([5,6]) ) ?
or would I have to do:

DrawPoint(self, wxPoint(array([5,6]) ) ) ?

or even worse:

DrawPoint(self, wxPoint(tuple(array([5,6]) ) ) ) ?

-Chris

-- --
Christopher Barker, Ph.D.
Oceanographer
                                                
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Robin,

I booted up Windows for the first time in a while yesterday, and tried
out the latest wxPython version.

It turns out that the floatcanvas in the demo is broken. It's all bugs
that I had fixed, but must have not gotten into the version you had. I
have no idea when you might do a next release, but I thought you should
have a functioning version. I've enclosed the main module:

floatcanvas.py

and the demo:

FloatCanvas.py

these versions work on my Win2k box fine. Sorry not to send a patch, I
don't have diff on Windows, and there were enough changes that I stopped
trying to figure out what changed.

Sorry I didn't get to this earlier.

Hmm. I'll make sure to update my ftp site as well.

-Chris

-- --
Christopher Barker, Ph.D.
Oceanographer
                                                
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

FloatCanvas.py (17.5 KB)

floatcanvas.py (52.7 KB)

This reminds me, I have an improved version of FancyText. You might want to grab the new version from http://starship.python.net/crew/hochberg/FancyText.py before the next release.

-tim

Chris Barker wrote:

···

Robin,

I booted up Windows for the first time in a while yesterday, and tried
out the latest wxPython version.

It turns out that the floatcanvas in the demo is broken. It's all bugs
that I had fixed, but must have not gotten into the version you had. I
have no idea when you might do a next release, but I thought you should
have a functioning version. I've enclosed the main module:

floatcanvas.py

and the demo:

FloatCanvas.py

these versions work on my Win2k box fine. Sorry not to send a patch, I
don't have diff on Windows, and there were enough changes that I stopped
trying to figure out what changed.

Sorry I didn't get to this earlier.

Hmm. I'll make sure to update my ftp site as well.

-Chris

-- --
Christopher Barker, Ph.D.
Oceanographer
                                               NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

------------------------------------------------------------------------

from wxPython.wx import *

## Stuff to integrate FloatCanvas into wxPython Demo
try:
   import Numeric
   haveNumeric = True
except ImportError:
   haveNumeric = False

if not haveNumeric:
   errorText = """\
The FloatCanvas requires the Numeric module:
You can get it at:
    http://sourceforge.net/projects/numpy
""" def runTest(frame, nb, log):
       dlg = wxMessageDialog(frame, errorText, 'Sorry', wxOK | wxICON_INFORMATION)
       dlg.ShowModal()
       dlg.Destroy()

   overview = ""

else:
   def runTest(frame, nb, log):
       """
       This method is used by the wxPython Demo Framework for integrating
       this demo with the rest.
       """
       win = DrawFrame(NULL, -1, "FloatCanvas Drawing Window",wxDefaultPosition,wxSize(500,500))
       frame.otherWin = win
       win.Show(True)

   from wxPython.lib import floatcanvas
   import wxPython.lib.colourdb
      ID_ABOUT_MENU = wxNewId() ID_EXIT_MENU = wxNewId() ID_ZOOM_TO_FIT_MENU = wxNewId()
   ID_DRAWTEST_MENU = wxNewId()
   ID_LINETEST_MENU = wxNewId()
   ID_DRAWMAP_MENU = wxNewId()
   ID_DRAWMAP2_MENU = wxNewId()
   ID_CLEAR_MENU = wxNewId()

   wxPython.lib.colourdb.updateColourDB()
   colors = wxPython.lib.colourdb.getColourList()

   LineStyles = floatcanvas.draw_object.LineStyleList.keys()
      class DrawFrame(wxFrame):
          """
          A frame used for the FloatCanvas Demo
              """
              def __init__(self,parent, id,title,position,size):
           wxFrame.__init__(self,parent, id,title,position, size)
                      ## Set up the MenuBar
                      MenuBar = wxMenuBar()
                      file_menu = wxMenu()
           file_menu.Append(ID_EXIT_MENU, "&Close","Close this frame")
           EVT_MENU(self, ID_EXIT_MENU, self.OnQuit)
           MenuBar.Append(file_menu, "&File")
                      draw_menu = wxMenu()
           draw_menu.Append(ID_DRAWTEST_MENU, "&Draw Test","Run a test of drawing random components")
           EVT_MENU(self, ID_DRAWTEST_MENU,self.DrawTest)
           draw_menu.Append(ID_LINETEST_MENU, "&Line Test","Run a test of drawing random lines")
           EVT_MENU(self, ID_LINETEST_MENU,self.LineTest)
           draw_menu.Append(ID_DRAWMAP_MENU, "Draw &Map","Run a test of drawing a map")
           EVT_MENU(self, ID_DRAWMAP_MENU,self.DrawMap)
           draw_menu.Append(ID_CLEAR_MENU, "&Clear","Clear the Canvas")
           EVT_MENU(self, ID_CLEAR_MENU,self.Clear)
           MenuBar.Append(draw_menu, "&Draw")
           
           view_menu.Append(ID_ZOOM_TO_FIT_MENU, "Zoom to &Fit","Zoom to fit the window")
           EVT_MENU(self, ID_ZOOM_TO_FIT_MENU,self.ZoomToFit)
           MenuBar.Append(view_menu, "&View")
                      help_menu = wxMenu()
           help_menu.Append(ID_ABOUT_MENU, "&About",
                                   "More information About this program")
           EVT_MENU(self, ID_ABOUT_MENU, self.OnAbout)
           MenuBar.Append(help_menu, "&Help")
                      self.SetMenuBar(MenuBar)
                      self.CreateStatusBar()
           self.SetStatusText("")
                      EVT_CLOSE(self, self.OnCloseWindow)
                      # Other event handlers:
           EVT_RIGHT_DOWN(self, self.RightButtonEvent)
                      # 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 =
                      return None
                  def RightButtonEvent(self,event):
           print "Right Button has been clicked in DrawFrame"
           print "coords are: %i, %i"%(event.GetX(),event.GetY())
           event.Skip()
                  def OnAbout(self, event):
           dlg = wxMessageDialog(self, "This is a small program to demonstrate\n"
                                                     "the use of the FloatCanvas\n",
                                                     "About Me", wxOK | wxICON_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.Canvas.Draw()
                  def OnQuit(self,event):
           self.Close(True)
                  def OnCloseWindow(self, event):
           self.Destroy()
                  def DrawTest(self,event):
           wxGetApp().Yield()
           import random
           import RandomArray
           Range = (-10,10)
                      Canvas = self.Canvas
           object_list = self.object_list
                      ## Random tests of everything:
                      # Rectangles
           for i in range(5):
               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]))
                            # Ellipses
           for i in range(5):
               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]))
                            # 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]))
                            # 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)
               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"))
                            # 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)
               self.object_list.append(self.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)
               self.object_list.append(self.Canvas.AddPolygon(points,
                                                              LineWidth = lw,
                                                              LineColor = colors[cl],
                                                              FillColor = colors[cf],
                                                              FillStyle = 'Solid'))
               
           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))
                      # 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.object_list.append(self.Canvas.AddText(String,x,y,Size = ts,ForeGround = colors[cf],Position = "cc"))
                          self.Canvas.ZoomToBB()
                  def DrawMap(self,event = None):
           wxGetApp().Yield()
           import os, time
       ## Test of Actual Map Data
           self.Clear()
           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()
           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.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) )
          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 =
           for i in range(2000):
               points = (random.randint(Range[0],Range[1]),
                        random.randint(Range[0],Range[1]),
                        random.randint(Range[0],Range[1]))
               linepoints.append(points)
               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) )
           start = time.clock()
           self.Canvas.ZoomToBB()
           print "It took %f seconds to draw %i lines"%(time.clock() - start,len(linepoints) )
      class DemoApp(wxApp):
       """
       How the demo works:
              Under the Draw menu, there are three options:
              *Draw Test: will put up a picture of a bunch of randomly generated
       objects, of each kind supported.
              *Draw Map: will draw a map of the world. Be patient, it is a big map,
       with a lot of data, and will take a while to load and draw (about 10 sec on my 450Mhz PIII). Redraws take about 2 sec. This demonstrates how the
       performance is not very good for large drawings.
              *Clear: Clears the Canvas.
              Once you have a picture drawn, you can zoom in and out and move about
       the picture. There is a tool bar with three tools that can be
       selected.
              The magnifying glass with the plus is the zoom in tool. Once selected,
       if you click the image, it will zoom in, centered on where you
       clicked. If you click and drag the mouse, you will get a rubber band
       box, and the image will zoom to fit that box when you release it.
              The magnifying glass with the minus is the zoom out tool. Once selected,
       if you click the image, it will zoom out, centered on where you
       clicked. (note that this takes a while when you are looking at the map,
       as it has a LOT of lines to be drawn. The image is double buffered, so
       you don't see the drawing in progress)
              The hand is the move tool. Once selected, if you click and drag on the
       image, it will move so that the part you clicked on ends up where you
       release the mouse. Nothing is changed while you are dragging. The
       drawing is too slow for that.
              I'd like the cursor to change as you change tools, but the stock
       wxCursors didn't include anything I liked, so I stuck with the
       pointer. Please let me know if you have any nice cursor images for me to
       use.
       
              -Chris Barker
              Chris.Barker@noaa.gov
          """
              def OnInit(self):
           frame = DrawFrame(NULL, -1, "FloatCanvas Demo App",wxDefaultPosition,wxSize(700,700))
              self.SetTopWindow(frame)
              return True
                  def Read_MapGen(filename,stats = 0,AllLines=0):
       """
       This function reads a MapGen Format file, and
       returns a list of NumPy arrays with the line segments in them.
              Each NumPy array in the list is an NX2 array of Python Floats.
              The demo should have come with a file, "world.dat" that is the
       shorelines of the whole world, in MapGen format.
              """
       import string
       from Numeric import array
       file = open(filename,'rt')
       data = file.readlines()
       data = map(string.strip,data)
              Shorelines =
       segment =
       for line in data:
           if line:
               if line == "# -b": #New segment beginning
                   if segment: Shorelines.append(array(segment))
                   segment =
               else:
                   segment.append(map(float,string.split(line)))
       if segment: Shorelines.append(array(segment))
              if stats:
           NumSegments = len(Shorelines)
           NumPoints = 0
           for segment in Shorelines:
               NumPoints = NumPoints + len(segment)
           AvgPoints = NumPoints / NumSegments
           print "Number of Segments: ", NumSegments
           print "Average Number of Points per segment: ",AvgPoints
       if AllLines:
           Lines =
           for segment in Shorelines:
               Lines.append(segment[0])
               for point in segment[1:-1]:
                   Lines.append(point)
               Lines.append(segment[-1])
           #print Shorelines
           #for point in Lines: print point
           return Lines
       else:
           return Shorelines
      ## for the wxPython demo:
   overview = floatcanvas.FloatCanvas.__doc__
     if __name__ == "__main__":
   if not haveNumeric:
       print errorText
   else:
       app = DemoApp(0)
       app.MainLoop()
   
------------------------------------------------------------------------

#!/usr/bin/env python2.3

from Numeric import array,Float,cos,pi,sum,minimum,maximum,Int32

from time import clock, sleep
from wxPython.wx import *
import types
import os

ID_ZOOM_IN_BUTTON = wxNewId()
ID_ZOOM_OUT_BUTTON = wxNewId()
ID_ZOOM_TO_FIT_BUTTON = wxNewId()
ID_MOVE_MODE_BUTTON = wxNewId()
ID_TEST_BUTTON = wxNewId()

ID_ABOUT_MENU = wxNewId() ID_EXIT_MENU = wxNewId() ID_ZOOM_TO_FIT_MENU = wxNewId()
ID_DRAWTEST_MENU = wxNewId()
ID_DRAWMAP_MENU = wxNewId()
ID_CLEAR_MENU = wxNewId()

ID_TEST = wxNewId()

### 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 wxBitmapFromXPMData(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 wxBitmapFromXPMData(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 wxBitmapFromXPMData(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 = wxNewEventType()
## ##FLOATCANVAS_EVENT_LEFT_UP = wxNewEventType()
## ##FLOATCANVAS_EVENT_RIGHT_DOWN = wxNewEventType()
## ##FLOATCANVAS_EVENT_RIGHT_UP = wxNewEventType()
## ##FLOATCANVAS_EVENT_MOUSE_OVER = wxNewEventType()

##WXFLOATCANVASEVENT = wxNewEventType()

##def EVT_WXFLOATCANVASEVENT( window, function ):

## """Your documentation here"""

## window.Connect( -1, -1, WXFLOATCANVASEVENT, function )

##class wxFloatCanvasObjectEvent(wxPyCommandEvent):
## def __init__(self, WindowID,Object):
## wxPyCommandEvent.__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") : wxTRANSPARENT_BRUSH,
               ("Blue","Solid") : wxBLUE_BRUSH,
               ("Green","Solid") : wxGREEN_BRUSH,
               ("White","Solid") : wxWHITE_BRUSH,
               ("Black","Solid") : wxBLACK_BRUSH,
               ("Grey","Solid") : wxGREY_BRUSH,
               ("MediumGrey","Solid") : wxMEDIUM_GREY_BRUSH,
               ("LightGrey","Solid") : wxLIGHT_GREY_BRUSH,
               ("Cyan","Solid") : wxCYAN_BRUSH,
               ("Red","Solid") : wxRED_BRUSH
                       }
       PenList = {
               (None,"Transparent",1) : wxTRANSPARENT_PEN,
               ("Green","Solid",1) : wxGREEN_PEN,
               ("White","Solid",1) : wxWHITE_PEN,
               ("Black","Solid",1) : wxBLACK_PEN,
               ("Grey","Solid",1) : wxGREY_PEN,
               ("MediumGrey","Solid",1) : wxMEDIUM_GREY_PEN,
               ("LightGrey","Solid",1) : wxLIGHT_GREY_PEN,
               ("Cyan","Solid",1) : wxCYAN_PEN,
               ("Red","Solid",1) : wxRED_PEN
               }
              FillStyleList = {
               "Transparent" : wxTRANSPARENT,
               "Solid" : wxSOLID,
               "BiDiagonalHatch": wxBDIAGONAL_HATCH,
               "CrossDiagHatch" : wxCROSSDIAG_HATCH,
               "FDiagonal_Hatch": wxFDIAGONAL_HATCH,
               "CrossHatch" : wxCROSS_HATCH,
               "HorizontalHatch": wxHORIZONTAL_HATCH,
               "VerticalHatch" : wxVERTICAL_HATCH
               }
              LineStyleList = {
               "Solid" : wxSOLID,
               "Transparent": wxTRANSPARENT,
               "Dot" : wxDOT,
               "LongDash" : wxLONG_DASH,
               "ShortDash" : wxSHORT_DASH,
               "DotDash" : wxDOT_DASH,
               }
              def SetBrush(self,FillColor,FillStyle):
           if FillColor is None or FillStyle is None:
               self.Brush = wxTRANSPARENT_BRUSH
               self.FillStyle = "Transparent"
           else:
               if not self.BrushList.has_key((FillColor,FillStyle)):
                   self.BrushList[(FillColor,FillStyle)] = wxBrush(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 = wxTRANSPARENT_PEN
               self.LineStyle = 'Transparent'
           else:
               if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
                   self.PenList[(LineColor,LineStyle,LineWidth)] = wxPen(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(wxTRANSPARENT_PEN)
                   # what's this for?> self.LineStyle = 'Transparent'
               if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
                   Pen = wxPen(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: wxSWISS, wxNORMAL, wxNORMAL.
   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 = wxFont(Size, wxMODERN, wxNORMAL, wxNORMAL, 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(wxSOLID)
           dc.SetTextBackground(self.BackGround)
       else:
           dc.SetBackgroundMode(wxTRANSPARENT)

       # compute the shift, and adjust the coordinates, if neccesary
       # This had to be put in here, becsuse there is no wxDC 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(wxPanel):
   """
   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 wxDC.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 = wxDefaultSize,
                ProjectionFun = None,
                BackgroundColor = "WHITE",
                Debug = 0,
                EnclosingFrame = None,
                UseToolbar = 1,
                UseBackground = 0,
                UseHitTest = 0):

       wxPanel.__init__( self, parent, id, wxDefaultPosition, 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 = wxBoxSizer(wxVERTICAL)
           box.Add(self.BuildToolbar(), 0, wxALL | wxALIGN_LEFT | wxGROW, 4)
                      self.DrawPanel = wxWindow(self,-1,wxDefaultPosition,wxDefaultSize,wxSUNKEN_BORDER)
           box.Add(self.DrawPanel,1,wxGROW)
                      box.Fit(self)
           self.SetAutoLayout(True)
           self.SetSizer(box)
       else:
           self.DrawPanel = self

       self.DrawPanel.BackgroundBrush = wxBrush(BackgroundColor,wxSOLID)

       self.Debug = Debug

       self.EnclosingFrame = EnclosingFrame
              EVT_PAINT(self.DrawPanel, self.OnPaint)
       EVT_SIZE(self.DrawPanel, self.OnSize)
              EVT_LEFT_DOWN(self.DrawPanel, self.LeftButtonEvent)
       EVT_LEFT_UP(self.DrawPanel, self.LeftButtonEvent)
       EVT_RIGHT_DOWN(self.DrawPanel, self.RightButtonEvent)
       EVT_MOTION(self.DrawPanel, self.LeftButtonEvent)
       
       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
       self.OnSize(None)

          def BuildToolbar(self):
       tb = wxToolBar(self,-1)
       self.ToolBar = tb
              tb.SetToolBitmapSize((23,23))
              tb.AddTool(ID_ZOOM_IN_BUTTON, GetPlusBitmap(), isToggle=true,shortHelpString = "Zoom In")
       EVT_TOOL(self, ID_ZOOM_IN_BUTTON, self.SetMode)
              tb.AddTool(ID_ZOOM_OUT_BUTTON, GetMinusBitmap(), isToggle=true,shortHelpString = "Zoom Out")
       EVT_TOOL(self, ID_ZOOM_OUT_BUTTON, self.SetMode)
              tb.AddTool(ID_MOVE_MODE_BUTTON, GetHandBitmap(), isToggle=true,shortHelpString = "Move")
       EVT_TOOL(self, ID_MOVE_MODE_BUTTON, self.SetMode)
              tb.AddSeparator()
              tb.AddControl(wxButton(tb, ID_ZOOM_TO_FIT_BUTTON, "Zoom To Fit",wxDefaultPosition, wxDefaultSize))
       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 = wxClientDC(self.DrawPanel)
                   dc.BeginDrawing()
                   dc.SetPen(wxPen('WHITE', 2,wxSHORT_DASH))
                   dc.SetBrush(wxTRANSPARENT_BRUSH)
                   dc.SetLogicalFunction(wxXOR)
                   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 = wxClientDC(self.DrawPanel)
                   dc.BeginDrawing()
                   dc.SetPen(wxPen('WHITE', 1,))
                   dc.SetBrush(wxTRANSPARENT_BRUSH)
                   dc.SetLogicalFunction(wxXOR)
                   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 = wxEmptyBitmap(self.PanelSize[0],self.PanelSize[1])
       if self.UseBackground:
           self._BackBuffer = wxEmptyBitmap(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 = wxBufferedPaintDC(self.DrawPanel, self._Buffer)
       dc = wxPaintDC(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 = wxClientDC(self.DrawPanel)
       ViewPortWorld = ( self.PixelToWorld((0,0)), self.PixelToWorld(self.PanelSize) )
       ViewPortBB = array( ( minimum.reduce(ViewPortWorld), maximum.reduce(ViewPortWorld) ) )
       if self.UseBackground:
           dc = wxMemoryDC()
           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:
                               ScreenDC.Blit(0, 0, self.PanelSize[0],self.PanelSize[1], 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:
                   ScreenDC.Blit(0, 0, self.PanelSize[0],self.PanelSize[1], dc, 0, 0)
           dc.EndDrawing()
       else: # not using a Background DC
           dc = wxMemoryDC()
           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:
                           ScreenDC.Blit(0, 0, self.PanelSize[0],self.PanelSize[1], 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
       ScreenDC.Blit(0, 0, self.PanelSize[0],self.PanelSize[1], 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(wxPen('WHITE', 2,wxSHORT_DASH))
           ScreenDC.SetBrush(wxTRANSPARENT_BRUSH)
           ScreenDC.SetLogicalFunction(wxXOR)
           ScreenDC.DrawRectangle(*self.PrevRBBox)
       elif self.PrevMoveBox:
           ScreenDC.SetPen(wxPen('WHITE', 1,))
           ScreenDC.SetBrush(wxTRANSPARENT_BRUSH)
           ScreenDC.SetLogicalFunction(wxXOR)
           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
       
------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-dev-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-dev-help@lists.wxwindows.org

Chris Barker wrote:

New wxDC Methods

About these. Can you pass in any len-2 sequence where a wxPoint is
expected?

Anything that satisfies a

  if (PySequence_Check(source) && PySequence_Length(source) == 2)

test will be converted to a wxPoint (or the other basic types) automatically.

For example:

   DrawPoint(self, point)

could I pass in a NumPy array like: DrawPoint(self, array([5,6]) ) ?

Yes, it should work like that. You can give it a try with something like window.SetPosition to make sure, as it expects a wxPoint so the typemap should do the conversion in that method too.

Here is the whole typemap helper function in case anyone is interested:

bool wxPoint_helper(PyObject* source, wxPoint** obj) {

     // If source is an object instance then it may already be the right type
     if (wxPySwigInstance_Check(source)) {
         wxPoint* ptr;
         if (! wxPyConvertSwigPtr(source, (void **)&ptr, wxT("wxPoint")))
             goto error;
         *obj = ptr;
         return TRUE;
     }
     // otherwise a length-2 sequence of integers is expected
     if (PySequence_Check(source) && PySequence_Length(source) == 2) {
         PyObject* o1 = PySequence_GetItem(source, 0);
         PyObject* o2 = PySequence_GetItem(source, 1);
         // This should really check for integers, not numbers -- but that would break code.
         if (!PyNumber_Check(o1) || !PyNumber_Check(o2)) {
             Py_DECREF(o1);
             Py_DECREF(o2);
             goto error;
         }
         **obj = wxPoint(PyInt_AsLong(o1), PyInt_AsLong(o2));
         Py_DECREF(o1);
         Py_DECREF(o2);
         return TRUE;
     }
  error:
     PyErr_SetString(PyExc_TypeError, "Expected a 2-tuple of integers or a wxPoint object.");
     return FALSE;
}

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Chris Barker wrote:

Robin,

I booted up Windows for the first time in a while yesterday, and tried
out the latest wxPython version.

It turns out that the floatcanvas in the demo is broken. It's all bugs
that I had fixed, but must have not gotten into the version you had. I
have no idea when you might do a next release, but I thought you should
have a functioning version. I've enclosed the main module:

Got them, thanks.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Tim Hochberg wrote:

This reminds me, I have an improved version of FancyText. You might want to grab the new version from http://starship.python.net/crew/hochberg/FancyText.py before the next release.

Got it. Thanks.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Hi Robin,

I took some time out to read this and I have a few questions:

1. Is this to be considered a 'complete list' of the major changes, or is the feature set for wxPython 3.0 still in flux at this point? Just curious. =)

2. You mentioned that public data members are now wrapped using property() - does this then mean that public data members now can be accessed directly rather than through Getters and setters? (i.e. mywindow.size, instead of GetSize/SetSize) If so I think this should get a little more attention in the final version of the Guide. <G>

3. I noticed in the latest version posted online that you use wx.core.Window as an example - does this mean that you're building wxPython using Multilib now? =)

4. What is the significance of the "new style classes" you mentioned? It sounds like you're referring to the new type/class system in Python 2.2+ but does this mean that when you check an object's type, it will return the proper class now? (I remember reading that in some cases, when you query the object's class type, it will return say wxWindow instead of wxMyWindowSubclass.)

That's all I can think of right now. Cool stuff!

Thanks,

Kevin

···

On Wednesday, October 8, 2003, at 12:59 PM, Robin Dunn wrote:

I started writing a Migration Guide for wxPython 2.5 today. I thought I would share it with you guys to help give you a little more info about what changes are being done. There is still lots to be written, but some of that is still up in the air so I'll write about them in the Migration Guide when things settle a bit more.

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

============================
wxPython 2.5 Migration Guide

This document will help explain some of the major changes in wxPython
2.5 and let you know what you need to do to adapt your programs to
those changes. Be sure to also check in the CHANGES.txt file like
usual to see info about the not so major changes and things that have
been added to wxPython.

Module Initialization
---------------------

The import-startup-bootstrap process employed by wxPython was changed
such that wxWindows and the underlying gui toolkit are **not**
initialized until the wxApp object is created (but before wxApp.OnInit
is called.) This was required because of some changes that were made
to the C++ wxApp class.

There are both benefits and potential problems with this change. The
benefits are that you can import wxPython without requiring access to
a GUI (for checking version numbers, etc.) and that in a
multi-threaded environment the thread that creates the app object will
now be the GUI thread instead of the one that imports wxPython. Some
potential problems are that the C++ side of the "stock-objects"
(wxBLUE_PEN, wxTheColourDatabase, etc.) are not initialized until the
wxApp object is created, so you should not use them until after you
have created you wxApp object. Also, you will probably not be able to
do any kind of GUI operation unless you first have created an app
object, (even on Windows where most anything was possible before.)

SWIG 1.3
--------

wxPython is now using SWIG 1.3 from CVS (with several of my own
customizations added that I hope to get folded back into the main SWIG
distribution.) This has some far reaching ramifications:

    All classes derive from object and so all are now "new-style
    classes"

    Public data members of the C++ classes are wrapped as Python
    properties using property().

    Static C++ methods are wrapped using the staticmethod()
    feature of Python and so are accessible as ClassName.MethodName
    as expected. They are still available as top level functions
    ClassName_MethodName as before.

    The relationship between the wxFoo and wxFooPtr classes have
    changed for the better. Specifically, all instances that you see
    will be wxFoo even if they are created internally using wxFooPtr,
    because wxFooPtr.__init__ will change the instance's __class__ as
    part of the initialization. If you have any code that checks
    class type using something like isinstance(obj, wxFooPtr) you will
    need to change it to isinstance(obj, wxFoo).

Binding Events
--------------

All of the EVT_* functions are now instances of the wxPyEventBinder
class. They have a __call__ method so they can still be used as
functions like before, but making them instances adds some
flexibility.

wxEvtHandler (the base class for wxWindow) now has a Bind method that
makes binding events to windows a little easier. Here is its
definition and docstring::

        def Bind(self, event, handler, source=None, id=wxID_ANY, id2=wxID_ANY):
            """
            Bind an event to an event handler.

              event One of the EVT_* objects that specifies the
                        type of event to bind.

              handler A callable object to be invoked when the event
                        is delivered to self. Pass None to disconnect an
                        event handler.

              source Sometimes the event originates from a different window
                        than self, but you still want to catch it in self. (For
                        example, a button event delivered to a frame.) By
                        passing the source of the event, the event handling
                        system is able to differentiate between the same event
                        type from different controls.

              id,id2 Used for menu IDs or for event types that require a
                        range of IDs

            """

I hope to be able to remove the need for using IDs even for menu
events too...

If you create your own custom event types and EVT_* functions, and you
want to be able to use them with the Bind method above then you should
change your EVT_* to be an instance of wxPyEventBinder instead of a
function. If you used to have something like this::

    myCustomEventType = wxNewEventType()
    def EVT_MY_CUSTOM_EVENT(win, id, func):
        win.Connect(id, -1, myCustomEventType, func)

Change it like so::

    myCustomEventType = wxNewEventType()
    EVT_MY_CUSTOM_EVENT = wxPyEventBinder(myCustomEventType, 1)

The second parameter is an integer in [0, 1, 2] that specifies the
number of IDs that are needed to be passed to Connect.

New wxDC Methods
----------------

Many of the Draw methods of wxDC have alternate forms in C++ that take
wxPoint or wxSize parameters (let's call these *Type A*) instead of the
individual x, y, width, height, etc. parameters (and we'll call these
*Type B*). In the rest of the library I normally made the *Type A* forms of
the methods be the default and had renamed the *Type B* forms of the
methods to some similar name. For example in wxWindow we have these
methods::

    SetSize(self, size) # Type A
    SetSizeWH(self, width, height) # Type B

For various reasons the new *Type A* methods in wxDC were never added
and the existing *Type B* methods renamed. Now that lots of other
things are also changing in wxPython that it has been decided that it
is time to do the method renaming in wxDC too, in order to be
consistent with the rest of the library. The methods in wxDC that are
affected are listed here::

    FloodFillXY(self, x, y, colour, style = wxFLOOD_SURFACE);
    FloodFill(self, point, colour, style = wxFLOOD_SURFACE)

    GetPixelXY(self, x, y)
    GetPixel(self, point)

    DrawLineXY(self, x1, y1, x2, y2)
    DrawLine(self, point1, point2)

    CrossHairXY(self, x, y)
    CrossHair(self, point)

    DrawArcXY(self, x1, y1, x2, y2, xc, yc)
    DrawArc(self, point1, point2, center)

    DrawCheckMarkXY(self, x, y, width, height)
    DrawCheckMark(self, rect)

    DrawEllipticArcXY(self, x, y, w, h, start_angle, end_angle)
    DrawEllipticArc(self, point, size, start_angle, end_angle)

    DrawPointXY(self, x, y)
    DrawPoint(self, point)

    DrawRectangleXY(self, x, y, width, height)
    DrawRectangle(self, point, size)
    DrawRectangleRect(self, rect)

    DrawRoundedRectangleXY(self, x, y, width, height, radius)
    DrawRoundedRectangle(self, point, size, radius)
    DrawRoundedRectangleRect(self, rect, radius)

    DrawCircleXY(self, x, y, radius)
    DrawCircle(self, point, radius)

    DrawEllipseXY(self, x, y, width, height)
    DrawEllipse(self, point, size)
    DrawEllipseRect(self, rect)

    DrawIconXY(self, icon, x, y)
    DrawIcon(self, icon, point)

    DrawBitmapXY(self, bmp, x, y, useMask = FALSE)
    DrawBitmap(self, bmp, point, useMask = FALSE)

    DrawTextXY(self, text, x, y)
    DrawText(self, text, point)

    DrawRotatedTextXY(self, text, x, y, angle)
    DrawRotatedText(self, text, point, angle)

    BlitXY(self, xdest, ydest, width, height, sourceDC, xsrc, ysrc,
           rop = wxCOPY, useMask = FALSE, xsrcMask = -1, ysrcMask = -1)
    Blit(self, destPt, size, sourceDC, srcPt,
         rop = wxCOPY, useMask = FALSE, srcPtMask = wxDefaultPosition)

If you have code that draws on a DC you **will** get errors because of
these changes, but it should be easy to fix the code. You can either
change the name of the *Type B* method called as shown above, or just
add parentheses around the parameters as needed to turn them into
tuples. For example, if you had this code before::

   dc.DrawRectangle(x, y, width, height)

You could just change it like this::

    dc.DrawRectangle((x, y), (width, height))

Or if you were already using a point and size::

   dc.DrawRectangle(p.x, p.y, s.width, s.height)

Then you can just change it like this::

   dc.DrawRectangle(p, s)

Building wxPython
-----------------

wxPython's setup.py script now expects to use existing libraries for
the contribs (gizmos, stc, xrc, etc.) rather than building local
copies of them. If you build your own copies of wxPython please be
aware that you now need to also build the ogl, stc, xrc, and gizmos
libraries in addition to the main wx lib. [[TODO: update the
BUILD.*.txt files too!]]

---------------------------------------------------------------------
To unsubscribe, e-mail: wxPython-dev-unsubscribe@lists.wxwindows.org
For additional commands, e-mail: wxPython-dev-help@lists.wxwindows.org

Kevin Ollivier wrote:

Hi Robin,

I took some time out to read this and I have a few questions:

1. Is this to be considered a 'complete list' of the major changes, or is the feature set for wxPython 3.0 still in flux at this point? Just curious. =)

There are a few more things I woudl like to get done before doing a release. I still need to get a lot of the old stuff converted over to SWIG 1.3 though (just a subset of the core is done right now, enough for me to test with and to redevelop some of the foundational code.)

2. You mentioned that public data members are now wrapped using property() - does this then mean that public data members now can be accessed directly rather than through Getters and setters? (i.e. mywindow.size, instead of GetSize/SetSize) If so I think this should get a little more attention in the final version of the Guide. <G>

No, currently it is just the public data memebers of the C++ classes. SWIG automatically generates getters/setters for them, and then uses property() to bind them together. The old SWIG took care of that with some very ugly __getattr__/__setattr__ implementations. See the Python wrapper for wxSize for example. From the programmers' perspective nothing has really changed, other than it being more efficient.

That being said, some of the work done for Borland adds some meta-data to the C++ classes that can be used by their builder to know how to get/set "properties" using the getters/setters. That same meta-info could be used to generate "size = property(GetSize, SetSize)" type of code for wxPython, (and by using the class metadata we will be sure that the Python and C++ properties match!) I don't plan on adding this for the first 2.5.x, but hope to be able to in the one after that. If somebody else wants to take a look at doing it please feel free.

3. I noticed in the latest version posted online that you use wx.core.Window as an example - does this mean that you're building wxPython using Multilib now? =)

I am, but the "core" above doesn't have anything to do with the "core" wxlib, (although you've caused me to think about that again.) Currently the core module will be everything that was in the wxPython.wx namespace in the old version.

4. What is the significance of the "new style classes" you mentioned? It sounds like you're referring to the new type/class system in Python 2.2+

Yes. See http://www.python.org/doc/2.2.3/whatsnew/sect-rellinks.html

but does this mean that when you check an object's type, it will return the proper class now? (I remember reading that in some cases, when you query the object's class type, it will return say wxWindow instead of wxMyWindowSubclass.)

Previously type(obj) would tell you that it was an instance type. Now it will tell you that it is of type wxMyWindowSubClass, (because classes are types now.) If you are refering to using obj.__class__ or something then it should have been doing what you expect already.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn wrote:

No, currently it is just the public data memebers of the C++ classes. SWIG automatically generates getters/setters for them, and then uses property() to bind them together. The old SWIG took care of that with some very ugly __getattr__/__setattr__ implementations. See the Python wrapper for wxSize for example. From the programmers' perspective nothing has really changed, other than it being more efficient.

I've updated the Migration Guide with a comment about this.

I've also put a copy of the SWIG generated .py modules (and also the compatibility modules generated by my build_renamers script) there for the curious to take a peek at. If you've used SWIG 1.3.x before you'll notice that there are a number of differences from the standard SWIG. The biggest is that all the crap for compatibility with classic classes is gone, making things leaner, meaner and much more efficient.

http://alldunn.com/wxPython/preview/2.5.x/

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Hi Robin,

I wonder if you can give your opinion on Boost.Python comparing to SWIG.

As well, will the Document/View modules be available in this 2.5 release?

Philip

Philip Yue wrote:

···

Hi Robin,

I wonder if you can give your opinion on Boost.Python comparing to SWIG.

As well, will the Document/View modules be available in this 2.5 release?

Philip

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Philip Yue wrote:

Hi Robin,

I wonder if you can give your opinion on Boost.Python comparing to SWIG.

It's been a long time since I looked at it so things have probably changed, but my impression then was that it would be lots more work to maintian wrapper code than it is with SWIG. Also there were some things that it couldn't do that I rely on (but I don't remember what they were.)

As well, will the Document/View modules be available in this 2.5 release?

I have no immediate plans on wrapping the C++ classes, although if/when I switch to using the new directors feature of SWIG that may change. In the meantime there is a Python-only port of those classes in development by a 3rd party that can be added once he has fixed some bugs.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

hi all,

has anyone attempted vtk/wxWindows/wxPython integration? how good is vtk comparing to other opensource 3D lib? tia.

philip

Philip Yue wrote:

hi all,

has anyone attempted vtk/wxWindows/wxPython integration?

Yes, it is included with VTK.

how good is vtk comparing to other opensource 3D lib? tia.

It would be better if this question was asked on the wxPython-users list because there are people there that actually use them.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

hi robin,

i read somewhere there is a process migrating wxPython into a wx namespace, such wx.Window etc. is this still in progress or when will the migration be complete? thanks

philip

Philip Yue wrote:

hi robin,

i read somewhere there is a process migrating wxPython into a wx
namespace, such wx.Window etc. is this still in progress or when will
the migration be complete? thanks

Yes, it is in progress now. There is a preview version of it in the latest 2.4 release where the symbols from the wxPython package are dynamically imported into the wx package and renamed to drop the leading wx.

In the 2.5 tree I am working on (among a ton of other things) completing the transition by moving the real classes and other things out of the wxPython package and into the wx package, with their leading wx prefix chopped off. There will also be a set of modules left in the wxPython package for compatibility that imports from wx puts the wx prefix back on the names.

You can take a look at http://alldunn.com/wxPython/preview/2.5.x/ for a sample of what the .py files in the two packages will look like, and there is also a preliminary Migration Guide there too.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!