wxEL_ALLOW_NEW,
wxEL_ALLOW_EDIT,
wxEL_ALLOW_DELETE,
+ wxEL_NO_REORDER,
+ wxEL_DEFAULT_STYLE
};
const wxString& label = wxPyEmptyString,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
- long style = wxEL_ALLOW_NEW | wxEL_ALLOW_EDIT | wxEL_ALLOW_DELETE,
+ long style = wxEL_DEFAULT_STYLE,
const wxString& name = wxPyEditableListBoxNameStr);
-
+ %RenameCtor(PreEditableListBox, wxEditableListBox());
+
+ bool Create(wxWindow *parent, wxWindowID id,
+ const wxString& label,
+ const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize,
+ long style = wxEL_DEFAULT_STYLE,
+ const wxString& name = wxEditableListBoxNameStr);
void SetStrings(const wxArrayString& strings);
wxTreeListItem *m_shiftItem; // item, where the shift key was pressed
wxTreeListItem *m_editItem; // item, which is currently edited
wxTreeListItem *m_selectItem; // current selected item, not with wxTR_MULTIPLE
-
+ wxTreeListItem *m_select_me;
+
int m_curColumn;
int m_btnWidth, m_btnWidth2;
// implementation
// ===========================================================================
+
+// ---------------------------------------------------------------------------
+// internal helpers
+// ---------------------------------------------------------------------------
+
+// check if the given item is under another one
+static bool IsDescendantOf(const wxTreeListItem *parent, const wxTreeListItem *item)
+{
+ while ( item )
+ {
+ if ( item == parent )
+ {
+ // item is a descendant of parent
+ return true;
+ }
+
+ item = item->GetItemParent();
+ }
+
+ return false;
+}
+
+
// ---------------------------------------------------------------------------
// wxTreeListRenameTimer (internal)
// ---------------------------------------------------------------------------
m_shiftItem = (wxTreeListItem*)NULL;
m_editItem = (wxTreeListItem*)NULL;
m_selectItem = (wxTreeListItem*)NULL;
-
+ m_select_me = (wxTreeListItem*)NULL;
+
m_curColumn = -1; // no current column
m_hasFocus = false;
}
wxTreeListItem *parent = item->GetItemParent();
+
+
+ // m_select_me records whether we need to select
+ // a different item, in idle time.
+ if ( m_select_me && IsDescendantOf(item, m_select_me) )
+ {
+ m_select_me = parent;
+ }
+
+ if ( IsDescendantOf(item, m_curItem) )
+ {
+ // Don't silently change the selection:
+ // do it properly in idle time, so event
+ // handlers get called.
+
+ // m_current = parent;
+ m_curItem = NULL;
+ m_select_me = parent;
+ }
+
+ // remove the item from the tree
if (parent) {
parent->GetChildren().Remove (item); // remove by value
}
SendDeleteEvent (item);
if (m_selectItem == item) m_selectItem = (wxTreeListItem*)NULL;
item->DeleteChildren (this);
+
+ if (item == m_select_me)
+ m_select_me = NULL;
+
delete item;
}
if (unselect_others) {
m_selectItem = (item->IsSelected())? item: (wxTreeListItem*)NULL;
}
-
}
// send event to user code
m_curItem = (wxTreeListItem*)newItem.m_pItem; // make the new item the current item
RefreshLine (oldItem);
}
-
}
wxTreeItemId wxTreeListMainWindow::HitTest (const wxPoint& point, int& flags, int& column) {
* we actually redraw the tree when everything is over */
if (!m_dirty) return;
-
m_dirty = false;
+ // Check if we need to select the root item
+ // because nothing else has been selected.
+ // Delaying it means that we can invoke event handlers
+ // as required, when a first item is selected.
+ if (!m_owner->HasFlag(wxTR_MULTIPLE) && !m_owner->GetSelection().IsOk())
+ {
+ if (m_select_me)
+ m_owner->SelectItem(m_select_me);
+ else if (m_owner->GetRootItem().IsOk())
+ m_owner->SelectItem(m_owner->GetRootItem());
+ m_select_me = NULL;
+ m_curItem = (wxTreeListItem*)m_owner->GetSelection().m_pItem;
+
+ }
+
CalculatePositions();
Refresh();
AdjustMyScrollbars();
self.Bind(wx.EVT_MENU, self.OnChangeContentPane, id=ID_TextContent)
self.Bind(wx.EVT_MENU, self.OnChangeContentPane, id=ID_SizeReportContent)
self.Bind(wx.EVT_MENU, self.OnChangeContentPane, id=ID_HTMLContent)
- self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT)
+ self.Bind(wx.EVT_MENU, self.OnExit, id=wx.ID_EXIT)
self.Bind(wx.EVT_MENU, self.OnAbout, id=ID_About)
self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_TransparentHint)
def OnClose(self, event):
-
self._mgr.UnInit()
+ del self._mgr
self.Destroy()
- event.Skip()
+ def OnExit(self, event):
+ self.Close()
def OnAbout(self, event):
title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
title.SetForegroundColour("blue")
- self.cp = cp = wx.CollapsiblePane(self, label=label1)
+ self.cp = cp = wx.CollapsiblePane(self, label=label1,
+ style=wx.CP_DEFAULT_STYLE|wx.CP_NO_TLW_RESIZE)
self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnPaneChanged, cp)
self.MakePaneContent(cp.GetPane())
return shape
return None
- # Remove a shape from the display
- def EraseShape(self, shape, dc):
- r = shape.GetRect()
- dc.SetClippingRect(r)
- self.TileBackground(dc)
- self.DrawShapes(dc)
- dc.DestroyClippingRegion()
# Clears the background, then redraws it. If the DC is passed, then
# we only do so in the area so designated. Otherwise, it's the whole thing.
def OnEraseBackground(self, evt):
dc = evt.GetDC()
-
if not dc:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
if dx <= tolerance and dy <= tolerance:
return
- # erase the shape since it will be drawn independently now
- dc = wx.ClientDC(self)
+ # refresh the area of the window where the shape was so it
+ # will get erased.
self.dragShape.shown = False
- self.EraseShape(self.dragShape, dc)
-
+ self.RefreshRect(self.dragShape.GetRect(), True)
+ self.Update()
if self.dragShape.text:
self.dragImage = wx.DragString(self.dragShape.text,
+#!/usr/bin/env python
-
-#print "running:", wx.__version__
-##First, make sure Numeric or numarray can be imported.
try:
- import Numeric
- import RandomArray
- haveNumeric = True
+ import numpy as N
+ import numpy.random as RandomArray
+ haveNumpy = True
+ #print "Using numpy, version:", N.__version__
except ImportError:
- # Numeric isn't there, let's try numarray
- try:
- import numarray as Numeric
- import numarray.random_array as RandomArray
- haveNumeric = True
- except ImportError:
- # numarray isn't there either
- haveNumeric = False
- errorText = (
- "The FloatCanvas requires either the Numeric or numarray module\n\n"
- "You can get them at:\n"
- "http://sourceforge.net/projects/numpy\n\n"
- "NOTE: The Numeric module is substantially faster than numarray for this\n"
- "purpose, if you have lots of objects\n"
- )
+ # numpy isn't there
+ haveNumpy = False
+ errorText = (
+ "The FloatCanvas requires the numpy module, version 1.* \n\n"
+ "You can get info about it at:\n"
+ "http://numpy.scipy.org/\n\n"
+ )
#---------------------------------------------------------------------------
item = draw_menu.Append(-1, "&Arrows","Run a test of Arrows")
self.Bind(wx.EVT_MENU, self.ArrowTest, item)
- item = draw_menu.Append(-1, "&Hide","Run a test of the Show() Hide() Show() and methods")
+ item = draw_menu.Append(-1, "&ArrowLine Test","Run a test of drawing Arrow Lines")
+ self.Bind(wx.EVT_MENU, self.ArrowLineTest, item)
+
+ item = draw_menu.Append(-1, "&Hide","Run a test of hiding and showing objects")
self.Bind(wx.EVT_MENU, self.HideTest, item)
MenuBar.Append(draw_menu, "&Tests")
# Add the Canvas
- self.Canvas = NavCanvas.NavCanvas(self,
- Debug = 0,
- BackgroundColor = "DARK SLATE BLUE")
+ NC = NavCanvas.NavCanvas(self,
+ Debug = 0,
+ BackgroundColor = "DARK SLATE BLUE")
+
+ self.Canvas = NC.Canvas # reference the contained FloatCanvas
self.MsgWindow = wx.TextCtrl(self, wx.ID_ANY,
"Look Here for output from events\n",
##Create a sizer to manage the Canvas and message window
MainSizer = wx.BoxSizer(wx.VERTICAL)
- MainSizer.Add(self.Canvas, 4, wx.EXPAND)
+ MainSizer.Add(NC, 4, wx.EXPAND)
MainSizer.Add(self.MsgWindow, 1, wx.EXPAND | wx.ALL, 5)
self.SetSizer(MainSizer)
- wx.EVT_CLOSE(self, self.OnCloseWindow)
+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
- FloatCanvas.EVT_MOTION(self.Canvas, self.OnMove )
+ self.Canvas.Bind(FloatCanvas.EVT_MOTION, self.OnMove)
+ self.Canvas.Bind(FloatCanvas.EVT_MOUSEWHEEL, self.OnWheel)
self.EventsAreBound = False
def BindAllMouseEvents(self):
if not self.EventsAreBound:
## Here is how you catch FloatCanvas mouse events
- FloatCanvas.EVT_LEFT_DOWN(self.Canvas, self.OnLeftDown )
- FloatCanvas.EVT_LEFT_UP(self.Canvas, self.OnLeftUp )
- FloatCanvas.EVT_LEFT_DCLICK(self.Canvas, self.OnLeftDouble )
+ self.Canvas.Bind(FloatCanvas.EVT_LEFT_DOWN, self.OnLeftDown)
+ self.Canvas.Bind(FloatCanvas.EVT_LEFT_UP, self.OnLeftUp)
+ self.Canvas.Bind(FloatCanvas.EVT_LEFT_DCLICK, self.OnLeftDouble)
- FloatCanvas.EVT_MIDDLE_DOWN(self.Canvas, self.OnMiddleDown )
- FloatCanvas.EVT_MIDDLE_UP(self.Canvas, self.OnMiddleUp )
- FloatCanvas.EVT_MIDDLE_DCLICK(self.Canvas, self.OnMiddleDouble )
+ self.Canvas.Bind(FloatCanvas.EVT_MIDDLE_DOWN, self.OnMiddleDown)
+ self.Canvas.Bind(FloatCanvas.EVT_MIDDLE_UP, self.OnMiddleUp)
+ self.Canvas.Bind(FloatCanvas.EVT_MIDDLE_DCLICK, self.OnMiddleDouble)
- FloatCanvas.EVT_RIGHT_DOWN(self.Canvas, self.OnRightDown )
- FloatCanvas.EVT_RIGHT_UP(self.Canvas, self.OnRightUp )
- FloatCanvas.EVT_RIGHT_DCLICK(self.Canvas, self.OnRightDouble )
+ self.Canvas.Bind(FloatCanvas.EVT_RIGHT_DOWN, self.OnRightDown)
+ self.Canvas.Bind(FloatCanvas.EVT_RIGHT_UP, self.OnRightUp)
+ self.Canvas.Bind(FloatCanvas.EVT_RIGHT_DCLICK, self.OnRightDouble)
- FloatCanvas.EVT_MOUSEWHEEL(self.Canvas, self.OnWheel )
self.EventsAreBound = True
+
def UnBindAllMouseEvents(self):
## Here is how you unbind FloatCanvas mouse events
- FloatCanvas.EVT_LEFT_DOWN(self.Canvas, None )
- FloatCanvas.EVT_LEFT_UP(self.Canvas, None )
- FloatCanvas.EVT_LEFT_DCLICK(self.Canvas, None)
-
- FloatCanvas.EVT_MIDDLE_DOWN(self.Canvas, None )
- FloatCanvas.EVT_MIDDLE_UP(self.Canvas, None )
- FloatCanvas.EVT_MIDDLE_DCLICK(self.Canvas, None )
-
- FloatCanvas.EVT_RIGHT_DOWN(self.Canvas, None )
- FloatCanvas.EVT_RIGHT_UP(self.Canvas, None )
- FloatCanvas.EVT_RIGHT_DCLICK(self.Canvas, None )
-
- FloatCanvas.EVT_MOUSEWHEEL(self.Canvas, None )
- FloatCanvas.EVT_LEFT_DOWN(self.Canvas, None )
- FloatCanvas.EVT_LEFT_UP(self.Canvas, None )
- FloatCanvas.EVT_LEFT_DCLICK(self.Canvas, None)
-
- FloatCanvas.EVT_MIDDLE_DOWN(self.Canvas, None )
- FloatCanvas.EVT_MIDDLE_UP(self.Canvas, None )
- FloatCanvas.EVT_MIDDLE_DCLICK(self.Canvas, None )
+ self.Canvas.Unbind(FloatCanvas.EVT_LEFT_DOWN)
+ self.Canvas.Unbind(FloatCanvas.EVT_LEFT_UP)
+ self.Canvas.Unbind(FloatCanvas.EVT_LEFT_DCLICK)
- FloatCanvas.EVT_RIGHT_DOWN(self.Canvas, None )
- FloatCanvas.EVT_RIGHT_UP(self.Canvas, None )
- FloatCanvas.EVT_RIGHT_DCLICK(self.Canvas, None )
+ self.Canvas.Unbind(FloatCanvas.EVT_MIDDLE_DOWN)
+ self.Canvas.Unbind(FloatCanvas.EVT_MIDDLE_UP)
+ self.Canvas.Unbind(FloatCanvas.EVT_MIDDLE_DCLICK)
- FloatCanvas.EVT_MOUSEWHEEL(self.Canvas, None )
+ self.Canvas.Unbind(FloatCanvas.EVT_RIGHT_DOWN)
+ self.Canvas.Unbind(FloatCanvas.EVT_RIGHT_UP)
+ self.Canvas.Unbind(FloatCanvas.EVT_RIGHT_DCLICK)
self.EventsAreBound = False
def PrintCoords(self,event):
- #print "coords are: %s"%(event.Coords,)
- #print "pixel coords are: %s\n"%(event.GetPosition(),)
self.Log("coords are: %s"%(event.Coords,))
self.Log("pixel coords are: %s\n"%(event.GetPosition(),))
self.PrintCoords(event)
def OnRightUp(self, event):
- self.Log("RightDown")
+ self.Log("RightUp")
self.PrintCoords(event)
def OnRightDouble(self, event):
Updates the status bar with the world coordinates
"""
self.SetStatusText("%.2f, %.2f"%tuple(event.Coords))
+ event.Skip()
def OnAbout(self, event):
dlg = wx.MessageDialog(self,
def Clear(self,event = None):
self.UnBindAllMouseEvents()
- self.Canvas.ClearAll()
- self.Canvas.SetProjectionFun(None)
+ self.Canvas.InitAll()
self.Canvas.Draw()
def OnQuit(self,event):
self.Destroy()
def DrawTest(self,event=None):
- wx.GetApp().Yield()
+ """
+ This demo draws a few of everything
+
+ """
+
+ wx.GetApp().Yield(True)
Range = (-10,10)
colors = self.colors
self.BindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
+ Canvas.InitAll()
+ #
+ ## these set the limits for how much you can zoom in and out
+ Canvas.MinScale = 14
+ Canvas.MaxScale = 500
+
############# Random tests of everything ##############
LineColor = colors[random.randint(0,len(colors)-1)],
ArrowHeadAngle = random.uniform(20,90))
+ # ArrowLines
+ for i in range(5):
+ points = []
+ for j in range(random.randint(2,10)):
+ point = (random.randint(Range[0],Range[1]),random.randint(Range[0],Range[1]))
+ points.append(point)
+ lw = random.randint(1,10)
+ cf = random.randint(0,len(colors)-1)
+ cl = random.randint(0,len(colors)-1)
+ Canvas.AddArrowLine(points, LineWidth = lw, LineColor = colors[cl], ArrowHeadSize= 16)
+
+
Canvas.ZoomToBB()
def TestAnimation(self,event=None):
having to re-draw the whole background.
"""
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
Range = (-10,10)
self.Range = Range
self.UnBindAllMouseEvents()
Canvas = self.Canvas
-
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
+ Canvas.InitAll()
## Random tests of everything:
colors = self.colors
self.Timer.Start(self.FrameDelay)
#print "Did %i frames in %f seconds"%(N, (time.time() - start) )
- def TestHitTest(self,event=None):
- wx.GetApp().Yield()
+ def TestHitTest(self, event=None):
+ wx.GetApp().Yield(True)
self.UnBindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
+ Canvas.InitAll()
#Add a Hit-able rectangle
w, h = 60, 20
dx = 80
dy = 40
x, y = 20, 20
- FontSize = 8
+ FontSize = 10
#Add one that is not HitAble
Canvas.AddRectangle((x,y), (w, h), LineWidth = 2)
R.Name = color
R.Bind(FloatCanvas.EVT_FC_ENTER_OBJECT, self.RectMouseOver)
R.Bind(FloatCanvas.EVT_FC_LEAVE_OBJECT, self.RectMouseLeave)
- Canvas.AddText("Mouse ENter&Leave", (x, y), Size = FontSize, Position = "bl")
+ Canvas.AddText("Mouse Enter&Leave", (x, y), Size = FontSize, Position = "bl")
Canvas.AddText(R.Name, (x, y+h), Size = FontSize, Position = "tl")
x = 20
x += dx
color = "SEA GREEN"
- Points = Numeric.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), Numeric.Float)
+ Points = N.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), N.float_)
R = Canvas.AddPolygon(Points, LineWidth = 2, FillColor = color)
R.Name = color + " Polygon"
R.Bind(FloatCanvas.EVT_FC_RIGHT_DOWN, self.RectGotHitRight)
x += dx
color = "Red"
- Points = Numeric.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), Numeric.Float)
+ Points = N.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), N.float_)
R = Canvas.AddPointSet(Points, Diameter = 4, Color = color)
R.Name = "PointSet"
R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.PointSetGotHit)
x += dx
color = "Cyan"
Point = (x + w/2, y)
- #Points = Numeric.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), Numeric.Float)
+ #Points = N.array(( (x, y), (x, y+2.*h/3), (x+w, y+h), (x+w, y+h/2.), (x + 2.*w/3, y+h/2.), (x + 2.*w/3,y) ), N.float_)
R = Canvas.AddSquarePoint(Point, Size = 8, Color = color)
R.Name = "SquarePoint"
R.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RectGotHit)
self.Canvas.ZoomToBB()
def TestHitTestForeground(self,event=None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
self.UnBindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
+ Canvas.InitAll()
#Add a Hitable rectangle
w, h = 60, 20
def TestText(self, event= None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
self.BindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
+ Canvas.InitAll()
+
+ DefaultSize = 12
Point = (3, 0)
## Add a non-visible rectangle, just to get a Bounding Box
# Text
String = "Some text"
- self.Canvas.AddText("Top Left",Point,Size = 14,Color = "Yellow",BackgroundColor = "Blue", Position = "tl")
- self.Canvas.AddText("Bottom Left",Point,Size = 14,Color = "Cyan",BackgroundColor = "Black",Position = "bl")
- self.Canvas.AddText("Top Right",Point,Size = 14,Color = "Black",BackgroundColor = "Cyan",Position = "tr")
- self.Canvas.AddText("Bottom Right",Point,Size = 14,Color = "Blue",BackgroundColor = "Yellow",Position = "br")
+ self.Canvas.AddText("Top Left",Point,Size = DefaultSize,Color = "Yellow",BackgroundColor = "Blue", Position = "tl")
+ self.Canvas.AddText("Bottom Left",Point,Size = DefaultSize,Color = "Cyan",BackgroundColor = "Black",Position = "bl")
+ self.Canvas.AddText("Top Right",Point,Size = DefaultSize,Color = "Black",BackgroundColor = "Cyan",Position = "tr")
+ self.Canvas.AddText("Bottom Right",Point,Size = DefaultSize,Color = "Blue",BackgroundColor = "Yellow",Position = "br")
Canvas.AddPointSet((Point), Color = "White", Diameter = 2)
Point = (3, 2)
Canvas.AddPointSet((Point), Color = "White", Diameter = 2)
- self.Canvas.AddText("Top Center",Point,Size = 14,Color = "Black",Position = "tc")
- self.Canvas.AddText("Bottom Center",Point,Size = 14,Color = "White",Position = "bc")
+ self.Canvas.AddText("Top Center",Point,Size = DefaultSize,Color = "Black",Position = "tc")
+ self.Canvas.AddText("Bottom Center",Point,Size = DefaultSize,Color = "White",Position = "bc")
Point = (3, 4)
Canvas.AddPointSet((Point), Color = "White", Diameter = 2)
- self.Canvas.AddText("Center Right",Point,Size = 14,Color = "Black",Position = "cr")
- self.Canvas.AddText("Center Left",Point,Size = 14,Color = "Black",Position = "cl")
+ self.Canvas.AddText("Center Right",Point,Size = DefaultSize,Color = "Black",Position = "cr")
+ self.Canvas.AddText("Center Left",Point,Size = DefaultSize,Color = "Black",Position = "cl")
Point = (3, -2)
Canvas.AddPointSet((Point), Color = "White", Diameter = 2)
self.Canvas.AddText("Center Center",
- Point, Size = 14,
+ Point, Size = DefaultSize,
Color = "Black",
Position = "cc")
self.Canvas.AddText("ROMAN ITALIC BOLD Font", (-10, -5), Family = wx.ROMAN, Weight=wx.BOLD, Style=wx.ITALIC)
# NOTE: this font exists on my Linux box..who knows were else you'll find it!
- Font = wx.Font(20, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "zapf chancery")
- self.Canvas.AddText("zapf chancery Font", (-10, -6), Font = Font)
+ Font = wx.Font(20, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "helvetica")
+ self.Canvas.AddText("Helvetica Italic", (-10, -6), Font = Font)
self.Canvas.ZoomToBB()
def TestScaledText(self, event= None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
self.BindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
+ Canvas.InitAll()
Point = (0, 0)
Canvas.AddPointSet((x,0), Color = "White", Diameter = 4)
- # NOTE: this font exists on my Linux box..who knows were else you'll find it!
+ # NOTE: this font exists on my OS-X.who knows were else you'll find it!
Point = (-100, 50)
- Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "zapf chancery")
- T = self.Canvas.AddScaledText("zapf chancery Font", Point, Size = 20, Font = Font, Position = 'bc')
+ Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "helvetica")
+ T = self.Canvas.AddScaledText("Helvetica Italic", Point, Size = 20, Font = Font, Position = 'bc')
Point = (-50, -50)
- Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "bookman")
- T = self.Canvas.AddScaledText("Bookman Font", Point, Size = 8, Font = Font)
+ Font = wx.Font(12, wx.DEFAULT, wx.ITALIC, wx.NORMAL, False, "times")
+ T = self.Canvas.AddScaledText("Times Font", Point, Size = 8, Font = Font)
self.Canvas.ZoomToBB()
def TestScaledTextBox(self, event= None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
self.UnBindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
+ Canvas.InitAll()
Point = (45,40)
Box = Canvas.AddScaledTextBox("A Two Line\nString",
Family = wx.ROMAN,
Style = wx.NORMAL,
Weight = wx.NORMAL,
- Underline = False,
+ Underlined = False,
Position = 'br',
Alignment = "left",
InForeground = False)
Family = wx.TELETYPE,
Style = wx.NORMAL,
Weight = wx.NORMAL,
- Underline = False,
+ Underlined = False,
Position = 'cr',
Alignment = "left",
InForeground = False)
Family = wx.TELETYPE,
Style = wx.NORMAL,
Weight = wx.NORMAL,
- Underline = False,
+ Underlined = False,
Position = 'cl',
Alignment = "left",
InForeground = False)
Family = wx.TELETYPE,
Style = wx.NORMAL,
Weight = wx.NORMAL,
- Underline = False,
+ Underlined = False,
Position = 'tc',
Alignment = "left",
InForeground = False)
Family = wx.TELETYPE,
Style = wx.NORMAL,
Weight = wx.NORMAL,
- Underline = False,
+ Underlined = False,
Position = 'bc',
Alignment = "left",
InForeground = False)
Family = wx.ROMAN,
Alignment = "right"
)
- Point = Numeric.array((100, -20), Numeric.Float)
+ Point = N.array((100, -20), N.float_)
Box = Canvas.AddScaledTextBox("Here is even more auto wrapped text. This time the line spacing is set to 0.8. \n\nThe Padding is set to 0.",
Point,
Size = 3,
)
Canvas.AddPoint(Point, "Red", 2)
- Point = Numeric.array((0, -40), Numeric.Float)
- # Point = Numeric.array((0, 0), Numeric.Float)
+ Point = N.array((0, -40), N.float_)
+ # Point = N.array((0, 0), N.float_)
for Position in ["tl", "bl", "tr", "br"]:
# for Position in ["br"]:
Box = Canvas.AddScaledTextBox("Here is a\nfour liner\nanother line\nPosition=%s"%Position,
)
Canvas.AddPoint(Point, "Red", 4)
- Point = Numeric.array((-20, 60), Numeric.Float)
+ Point = N.array((-20, 60), N.float_)
Box = Canvas.AddScaledTextBox("Here is some\ncentered\ntext",
Point,
Size = 4,
LineSpacing = 0.8
)
- Point = Numeric.array((-20, 20), Numeric.Float)
+ Point = N.array((-20, 20), N.float_)
Box = Canvas.AddScaledTextBox("Here is some\nright aligned\ntext",
Point,
Size = 4,
LineSpacing = 0.8
)
- Point = Numeric.array((100, -60), Numeric.Float)
+ Point = N.array((100, -60), N.float_)
Box = Canvas.AddScaledTextBox("Here is some auto wrapped text. This time it is centered, rather than right aligned.\n\nThe Padding is set to 2.",
Point,
Size = 3,
self.Log("I'm the TextBox")
def TestBitmap(self, event= None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
self.UnBindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
-
+ Canvas.InitAll()
+
Canvas.AddRectangle((10, 20),
(400, 100),
LineWidth = 3,
self.Canvas.ZoomToBB()
def DrawMap(self,event = None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
import os, time
+
+ self.Canvas.InitAll()
+ self.Canvas.SetProjectionFun("FlatEarth")
self.BindAllMouseEvents()
## Test of Actual Map Data
- self.Canvas.ClearAll()
- self.Canvas.SetProjectionFun("FlatEarth")
#start = time.clock()
self.Log("Loading Map from a File")
- wx.GetApp().Yield() # so log text will get displayed now.
+ wx.GetApp().Yield(True) # so log text will get displayed now.
Shorelines = self.Read_MapGen(os.path.join("data",'world.dat'),stats = 0)
#print "It took %f seconds to load %i shorelines"%(time.clock() - start,len(Shorelines) )
#start = time.clock()
#print "It took %f seconds to draw %i shorelines"%(time.clock() - start,len(Shorelines) )
+
def LineTest(self,event = None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
import os, time
# import random
colors = self.colors
Range = (-10,10)
## Test of drawing lots of lines
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
- #start = time.clock()
+ Canvas.InitAll()
+ #start = time.clock()
linepoints = []
linecolors = []
linewidths = []
Canvas.ZoomToBB()
#print "It took %f seconds to draw %i lines"%(time.clock() - start,len(linepoints) )
+ def ArrowLineTest(self,event = None):
+ wx.GetApp().Yield(True)
+ Canvas = self.Canvas
+ Canvas.InitAll()
+ # import os, time
+## import random
+ Range = (-100,100)
+ colors = self.colors
+
+ # 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,4)
+ cf = random.randint(0,len(colors)-1)
+ cl = random.randint(0,len(colors)-1)
+ al = random.randint(8,20)
+ aa = random.randint(20,90)
+ Canvas.AddArrowLine(points,
+ LineWidth = lw,
+ LineColor = colors[cl],
+ ArrowHeadSize = al,
+ ArrowHeadAngle = aa)
+
+ Canvas.ZoomToBB()
+
def SpeedTest(self,event=None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
BigRange = (-1000,1000)
colors = self.colors
self.UnBindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
-
+ Canvas.InitAll()
+
# Pointset
coords = []
for i in range(1000):
Canvas.ZoomToBB()
def PropertiesChangeTest(self,event=None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
Range = (-10,10)
colors = self.colors
self.UnBindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
-
+ Canvas.InitAll()
+
self.ColorObjectsAll = []
self.ColorObjectsLine = []
self.ColorObjectsColor = []
self.Canvas.Draw(Force = True)
def ArrowTest(self,event=None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
self.UnBindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
-
+ Canvas.InitAll()
+ Canvas.MinScale = 15
+ Canvas.MaxScale = 30
+
# put in a rectangle to get a bounding box
Canvas.AddRectangle((0,0), (20,20), LineColor = None)
Canvas.AddText("Clickable Arrow", (4,18), Position = "bc")
Arrow = Canvas.AddArrow((4,18), 80, Direction = 90 ,LineWidth = 3, LineColor = "Red", ArrowHeadAngle = 30)
+ Arrow.HitLineWidth = 6
Arrow.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.ArrowClicked)
- Canvas.AddText("Changable Arrow", (16,4), Position = "cc")
+ Canvas.AddText("Changable Arrow: try clicking it", (16,4), Position = "tc")
self.RotArrow = Canvas.AddArrow((16,4), 80, Direction = 0 ,LineWidth = 3, LineColor = "Green", ArrowHeadAngle = 30)
+ self.RotArrow.HitLineWidth = 6
self.RotArrow.Bind(FloatCanvas.EVT_FC_LEFT_DOWN, self.RotateArrow)
Canvas.ZoomToBB()
def ArrowClicked(self,event):
- print "The Arrow was Clicked"
+ self.Log("The Arrow was Clicked")
def RotateArrow(self,event):
##print "The Changeable Arrow was Clicked"
self.Canvas.Draw(Force = True)
def HideTest(self, event=None):
- wx.GetApp().Yield()
+ wx.GetApp().Yield(True)
self.UnBindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
-
+ Canvas.InitAll()
+
Range = (-10,10)
# Create a couple random Polygons
def HidePoly(self, Button):
Poly = Button.HidePoly
+
if Poly.Visible:
- Poly.Hide()
+ Poly.Visible = False
Button.SetText(Button.String.replace("Hide","Show"))
else:
- Poly.Show()
+ Poly.Visible = True
Button.SetText(Button.String.replace("Show", "Hide"))
self.Canvas.Draw(True)
+
def TempTest(self, event= None):
- wx.GetApp().Yield()
+ """
+
+ This is the start of a poly editor test, but it's not complete
+ so you can only run it through a command line flag:
+
+ python FloatCanvasDemo.py --temp
+
+ """
+
+ wx.GetApp().Yield(True)
self.UnBindAllMouseEvents()
Canvas = self.Canvas
- Canvas.ClearAll()
- Canvas.SetProjectionFun(None)
-
+ Canvas.InitAll()
+
Range = (-10,10)
# Create a random Polygon
Canvas.ZoomToBB()
def SelectPoly(self, Object):
- print "In SelectPoly"
Canvas = self.Canvas
if Object is self.SelectedPoly:
pass
Canvas.Draw()
def SelectPointHit(self, Point):
- print "Point Num: %i Hit"%Point.VerticeNum
+ self.Log("Point Num: %i Hit"%Point.VerticeNum)
self.SelectedPoint = Point
def Read_MapGen(self, filename, stats = 0,AllLines=0):
for line in data:
if line:
if line == "# -b": #New segment beginning
- if segment: Shorelines.append(Numeric.array(segment))
+ if segment: Shorelines.append(N.array(segment))
segment = []
else:
segment.append(map(float,string.split(line)))
- if segment: Shorelines.append(Numeric.array(segment))
+ if segment: Shorelines.append(N.array(segment))
if stats:
NumSegments = len(Shorelines)
if __name__ == "__main__":
+ # running stand alone, Use wxversion:
+# import wxversion
+# wxversion.select("2.6")
+# wxversion.select("2.8")
import wx
# check options:
import sys, getopt
- optlist, args = getopt.getopt(sys.argv[1:],'l',["local",
- "all",
+ optlist, args = getopt.getopt(sys.argv[1:],'l',["all",
"text",
"map",
"stext",
"temp",
"props",
"arrow",
+ "arrowline",
"hide"])
- if not haveNumeric:
+ if not haveNumpy:
raise ImportError(errorText)
StartUpDemo = "all" # the default
- for opt in optlist:
- if opt[0] == "--all":
- StartUpDemo = "all"
- elif opt[0] == "--text":
- StartUpDemo = "text"
- elif opt[0] == "--map":
- StartUpDemo = "map"
- elif opt[0] == "--stext":
- StartUpDemo = "stext"
- elif opt[0] == "--stextbox":
- StartUpDemo = "stextbox"
- elif opt[0] == "--bitmap":
- StartUpDemo = "bitmap"
- elif opt[0] == "--hit":
- StartUpDemo = "hit"
- elif opt[0] == "--hitf":
- StartUpDemo = "hitf"
- elif opt[0] == "--animate":
- StartUpDemo = "animate"
- elif opt[0] == "--speed":
- StartUpDemo = "speed"
- elif opt[0] == "--temp":
- StartUpDemo = "temp"
- elif opt[0] == "--props":
- StartUpDemo = "props"
- elif opt[0] == "--arrow":
- StartUpDemo = "arrow"
- elif opt[0] == "--hide":
- StartUpDemo = "hide"
+ if optlist:
+ StartUpDemo = optlist[0][0][2:]
+
class DemoApp(wx.App):
"""
frame.Show()
## check to see if the demo is set to start in a particular mode.
+ ## fixme: should this be in a dict instead?
if StartUpDemo == "text":
frame.TestText()
elif StartUpDemo == "stext":
elif StartUpDemo == "hit":
frame.TestHitTest()
elif StartUpDemo == "hitf":
- "starting TestHitTestForeground"
frame.TestHitTestForeground()
elif StartUpDemo == "animate":
- "starting TestAnimation"
frame.TestAnimation()
elif StartUpDemo == "speed":
- "starting SpeedTest"
frame.SpeedTest()
elif StartUpDemo == "temp":
- "starting temp Test"
frame.TempTest()
elif StartUpDemo == "props":
- "starting PropertiesChange Test"
frame.PropertiesChangeTest()
elif StartUpDemo == "arrow":
- "starting arrow Test"
frame.ArrowTest()
+ elif StartUpDemo == "arrowline":
+ frame.ArrowLineTest()
elif StartUpDemo == "hide":
- "starting Hide Test"
frame.HideTest()
return True
# It's not running stand-alone, set up for wxPython demo.
# don't neeed wxversion here.
import wx
- if not haveNumeric:
+ if not haveNumpy:
## TestPanel and runTest used for integration into wxPython Demo
class TestPanel(wx.Panel):
def __init__(self, parent, log):
self.log = log
wx.Panel.__init__(self, parent, -1)
- import images
+ from wx.lib.floatcanvas.ScreenShot import getScreenShotBitmap
note1 = wx.StaticText(self, -1, errorText)
note2 = wx.StaticText(self, -1, "This is what the FloatCanvas can look like:")
S.Add((10, 10), 1)
S.Add(note1, 0, wx.ALIGN_CENTER)
S.Add(note2, 0, wx.ALIGN_CENTER | wx.BOTTOM, 4)
- S.Add(wx.StaticBitmap(self,-1,images.getFloatCanvasBitmap()),0,wx.ALIGN_CENTER)
+ S.Add(wx.StaticBitmap(self,-1,getScreenShotBitmap()),0,wx.ALIGN_CENTER)
S.Add((10, 10), 1)
self.SetSizer(S)
self.Layout()
# * Annoying switching between tabs and resulting flicker
# how to replace a page in the notebook without deleting/adding?
# Where is SetPage!? tried freeze...tried reparent of dummy panel....
+# AG: It looks like this issue is fixed by Freeze()ing and Thaw()ing the
+# main frame and not the notebook
# TODO List:
-# * UI design more prefessional
+# * UI design more professional (is the new version more professional?)
# * save file positions (new field in demoModules) (@ LoadDemoSource)
# * Update main overview
import sys, os, time, traceback, types
import wx # This module uses the new wx namespace
+import wx.aui
import wx.html
import images
#---------------------------------------------------------------------------
+USE_CUSTOMTREECTRL = False
+ALLOW_AUI_FLOATING = False
+DEFAULT_PERSPECTIVE = "Default Perspective"
+
+#---------------------------------------------------------------------------
+
+_demoPngs = ["overview", "recent", "frame", "dialog", "moredialog", "core",
+ "book", "customcontrol", "morecontrols", "layout", "process", "clipboard",
+ "images", "miscellaneous"]
_treeList = [
# new stuff
def ActiveModuleChanged(self):
self.LoadDemoSource(self.demoModules.GetSource())
self.UpdateControlState()
+ self.mainFrame.Freeze()
self.ReloadDemo()
+ self.mainFrame.Thaw()
def LoadDemoSource(self, source):
self.demoModules.LoadFromFile(modModified, modifiedFilename)
self.ActiveModuleChanged()
+ self.mainFrame.SetTreeModified(True)
+
def OnRestore(self, event): # Handles the "Delete Modified" button
modifiedFilename = GetModifiedFilename(self.demoModules.name)
self.demoModules.Delete(modModified)
os.unlink(modifiedFilename) # Delete the modified copy
busy = wx.BusyInfo("Reloading demo module...")
+
self.ActiveModuleChanged()
+ self.mainFrame.SetTreeModified(False)
+
#---------------------------------------------------------------------------
def opj(path):
"""Convert paths to the platform-specific separator"""
- str = apply(os.path.join, tuple(path.split('/')))
+ st = apply(os.path.join, tuple(path.split('/')))
# HACK: on Linux, a leading / gets lost...
if path.startswith('/'):
- str = '/' + str
- return str
+ st = '/' + st
+ return st
+
+
+def GetDataDir():
+ """
+ Return the standard location on this platform for application data
+ """
+ sp = wx.StandardPaths.Get()
+ return sp.GetUserDataDir()
def GetModifiedDirectory():
Returns the directory where modified versions of the demo files
are stored
"""
- return opj(wx.GetHomeDir() + "/.wxPyDemo/modified/")
+ return os.path.join(GetDataDir(), "modified")
def GetModifiedFilename(name):
"""
if not name.endswith(".py"):
name = name + ".py"
- return GetModifiedDirectory() + name
+ return os.path.join(GetModifiedDirectory(), name)
def GetOriginalFilename(name):
return False
+def GetConfig():
+ if not os.path.exists(GetDataDir()):
+ os.makedirs(GetDataDir())
+
+ config = wx.FileConfig(
+ localFilename=os.path.join(GetDataDir(), "options"))
+ return config
+
+
+def SearchDemo(name, keyword):
+ """ Returns whether a demo contains the search keyword or not. """
+ fid = open(GetOriginalFilename(name), "rt")
+ fullText = fid.read()
+ fid.close()
+ if fullText.find(keyword) >= 0:
+ return True
+
+ return False
+
#---------------------------------------------------------------------------
class ModuleDictWrapper:
overviewText = "wxPython Overview"
def __init__(self, parent, title):
- wx.Frame.__init__(self, parent, -1, title, size = (950, 720),
+ wx.Frame.__init__(self, parent, -1, title, size = (970, 720),
style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self.SetMinSize((640,480))
+
+ self.mgr = wx.aui.AuiManager()
+ self.mgr.SetManagedWindow(self)
self.loaded = False
self.cwd = os.getcwd()
except:
self.tbicon = None
- wx.CallAfter(self.ShowTip)
-
self.otherWin = None
self.Bind(wx.EVT_IDLE, self.OnIdle)
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
self.Centre(wx.BOTH)
self.CreateStatusBar(1, wx.ST_SIZEGRIP)
- splitter = wx.SplitterWindow(self, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
- splitter2 = wx.SplitterWindow(splitter, -1, style=wx.CLIP_CHILDREN | wx.SP_LIVE_UPDATE | wx.SP_3D)
-
- def EmptyHandler(evt): pass
- #splitter.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
- #splitter2.Bind(wx.EVT_ERASE_BACKGROUND, EmptyHandler)
-
- # Prevent TreeCtrl from displaying all items after destruction when True
self.dying = False
+ self.skipLoad = False
+
+ def EmptyHandler(evt): pass
+ self.ReadConfigurationFile()
+
# Create a Notebook
- self.nb = wx.Notebook(splitter2, -1, style=wx.CLIP_CHILDREN)
-
- # Make a File menu
- self.mainmenu = wx.MenuBar()
- menu = wx.Menu()
- item = menu.Append(-1, '&Redirect Output',
- 'Redirect print statements to a window',
- wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
-
- exitItem = menu.Append(-1, 'E&xit\tCtrl-Q', 'Get the heck outta here!')
- self.Bind(wx.EVT_MENU, self.OnFileExit, exitItem)
- wx.App.SetMacExitMenuItemId(exitItem.GetId())
- self.mainmenu.Append(menu, '&File')
-
- # Make a Demo menu
- menu = wx.Menu()
- for item in _treeList[:-1]:
- submenu = wx.Menu()
- for childItem in item[1]:
- mi = submenu.Append(-1, childItem)
- self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
- menu.AppendMenu(wx.NewId(), item[0], submenu)
- self.mainmenu.Append(menu, '&Demo')
-
-
- # Make a Help menu
- menu = wx.Menu()
- findItem = menu.Append(-1, '&Find\tCtrl-F', 'Find in the Demo Code')
- findnextItem = menu.Append(-1, 'Find &Next\tF3', 'Find Next')
- menu.AppendSeparator()
-
- shellItem = menu.Append(-1, 'Open Py&Shell Window\tF5',
- 'An interactive interpreter window with the demo app and frame objects in the namesapce')
- inspToolItem = menu.Append(-1, 'Open &Widget Inspector\tF6',
- 'A tool that lets you browse the live widgets and sizers in an application')
- menu.AppendSeparator()
- helpItem = menu.Append(-1, '&About wxPython Demo', 'wxPython RULES!!!')
- wx.App.SetMacAboutMenuItemId(helpItem.GetId())
-
- self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
- self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, inspToolItem)
- self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
- self.Bind(wx.EVT_MENU, self.OnHelpFind, findItem)
- self.Bind(wx.EVT_MENU, self.OnFindNext, findnextItem)
- self.Bind(wx.EVT_FIND, self.OnFind)
- self.Bind(wx.EVT_FIND_NEXT, self.OnFind)
- self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)
- self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findItem)
- self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findnextItem)
- self.mainmenu.Append(menu, '&Help')
- self.SetMenuBar(self.mainmenu)
-
+ self.nb = wx.Notebook(self, -1, style=wx.CLIP_CHILDREN)
+ imgList = wx.ImageList(16, 16)
+ for png in ["overview", "code", "demo"]:
+ bmp = images.catalog[png].getBitmap()
+ imgList.Add(bmp)
+ self.nb.AssignImageList(imgList)
+
+ self.BuildMenuBar()
+
self.finddata = wx.FindReplaceData()
self.finddata.SetFlags(wx.FR_DOWN)
- if False:
- # This is another way to set Accelerators, in addition to
- # using the '\t<key>' syntax in the menu items.
- aTable = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), exitItem.GetId()),
- (wx.ACCEL_CTRL, ord('H'), helpItem.GetId()),
- (wx.ACCEL_CTRL, ord('F'), findItem.GetId()),
- (wx.ACCEL_NORMAL, wx.WXK_F3, findnextItem.GetId()),
- (wx.ACCEL_NORMAL, wx.WXK_F9, shellItem.GetId()),
- ])
- self.SetAcceleratorTable(aTable)
-
-
# Create a TreeCtrl
- tID = wx.NewId()
- leftPanel = wx.Panel(splitter)
+ leftPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL|wx.CLIP_CHILDREN)
+ self.treeMap = {}
+ self.searchItems = {}
+
+ self.tree = wxPythonDemoTree(leftPanel)
- self.filter = wx.SearchCtrl(leftPanel)
+ self.filter = wx.SearchCtrl(leftPanel, style=wx.TE_PROCESS_ENTER)
self.filter.ShowCancelButton(True)
self.filter.Bind(wx.EVT_TEXT, self.RecreateTree)
self.filter.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN,
lambda e: self.filter.SetValue(''))
-
- self.treeMap = {}
- self.tree = wx.TreeCtrl(leftPanel, tID, style =
- wx.TR_DEFAULT_STYLE #| wx.TR_HAS_VARIABLE_ROW_HEIGHT
- )
+ self.filter.Bind(wx.EVT_TEXT_ENTER, self.OnSearch)
+
+ searchMenu = wx.Menu()
+ item = searchMenu.AppendRadioItem(-1, "Sample Name")
+ self.Bind(wx.EVT_MENU, self.OnSearchMenu, item)
+ item = searchMenu.AppendRadioItem(-1, "Sample Content")
+ self.Bind(wx.EVT_MENU, self.OnSearchMenu, item)
+ self.filter.SetMenu(searchMenu)
- self.root = self.tree.AddRoot("wxPython Overview")
self.RecreateTree()
- self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded, id=tID)
- self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed, id=tID)
- self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=tID)
+ self.tree.SetExpansionState(self.expansionState)
+ self.tree.Bind(wx.EVT_TREE_ITEM_EXPANDED, self.OnItemExpanded)
+ self.tree.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed)
+ self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged)
self.tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown)
# Set up a wx.html.HtmlWindow on the Overview Notebook page
# we put it in a panel first because there seems to be a
# refresh bug of some sort (wxGTK) when it is directly in
# the notebook...
+
if 0: # the old way
self.ovr = wx.html.HtmlWindow(self.nb, -1, size=(400, 400))
- self.nb.AddPage(self.ovr, self.overviewText)
+ self.nb.AddPage(self.ovr, self.overviewText, imageId=0)
else: # hopefully I can remove this hacky code soon, see SF bug #216861
panel = wx.Panel(self.nb, -1, style=wx.CLIP_CHILDREN)
self.ovr = wx.html.HtmlWindow(panel, -1, size=(400, 400))
- self.nb.AddPage(panel, self.overviewText)
+ self.nb.AddPage(panel, self.overviewText, imageId=0)
def OnOvrSize(evt, ovr=self.ovr):
ovr.SetSize(evt.GetSize())
# Set up a log window
- self.log = wx.TextCtrl(splitter2, -1,
+ self.log = wx.TextCtrl(self, -1,
style = wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
if wx.Platform == "__WXMAC__":
self.log.MacCheckSpelling(False)
#wx.Log_SetActiveTarget(wx.LogStderr())
#wx.Log_SetTraceMask(wx.TraceMessages)
-
self.Bind(wx.EVT_ACTIVATE, self.OnActivate)
wx.GetApp().Bind(wx.EVT_ACTIVATE_APP, self.OnAppActivate)
# add the windows to the splitter and split it.
- splitter2.SplitHorizontally(self.nb, self.log, -160)
leftBox = wx.BoxSizer(wx.VERTICAL)
leftBox.Add(self.tree, 1, wx.EXPAND)
leftBox.Add(wx.StaticText(leftPanel, label = "Filter Demos:"), 0, wx.TOP|wx.LEFT, 5)
leftBox.Add(self.filter, 0, wx.EXPAND|wx.ALL, 5)
leftPanel.SetSizer(leftBox)
- splitter.SplitVertically(leftPanel, splitter2, 220)
-
- splitter.SetMinimumPaneSize(120)
- splitter2.SetMinimumPaneSize(60)
-
- # Make the splitter on the right expand the top window when resized
- def SplitterOnSize(evt):
- splitter = evt.GetEventObject()
- sz = splitter.GetSize()
- splitter.SetSashPosition(sz.height - 160, False)
- evt.Skip()
-
- splitter2.Bind(wx.EVT_SIZE, SplitterOnSize)
# select initial items
self.nb.SetSelection(0)
self.tree.SelectItem(selectedDemo)
self.tree.EnsureVisible(selectedDemo)
+ # Use the aui manager to set up everything
+ self.mgr.AddPane(self.nb, wx.aui.AuiPaneInfo().CenterPane().Name("Notebook"))
+ self.mgr.AddPane(leftPanel,
+ wx.aui.AuiPaneInfo().
+ Left().Layer(2).BestSize((240, -1)).
+ MinSize((160, -1)).
+ Floatable(ALLOW_AUI_FLOATING).FloatingSize((240, 700)).
+ Caption("wxPython Demos").
+ CloseButton(False).
+ Name("DemoTree"))
+ self.mgr.AddPane(self.log,
+ wx.aui.AuiPaneInfo().
+ Bottom().BestSize((-1, 150)).
+ MinSize((-1, 60)).
+ Floatable(ALLOW_AUI_FLOATING).FloatingSize((500, 160)).
+ Caption("Demo Log Messages").
+ CloseButton(False).
+ Name("LogWindow"))
+
+ self.auiConfigurations[DEFAULT_PERSPECTIVE] = self.mgr.SavePerspective()
+ self.mgr.Update()
+
+ self.mgr.SetFlags(self.mgr.GetFlags() ^ wx.aui.AUI_MGR_TRANSPARENT_DRAG)
+
+
- #---------------------------------------------
-
+ def ReadConfigurationFile(self):
+
+ self.auiConfigurations = {}
+ self.expansionState = [0, 1]
+
+ config = GetConfig()
+ val = config.Read('ExpansionState')
+ if val:
+ self.expansionState = eval(val)
+
+ val = config.Read('AUIPerspectives')
+ if val:
+ self.auiConfigurations = eval(val)
+
+
+ def BuildMenuBar(self):
+
+ # Make a File menu
+ self.mainmenu = wx.MenuBar()
+ menu = wx.Menu()
+ item = menu.Append(-1, '&Redirect Output',
+ 'Redirect print statements to a window',
+ wx.ITEM_CHECK)
+ self.Bind(wx.EVT_MENU, self.OnToggleRedirect, item)
+
+ exitItem = wx.MenuItem(menu, -1, 'E&xit\tCtrl-Q', 'Get the heck outta here!')
+ exitItem.SetBitmap(images.catalog['exit'].getBitmap())
+ menu.AppendItem(exitItem)
+ self.Bind(wx.EVT_MENU, self.OnFileExit, exitItem)
+ wx.App.SetMacExitMenuItemId(exitItem.GetId())
+ self.mainmenu.Append(menu, '&File')
+
+ # Make a Demo menu
+ menu = wx.Menu()
+ for indx, item in enumerate(_treeList[:-1]):
+ menuItem = wx.MenuItem(menu, -1, item[0])
+ submenu = wx.Menu()
+ for childItem in item[1]:
+ mi = submenu.Append(-1, childItem)
+ self.Bind(wx.EVT_MENU, self.OnDemoMenu, mi)
+ menuItem.SetBitmap(images.catalog[_demoPngs[indx+1]].getBitmap())
+ menuItem.SetSubMenu(submenu)
+ menu.AppendItem(menuItem)
+ self.mainmenu.Append(menu, '&Demo')
+
+ # Make an Option menu
+ # If we've turned off floatable panels then this menu is not needed
+ if ALLOW_AUI_FLOATING:
+ menu = wx.Menu()
+ auiPerspectives = self.auiConfigurations.keys()
+ auiPerspectives.sort()
+ perspectivesMenu = wx.Menu()
+ item = wx.MenuItem(perspectivesMenu, -1, DEFAULT_PERSPECTIVE, "Load startup default perspective", wx.ITEM_RADIO)
+ self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item)
+ perspectivesMenu.AppendItem(item)
+ for indx, key in enumerate(auiPerspectives):
+ if key == DEFAULT_PERSPECTIVE:
+ continue
+ item = wx.MenuItem(perspectivesMenu, -1, key, "Load user perspective %d"%indx, wx.ITEM_RADIO)
+ perspectivesMenu.AppendItem(item)
+ self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item)
+
+ menu.AppendMenu(wx.ID_ANY, "&AUI Perspectives", perspectivesMenu)
+ self.perspectives_menu = perspectivesMenu
+
+ item = wx.MenuItem(menu, -1, 'Save Perspective', 'Save AUI perspective')
+ item.SetBitmap(images.catalog['saveperspective'].getBitmap())
+ menu.AppendItem(item)
+ self.Bind(wx.EVT_MENU, self.OnSavePerspective, item)
+
+ item = wx.MenuItem(menu, -1, 'Delete Perspective', 'Delete AUI perspective')
+ item.SetBitmap(images.catalog['deleteperspective'].getBitmap())
+ menu.AppendItem(item)
+ self.Bind(wx.EVT_MENU, self.OnDeletePerspective, item)
+
+ menu.AppendSeparator()
+
+ item = wx.MenuItem(menu, -1, 'Restore Tree Expansion', 'Restore the initial tree expansion state')
+ item.SetBitmap(images.catalog['expansion'].getBitmap())
+ menu.AppendItem(item)
+ self.Bind(wx.EVT_MENU, self.OnTreeExpansion, item)
+
+ self.mainmenu.Append(menu, '&Options')
+
+ # Make a Help menu
+ menu = wx.Menu()
+ findItem = wx.MenuItem(menu, -1, '&Find\tCtrl-F', 'Find in the Demo Code')
+ findItem.SetBitmap(images.catalog['find'].getBitmap())
+ findNextItem = wx.MenuItem(menu, -1, 'Find &Next\tF3', 'Find Next')
+ findNextItem.SetBitmap(images.catalog['findnext'].getBitmap())
+ menu.AppendItem(findItem)
+ menu.AppendItem(findNextItem)
+ menu.AppendSeparator()
+
+ shellItem = wx.MenuItem(menu, -1, 'Open Py&Shell Window\tF5',
+ 'An interactive interpreter window with the demo app and frame objects in the namesapce')
+ shellItem.SetBitmap(images.catalog['pyshell'].getBitmap())
+ menu.AppendItem(shellItem)
+ inspToolItem = wx.MenuItem(menu, -1, 'Open &Widget Inspector\tF6',
+ 'A tool that lets you browse the live widgets and sizers in an application')
+ inspToolItem.SetBitmap(images.catalog['inspect'].getBitmap())
+ menu.AppendItem(inspToolItem)
+ menu.AppendSeparator()
+ helpItem = menu.Append(-1, '&About wxPython Demo', 'wxPython RULES!!!')
+ wx.App.SetMacAboutMenuItemId(helpItem.GetId())
+
+ self.Bind(wx.EVT_MENU, self.OnOpenShellWindow, shellItem)
+ self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, inspToolItem)
+ self.Bind(wx.EVT_MENU, self.OnHelpAbout, helpItem)
+ self.Bind(wx.EVT_MENU, self.OnHelpFind, findItem)
+ self.Bind(wx.EVT_MENU, self.OnFindNext, findNextItem)
+ self.Bind(wx.EVT_FIND, self.OnFind)
+ self.Bind(wx.EVT_FIND_NEXT, self.OnFind)
+ self.Bind(wx.EVT_FIND_CLOSE, self.OnFindClose)
+ self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findItem)
+ self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFindItems, findNextItem)
+ self.mainmenu.Append(menu, '&Help')
+ self.SetMenuBar(self.mainmenu)
+
+ if False:
+ # This is another way to set Accelerators, in addition to
+ # using the '\t<key>' syntax in the menu items.
+ aTable = wx.AcceleratorTable([(wx.ACCEL_ALT, ord('X'), exitItem.GetId()),
+ (wx.ACCEL_CTRL, ord('H'), helpItem.GetId()),
+ (wx.ACCEL_CTRL, ord('F'), findItem.GetId()),
+ (wx.ACCEL_NORMAL, wx.WXK_F3, findnextItem.GetId()),
+ (wx.ACCEL_NORMAL, wx.WXK_F9, shellItem.GetId()),
+ ])
+ self.SetAcceleratorTable(aTable)
+
+
+ #---------------------------------------------
def RecreateTree(self, evt=None):
+ # Catch the search type (name or content)
+ searchMenu = self.filter.GetMenu().GetMenuItems()
+ fullSearch = searchMenu[1].IsChecked()
+
+ if evt:
+ if fullSearch:
+ # Do not`scan all the demo files for every char
+ # the user input, use wx.EVT_TEXT_ENTER instead
+ return
+
+ expansionState = self.tree.GetExpansionState()
+
+ current = None
+ item = self.tree.GetSelection()
+ if item:
+ prnt = self.tree.GetItemParent(item)
+ if prnt:
+ current = (self.tree.GetItemText(item),
+ self.tree.GetItemText(prnt))
+
self.tree.Freeze()
self.tree.DeleteAllItems()
self.root = self.tree.AddRoot("wxPython Overview")
+ self.tree.SetItemImage(self.root, 0)
+ self.tree.SetItemPyData(self.root, 0)
+
+ treeFont = self.tree.GetFont()
+ catFont = self.tree.GetFont()
+
+ # The old native treectrl on MSW has a bug where it doesn't
+ # draw all of the text for an item if the font is larger than
+ # the default. It seems to be clipping the item's label as if
+ # it was the size of the same label in the default font.
+ if 'wxMSW' not in wx.PlatformInfo or wx.GetApp().GetComCtl32Version() >= 600:
+ treeFont.SetPointSize(treeFont.GetPointSize()+2)
+ treeFont.SetWeight(wx.BOLD)
+ catFont.SetWeight(wx.BOLD)
+
+ self.tree.SetItemFont(self.root, treeFont)
+
firstChild = None
+ selectItem = None
filter = self.filter.GetValue()
+ count = 0
+
for category, items in _treeList:
+ count += 1
if filter:
- items = [item for item in items if filter.lower() in item.lower()]
+ if fullSearch:
+ items = self.searchItems[category]
+ else:
+ items = [item for item in items if filter.lower() in item.lower()]
if items:
- child = self.tree.AppendItem(self.root, category)
+ child = self.tree.AppendItem(self.root, category, image=count)
+ self.tree.SetItemFont(child, catFont)
+ self.tree.SetItemPyData(child, count)
if not firstChild: firstChild = child
for childItem in items:
- theDemo = self.tree.AppendItem(child, childItem)
+ image = count
+ if DoesModifiedExist(childItem):
+ image = len(_demoPngs)
+ theDemo = self.tree.AppendItem(child, childItem, image=image)
+ self.tree.SetItemPyData(theDemo, count)
self.treeMap[childItem] = theDemo
-
+ if current and (childItem, category) == current:
+ selectItem = theDemo
+
+
self.tree.Expand(self.root)
if firstChild:
self.tree.Expand(firstChild)
if filter:
self.tree.ExpandAll()
+ elif expansionState:
+ self.tree.SetExpansionState(expansionState)
+ if selectItem:
+ self.skipLoad = True
+ self.tree.SelectItem(selectItem)
+ self.skipLoad = False
+
self.tree.Thaw()
-
+ self.searchItems = {}
+
+
+ def OnSearchMenu(self, event):
+
+ # Catch the search type (name or content)
+ searchMenu = self.filter.GetMenu().GetMenuItems()
+ fullSearch = searchMenu[1].IsChecked()
+
+ if fullSearch:
+ self.OnSearch()
+ else:
+ self.RecreateTree()
+
+
+ def OnSearch(self, event=None):
+
+ value = self.filter.GetValue()
+ if not value:
+ self.RecreateTree()
+ return
+
+ wx.BeginBusyCursor()
+
+ for category, items in _treeList:
+ self.searchItems[category] = []
+ for childItem in items:
+ if SearchDemo(childItem, value):
+ self.searchItems[category].append(childItem)
+
+ wx.EndBusyCursor()
+ self.RecreateTree()
+
+
+ def SetTreeModified(self, modified):
+ item = self.tree.GetSelection()
+ if modified:
+ image = len(_demoPngs)
+ else:
+ image = self.tree.GetItemPyData(item)
+ self.tree.SetItemImage(item, image)
+
+
def WriteText(self, text):
if text[-1:] == '\n':
text = text[:-1]
#---------------------------------------------
def OnSelChanged(self, event):
- if self.dying or not self.loaded:
+ if self.dying or not self.loaded or self.skipLoad:
return
item = event.GetItem()
def LoadDemo(self, demoName):
try:
wx.BeginBusyCursor()
+ self.Freeze()
os.chdir(self.cwd)
self.ShutdownDemoModule()
wx.LogMessage("Loading demo %s.py..." % demoName)
self.demoModules = DemoModules(demoName)
self.LoadDemoSource()
- self.tree.Refresh()
else:
self.SetOverview("wxPython", mainOverview)
self.codePage = None
self.UpdateNotebook(0)
finally:
wx.EndBusyCursor()
+ self.Thaw()
#---------------------------------------------
def LoadDemoSource(self):
self.demoPage = DemoErrorPanel(self.nb, self.codePage,
DemoError(sys.exc_info()), self)
+ bg = self.nb.GetThemeBackgroundColour()
+ if bg:
+ self.demoPage.SetBackgroundColour(bg)
+
assert self.demoPage is not None, "runTest must return a window!"
else:
self.SetOverview(self.demoModules.name + " Overview", overviewText)
if self.firstTime:
- # cahnge to the demo page the first time a module is run
+ # change to the demo page the first time a module is run
self.UpdateNotebook(2)
self.firstTime = False
else:
def UpdateNotebook(self, select = -1):
nb = self.nb
debug = False
+ self.Freeze()
def UpdatePage(page, pageText):
pageExists = False
if page:
if not pageExists:
# Add a new page
- nb.AddPage(page, pageText)
+ nb.AddPage(page, pageText, imageId=nb.GetPageCount())
if debug: wx.LogMessage("DBG: ADDED %s" % pageText)
else:
if nb.GetPage(pagePos) != page:
# Reload an existing page
- nb.Freeze()
nb.DeletePage(pagePos)
- nb.InsertPage(pagePos, page, pageText)
- nb.Thaw()
+ nb.InsertPage(pagePos, page, pageText, imageId=pagePos)
if debug: wx.LogMessage("DBG: RELOADED %s" % pageText)
else:
# Excellent! No redraw/flicker
if select >= 0 and select < nb.GetPageCount():
nb.SetSelection(select)
-
+
+ self.Thaw()
+
#---------------------------------------------
def SetOverview(self, name, text):
self.curOverview = text
else:
app.RestoreStdio()
print "Print statements and other standard output will now be sent to the usual location."
+
+
+ def OnAUIPerspectives(self, event):
+ perspective = self.perspectives_menu.GetLabel(event.GetId())
+ self.mgr.LoadPerspective(self.auiConfigurations[perspective])
+ self.mgr.Update()
+
+
+ def OnSavePerspective(self, event):
+ dlg = wx.TextEntryDialog(self, "Enter a name for the new perspective:", "AUI Configuration")
+
+ dlg.SetValue(("Perspective %d")%(len(self.auiConfigurations)+1))
+ if dlg.ShowModal() != wx.ID_OK:
+ return
+
+ perspectiveName = dlg.GetValue()
+ menuItems = self.perspectives_menu.GetMenuItems()
+ for item in menuItems:
+ if item.GetLabel() == perspectiveName:
+ wx.MessageBox("The selected perspective name:\n\n%s\n\nAlready exists."%perspectiveName,
+ "Error", style=wx.ICON_ERROR)
+ return
+
+ item = wx.MenuItem(self.perspectives_menu, -1, dlg.GetValue(),
+ "Load user perspective %d"%(len(self.auiConfigurations)+1),
+ wx.ITEM_RADIO)
+ self.Bind(wx.EVT_MENU, self.OnAUIPerspectives, item)
+ self.perspectives_menu.AppendItem(item)
+ item.Check(True)
+ self.auiConfigurations.update({dlg.GetValue(): self.mgr.SavePerspective()})
+
+
+ def OnDeletePerspective(self, event):
+ menuItems = self.perspectives_menu.GetMenuItems()[1:]
+ lst = []
+ loadDefault = False
+
+ for item in menuItems:
+ lst.append(item.GetLabel())
+
+ dlg = wx.MultiChoiceDialog(self,
+ "Please select the perspectives\nyou would like to delete:",
+ "Delete AUI Perspectives", lst)
+
+ if dlg.ShowModal() == wx.ID_OK:
+ selections = dlg.GetSelections()
+ strings = [lst[x] for x in selections]
+ for sel in strings:
+ self.auiConfigurations.pop(sel)
+ item = menuItems[lst.index(sel)]
+ if item.IsChecked():
+ loadDefault = True
+ self.perspectives_menu.GetMenuItems()[0].Check(True)
+ self.perspectives_menu.DeleteItem(item)
+ lst.remove(sel)
+
+ if loadDefault:
+ self.mgr.LoadPerspective(self.auiConfigurations[DEFAULT_PERSPECTIVE])
+ self.mgr.Update()
+
+
+ def OnTreeExpansion(self, event):
+ self.tree.SetExpansionState(self.expansionState)
+
def OnHelpAbout(self, event):
from About import MyAboutBox
self.mainmenu = None
if self.tbicon is not None:
self.tbicon.Destroy()
+
+ config = GetConfig()
+ config.Write('ExpansionState', str(self.tree.GetExpansionState()))
+ config.Write('AUIPerspectives', str(self.auiConfigurations))
+ config.Flush()
+
self.Destroy()
#---------------------------------------------
def ShowTip(self):
- try:
- showTipText = open(opj("data/showTips")).read()
+ config = GetConfig()
+ showTipText = config.Read("tips")
+ if showTipText:
showTip, index = eval(showTipText)
- except IOError:
+ else:
showTip, index = (1, 0)
+
if showTip:
tp = wx.CreateFileTipProvider(opj("data/tips.txt"), index)
##tp = MyTP(0)
showTip = wx.ShowTip(self, tp)
index = tp.GetCurrentTip()
- open(opj("data/showTips"), "w").write(str( (showTip, index) ))
-
+ config.Write("tips", str( (showTip, index) ))
+ config.Flush()
#---------------------------------------------
def OnDemoMenu(self, event):
frame.Show()
if self.fc.IsRunning():
self.Raise()
+ wx.CallAfter(frame.ShowTip)
+
+
+
+
+#---------------------------------------------------------------------------
+
+from wx.lib.mixins.treemixin import ExpansionState
+if USE_CUSTOMTREECTRL:
+ import wx.lib.customtreectrl as CT
+ TreeBaseClass = CT.CustomTreeCtrl
+else:
+ TreeBaseClass = wx.TreeCtrl
+
+
+class wxPythonDemoTree(ExpansionState, TreeBaseClass):
+ def __init__(self, parent):
+ TreeBaseClass.__init__(self, parent, style=wx.TR_DEFAULT_STYLE|
+ wx.TR_HAS_VARIABLE_ROW_HEIGHT)
+ self.BuildTreeImageList()
+ if USE_CUSTOMTREECTRL:
+ self.SetSpacing(10)
+ self.SetWindowStyle(self.GetWindowStyle() & ~wx.TR_LINES_AT_ROOT)
+
+ def AppendItem(self, parent, text, image=-1, wnd=None):
+ if USE_CUSTOMTREECTRL:
+ item = TreeBaseClass.AppendItem(self, parent, text, image=image, wnd=wnd)
+ else:
+ item = TreeBaseClass.AppendItem(self, parent, text, image=image)
+ return item
+
+ def BuildTreeImageList(self):
+ imgList = wx.ImageList(16, 16)
+ for png in _demoPngs:
+ imgList.Add(images.catalog[png].getBitmap())
+
+ # add the image for modified demos.
+ imgList.Add(images.catalog["custom"].getBitmap())
+ self.AssignImageList(imgList)
+
+ def GetItemIdentity(self, item):
+ return self.GetPyData(item)
+
+
+#---------------------------------------------------------------------------
+
class MyApp(wx.App):
def OnInit(self):
"""
"""
wx.SystemOptions.SetOptionInt("mac.window-plain-transition", 1)
-
+ self.SetAppName("wxPyDemo")
+
# For debugging
#self.SetAssertMode(wx.PYAPP_ASSERT_DIALOG)
self.limit_target = wx.CheckBox( panel, -1, "Limit control" )
+ self.limit_on_field_change = wx.CheckBox( panel, -1, "Limit on field change" )
self.allow_none = wx.CheckBox( panel, -1, "Allow empty control" )
self.group_digits = wx.CheckBox( panel, -1, "Group digits" )
self.group_digits.SetValue( True )
grid1.Add( self.limit_target, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
- grid1.Add( self.allow_none, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
+ grid1.Add( self.limit_on_field_change, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
+
hbox1 = wx.BoxSizer( wx.HORIZONTAL )
hbox1.Add( (17,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
- hbox1.Add( self.group_digits, 0, wx.ALIGN_LEFT|wx.LEFT, 5 )
+ hbox1.Add( self.allow_none, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
grid1.Add( hbox1, 0, wx.ALIGN_LEFT|wx.ALL, 5)
grid1.Add( (5,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
- grid1.Add( self.allow_negative, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
- grid1.Add( self.use_parens, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
+ grid1.Add( self.group_digits, 0, wx.ALIGN_LEFT|wx.LEFT, 5 )
+ grid1.Add( self.allow_negative, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
hbox2 = wx.BoxSizer( wx.HORIZONTAL )
hbox2.Add( (17,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
- hbox2.Add( self.select_on_entry, 0, wx.ALIGN_LEFT|wx.LEFT, 5 )
+ hbox2.Add( self.use_parens, 0, wx.ALIGN_LEFT|wx.ALL, 5 )
grid1.Add( hbox2, 0, wx.ALIGN_LEFT|wx.ALL, 5)
grid1.Add( (5,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
-
+
+ grid1.Add( self.select_on_entry, 0, wx.ALIGN_LEFT|wx.LEFT, 5 )
+ grid1.Add( (5,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
+ grid1.Add( (5,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
+ grid1.Add( (5,5), 0, wx.ALIGN_LEFT|wx.ALL, 5)
grid2 = wx.FlexGridSizer( 0, 2, 0, 0 )
grid2.Add( label, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 )
self.Bind(masked.EVT_NUM, self.SetTargetMinMax, self.min )
self.Bind(masked.EVT_NUM, self.SetTargetMinMax, self.max )
- self.Bind(wx.EVT_CHECKBOX, self.SetTargetMinMax, self.limit_target )
+ self.Bind(wx.EVT_CHECKBOX, self.OnSetLimited, self.limit_target )
+ self.Bind(wx.EVT_CHECKBOX, self.OnSetLimitOnFieldChange, self.limit_on_field_change )
self.Bind(wx.EVT_CHECKBOX, self.OnSetAllowNone, self.allow_none )
self.Bind(wx.EVT_CHECKBOX, self.OnSetGroupDigits, self.group_digits )
self.Bind(wx.EVT_CHECKBOX, self.OnSetAllowNegative, self.allow_negative )
self.SetTargetMinMax()
+ def OnSetLimited( self, event ):
+ limited = self.limit_target.GetValue()
+ self.target_ctl.SetLimited( limited )
+ limit_on_field_change = self.limit_on_field_change.GetValue()
+ if limited and limit_on_field_change:
+ self.limit_on_field_change.SetValue(False)
+ self.target_ctl.SetLimitOnFieldChange( False )
+ self.SetTargetMinMax()
+
+
+ def OnSetLimitOnFieldChange( self, event ):
+ limit_on_field_change = self.limit_on_field_change.GetValue()
+ self.target_ctl.SetLimitOnFieldChange( limit_on_field_change )
+ limited = self.limit_target.GetValue()
+ if limited and limit_on_field_change:
+ self.limit_target.SetValue(False)
+ self.target_ctl.SetLimited( False )
+
def SetTargetMinMax( self, event=None ):
min = max = None
- self.target_ctl.SetLimited( self.limit_target.GetValue() )
-
if self.set_min.GetValue():
min = self.min.GetValue()
if self.set_max.GetValue():
if shape.Selected():
shape.Select(False, dc)
- canvas.Redraw(dc)
+ #canvas.Redraw(dc)
+ canvas.Refresh(False)
else:
redraw = False
shapeList = canvas.GetDiagram().GetShapeList()
for s in toUnselect:
s.Select(False, dc)
- canvas.Redraw(dc)
+ ##canvas.Redraw(dc)
+ canvas.Refresh(False)
self.UpdateStatusBar(shape)
def OnMovePost(self, dc, x, y, oldX, oldY, display):
+ shape = self.GetShape()
ogl.ShapeEvtHandler.OnMovePost(self, dc, x, y, oldX, oldY, display)
- self.UpdateStatusBar(self.GetShape())
-
+ self.UpdateStatusBar(shape)
+ if "wxMac" in wx.PlatformInfo:
+ shape.GetCanvas().Refresh(False)
def OnRightClick(self, *dontcare):
self.log.WriteText("%s\n" % self.GetShape())
s.SetBitmap(bmp)
self.MyAddShape(s, 225, 130, None, None, "Bitmap")
- dc = wx.ClientDC(self)
- self.PrepareDC(dc)
+ #dc = wx.ClientDC(self)
+ #self.PrepareDC(dc)
for x in range(len(self.shapes)):
fromShape = self.shapes[x]
import wx, wx.lib.customtreectrl, wx.gizmos
-import treemixin
+try:
+ import treemixin
+except ImportError:
+ from wx.lib.mixins import treemixin
+overview = treemixin.__doc__
class TreeModel(object):
''' TreeModel holds the domain objects that are shown in the different
"-a -u -n _book_green bmp_source/book_green.png images.py",
"-a -u -n _book_blue bmp_source/book_blue.png images.py",
+ "-a -u -c bmp_source/book.png images.py",
+ "-a -u -c bmp_source/clipboard.png images.py",
+ "-a -u -c bmp_source/code.png images.py",
+ "-a -u -c bmp_source/core.png images.py",
+ "-a -u -c bmp_source/custom.png images.py",
+ "-a -u -c bmp_source/deleteperspective.png images.py",
+ "-a -u -c bmp_source/demo.png images.py",
+ "-a -u -c bmp_source/dialog.png images.py",
+ "-a -u -c bmp_source/exit.png images.py",
+ "-a -u -c bmp_source/expansion.png images.py",
+ "-a -u -c bmp_source/find.png images.py",
+ "-a -u -c bmp_source/findnext.png images.py",
+ "-a -u -c bmp_source/frame.png images.py",
+ "-a -u -c bmp_source/images.png images.py",
+ "-a -u -c bmp_source/inspect.png images.py",
+ "-a -u -c bmp_source/layout.png images.py",
+ "-a -u -c bmp_source/miscellaneous.png images.py",
+ "-a -u -c bmp_source/modifiedexists.png images.py",
+ "-a -u -c bmp_source/morecontrols.png images.py",
+ "-a -u -c bmp_source/moredialog.png images.py",
+ "-a -u -c bmp_source/overview.png images.py",
+ "-a -u -c bmp_source/process.png images.py",
+ "-a -u -c bmp_source/pyshell.png images.py",
+ "-a -u -c bmp_source/recent.png images.py",
+ "-a -u -c bmp_source/saveperspective.png images.py",
+ "-a -u -c bmp_source/customcontrol.png images.py",
+
+
" -u -c bmp_source/001.png throbImages.py",
"-a -u -c bmp_source/002.png throbImages.py",
"-a -u -c bmp_source/003.png throbImages.py",
wxPython/wx/lib/colourchooser
wxPython/wx/lib/editor
wxPython/wx/lib/floatcanvas
+wxPython/wx/lib/floatcanvas/Utilities
wxPython/wx/lib/masked
wxPython/wx/lib/mixins
wxPython/wx/lib/ogl
wxPython win32 README
---------------------
-The self-installer pacakge you have just installed contains the Python
+The self-installer package you have just installed contains the Python
extension modules, python modules and packages needed to run wxPython
applications. If you selected the "Make this install be the default
wxPython" option in the installer then this version will be the one
[config.OSX_HOST_panther, "2.3", "both"], env=config_env),
Job("bigmac.24",
"distrib/all/build-osx",
- [config.OSX_HOST_panther, "2.4", "both"], env=config_env)
+ [config.OSX_HOST_panther, "2.4", "both"], env=config_env),
+ Job("bigmac.25",
+ "distrib/all/build-osx",
+ [config.OSX_HOST_panther, "2.5", "both"], env=config_env)
])
tigerTask = Task([ #Job("smallfry.23",
# Job("co-mdk2006.24","distrib/all/build-rpm", ["beast", "co-mdk2006", "mdk2006", "2.4"], env=config_env),
])
- xavierTask = Task([
- Job("xavier.d", "distrib/all/build-deb", ["xavier", "/work/chroot/dapper", "dapper"], env=config_env),
- Job("xavier.f", "distrib/all/build-deb", ["xavier", "/work/chroot/feisty", "feisty"], env=config_env),
+ cyclopsTask1 = Task([
+ Job("cyclops.d", "distrib/all/build-deb", ["cyclops", "/work/chroot/dapper", "dapper"], env=config_env),
+ Job("cyclops.d64", "distrib/all/build-deb", ["cyclops", "/work/chroot/dapper64", "dapper64"], env=config_env),
+ Job("cyclops.fc6", "distrib/all/build-chrpm",
+ ["cyclops", "/work/chroot/fc6", "fc6", "fc6", "2.4"], env=config_env),
+ ])
+
+ cyclopsTask2 = Task([
+ Job("cyclops.f", "distrib/all/build-deb", ["cyclops", "/work/chroot/feisty", "feisty"], env=config_env),
+ Job("cyclops.f64", "distrib/all/build-deb", ["cyclops", "/work/chroot/feisty64", "feisty64"], env=config_env),
])
buildTasks = [ #jaguarTask,
tigerTask,
beastTask1,
beastTask2,
- xavierTask,
+ cyclopsTask1,
+ cyclopsTask2,
]
# Finalization. This is for things that must wait until all the
--- /dev/null
+#!/bin/bash
+#----------------------------------------------------------------------
+
+set -o errexit
+#set -o xtrace
+
+host=$1
+chRootRoot=$2
+chRootName=$3
+reltag=$4
+pyver=$5
+
+
+if [ $buildansi = yes ]; then
+ CHARTYPE=both
+else
+ CHARTYPE=unicode
+fi
+
+
+function TestOnline {
+ local host=$1
+ local message=$2
+
+ if ping -q -c1 -w1 $host > /dev/null; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+
+
+if [ $skipdeb != yes ]; then
+ # We use a chroot environment on the build machine for the debian
+ # builds, so this build is pretty simple. Just copy the tarball
+ # and a build script to the build machine, and then run
+ # do-build-deb in the chroot.
+
+ if TestOnline $host; then
+
+ echo "The $host machine is online, build continuing..."
+
+ echo "Copying source files and build script..."
+ ssh root@$host "mkdir -p $chRootRoot/$LINUX_BUILD && rm -rf $chRootRoot/$LINUX_BUILD/*"
+ scp $STAGING_DIR/wxPython-src* distrib/all/do-build-rpm $STAGING_DIR/wxPython.spec \
+ root@$host:$chRootRoot/$LINUX_BUILD
+
+ ssh root@$host "dchroot --chroot $chRootName --directory $LINUX_BUILD \"./do-build-rpm $reltag $skipclean $VERSION $pyver $CHARTYPE\""
+
+ echo "Fetching the results..."
+ ssh root@$host "rm $chRootRoot/$LINUX_BUILD/do-build-rpm"
+ scp "root@$host:$chRootRoot/$LINUX_BUILD/wxPython*.i[0-9]86.rpm" $STAGING_DIR
+ ssh root@$host "rm $chRootRoot/$LINUX_BUILD/wxPython*.i[0-9]86.rpm"
+ echo "Done!"
+ else
+ echo "The $host machine is **OFFLINE**, skipping the binary RPM build."
+ exit 0
+ fi
+fi
+
Source: "wx\lib\masked\*.py"; DestDir: "{app}\%(PKGDIR)s\wx\lib\masked"; Components: core
Source: "wx\lib\ogl\*.py"; DestDir: "{app}\%(PKGDIR)s\wx\lib\ogl"; Components: core
Source: "wx\lib\floatcanvas\*.py"; DestDir: "{app}\%(PKGDIR)s\wx\lib\floatcanvas"; Components: core
+Source: "wx\lib\floatcanvas\Utilities\*.py"; DestDir: "{app}\%(PKGDIR)s\wx\lib\floatcanvas\Utilities"; Components: core
Source: "wx\py\*.py"; DestDir: "{app}\%(PKGDIR)s\wx\py"; Components: core
Source: "wx\py\*.txt"; DestDir: "{app}\%(PKGDIR)s\wx\py"; Components: core
Source: "wx\py\*.ico"; DestDir: "{app}\%(PKGDIR)s\wx\py"; Components: core
Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\ogl\*.pyo";
Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\floatcanvas\*.pyc";
Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\floatcanvas\*.pyo";
+Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\floatcanvas\Utilities\*.pyc";
+Type: files; Name: "{app}\%(PKGDIR)s\wx\lib\floatcanvas\Utilities\*.pyo";
Type: files; Name: "{app}\%(PKGDIR)s\wx\py\*.pyc";
Type: files; Name: "{app}\%(PKGDIR)s\wx\py\*.pyo";
the minimal sizes of the sizer items to be in the same proportion as
the items proportions to return to the old behaviour.
+Added support for toolbar buttons with dropdown menus.
+
+
+
+++ /dev/null
-[
-['wx', 'BitmapButton', '-1, wx.Bitmap("image.png")', ''],
-['wx', 'Button', '-1, "normal"', ''],
-['wx', 'Button', '-1, "with a longer, longer label"', ''],
-['wx', 'Button', '-1, "default"', 'w.SetDefault()'],
-['wx', 'Button', '-1, "larger font"', 'w.SetFont(wx.Font(20, wx.SWISS, wx.NORMAL, wx.NORMAL))\n\n\n'],
-['wx', 'CheckBox', '-1, "checkbox"', ''],
-['wx', 'CheckBox', '-1, "checkbox with longer label"', ''],
-['wx', 'CheckListBox', '-1, size=(100,-1), choices="one two three four five six seven eight".split()', ''],
-['wx', 'Choice', '-1, choices="one two three four five six seven eight".split()', ''],
-['wx', 'Choice', '-1, size=(50,-1), choices="one two three four five six seven eight".split()', ''],
-['wx', 'ComboBox', '-1, value="default", choices="one two three four five six seven eight".split()', ''],
-['wx', 'ComboBox', '-1, value="default", size=(75,-1), choices="one two three four five six seven eight".split()', ''],
-['wx', 'ComboBox', '-1, value="read-only", style=wx.CB_READONLY, choices="read-only one two three four five six seven eight".split()', ''],
-['wx', 'Gauge', '-1, 100', 'w.SetValue(40)'],
-['wx', 'Gauge', '-1, 100, style=wx.GA_VERTICAL', 'w.SetValue(60)'],
-['wx', 'ListBox', '-1, choices="one two three four five six seven eight".split()', ''],
-['wx', 'ListCtrl', 'style=wx.LC_REPORT', 'w.InsertColumn(0, "Col1")\nw.InsertStringItem(0, "Item 0")\nw.InsertStringItem(0, "Item 1")\n#w.SetSizeHints((200,100))'],
-['wx', 'ListCtrl', 'style=wx.LC_REPORT, size=(100,100)', 'w.InsertColumn(0, "Col1")\nw.InsertStringItem(0, "Item 0")\nw.InsertStringItem(0, "Item 1")\n#w.SetSizeHints((200,100))'],
-['wx', 'Notebook', '', 'p = wx.Panel(w)\np.SetMinSize((150,150))\nw.AddPage(p, "test")\n'],
-['wx', 'Panel', 'size=(150,150)', ''],
-['wx', 'RadioBox', '-1, "label", majorDimension=2, choices="one two three four five six seven eight".split()', ''],
-['wx', 'RadioBox', '-1, "label", style=wx.RA_VERTICAL, majorDimension=2, choices="one two three four five six seven eight".split()', ''],
-['wx', 'RadioBox', '-1, "label", choices="one two three four".split()', ''],
-['wx', 'RadioBox', '-1, "label", style=wx.RA_VERTICAL, choices="one two three four".split()', ''],
-['wx', 'RadioButton', '-1, "radio button"', ''],
-['wx', 'ScrollBar', '', ''],
-['wx', 'ScrollBar', 'style=wx.SB_VERTICAL', ''],
-['wx', 'Slider', '-1, 20, 0, 100', ''],
-['wx', 'Slider', '-1, 20, 0, 100, size=(400, -1)', ''],
-['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_LABELS', ''],
-['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_LABELS, size=(400,-1)', ''],
-['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_VERTICAL', ''],
-['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_VERTICAL|wx.SL_LABELS', ''],
-['wx', 'SpinButton', 'style=wx.SP_HORIZONTAL', ''],
-['wx', 'SpinButton', 'style=wx.SP_VERTICAL', ''],
-['wx', 'SpinCtrl', '', ''],
-['wx', 'SpinCtrl', 'size=(50, -1)', ''],
-['wx', 'StaticBitmap', '-1, wx.Bitmap("image.png")', ''],
-['wx', 'StaticBox', '-1, "a longer label"', ''],
-['wx', 'StaticBox', '-1, "label"', ''],
-['wx', 'StaticBox', '-1, "with a size", size=(100,75)', ''],
-['wx', 'StaticLine', '-1, size=(-1,100), style=wx.LI_VERTICAL', ''],
-['wx', 'StaticLine', '-1, size=(100,-1), style=wx.LI_HORIZONTAL', ''],
-['wx', 'StaticText', '-1, "New font"', 'f = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)\nw.SetFont(f)\n'],
-['wx', 'StaticText', '-1, "normal"', ''],
-['wx', 'StaticText', '-1, "normal"', 'w.SetLabel("This is a longer label")'],
-['wx', 'TextCtrl', '-1, "default"', ''],
-['wx', 'TextCtrl', '-1, "larger size", size=(200, -1)', ''],
-['wx', 'TextCtrl', '-1, "small", size=(30,-1)', ''],
-['wx', 'TextCtrl', '-1, "some\\ndefault text\\n", size=(200, -1), style=wx.TE_MULTILINE', 'w.AppendText("Here is some more text\\n")'],
-['wx', 'TreeCtrl', '', ''],
-['wx.calendar', 'CalendarCtrl', '-1', ''],
-['wx.calendar', 'CalendarCtrl', '-1, style=wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION', ''],
-['wx.calendar', 'CalendarCtrl', '-1', 'w.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL))\n'],
-['wx.lib.stattext', 'GenStaticText', '-1, "New font"', 'f = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)\nw.SetFont(f)\n'],
-['wx.grid', 'Grid', '', 'w.CreateGrid(5,5)\n'],
-['wx.grid', 'Grid', 'size=(400,200)', 'w.CreateGrid(100,50)\n'],
-['wx', 'Button', 'wx.ID_SAVE', ''],
-]
+++ /dev/null
-"""
-This test app is meant to help check if a widget has a good
-DoGetBestSize method (in C++) by allowing the user to dynamically
-create any non-toplevel widget which will be placed in a Sizer
-designed to show how the widget will be sized naturally.
-"""
-
-import wx
-import sys
-import os
-
-# stuff for debugging
-print "wx.VERSION_STRING = ", wx.VERSION_STRING
-print "pid:", os.getpid()
-##raw_input("Press Enter...")
-
-class LayoutTestFrame(wx.Frame):
- def __init__(self):
- wx.Frame.__init__(self, None, -1, "Widget Layout Tester")
-
- p = wx.Panel(self)
-
- # Create control widgets
- self.testHistory = wx.ListBox(p, -1, size=(150, 300))
- self.moduleName = wx.TextCtrl(p, -1, "wx")
- self.className = wx.TextCtrl(p, -1, "")
- self.parameters = wx.TextCtrl(p, -1, "")
- self.postCreate = wx.TextCtrl(p, -1, "", size=(1,75),
- style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
- self.expression = wx.TextCtrl(p, -1, "", style=wx.TE_READONLY)
- self.docstring = wx.TextCtrl(p, -1, "", size=(1,75),
- style=wx.TE_READONLY|wx.TE_MULTILINE|wx.TE_DONTWRAP)
-
- self.expression.SetBackgroundColour(
- wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
- self.docstring.SetBackgroundColour(
- wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
-
-
- addBtn = wx.Button(p, -1, "Add")
- remBtn = wx.Button(p, -1, "Remove")
- repBtn = wx.Button(p, -1, "Replace")
- createBtn = wx.Button(p, -1, " Create Widget ")
- createBtn.SetDefault()
- destroyBtn = wx.Button(p, -1, "Destroy Widget")
- clearBtn = wx.Button(p, -1, "Clear")
-
- self.createBtn = createBtn
- self.destroyBtn = destroyBtn
-
- bottomPanel = wx.Panel(p, style=wx.SUNKEN_BORDER, name="bottomPanel")
- bottomPanel.SetMinSize((640,240))
- bottomPanel.SetOwnBackgroundColour("light blue")
-
- self.testPanel = wx.Panel(bottomPanel, name="testPanel")
- self.testPanel.SetOwnBackgroundColour((205, 183, 181)) # mistyrose3
- self.testWidget = None
-
- self.infoPane = InfoPane(p)
-
- # setup event bindings
- self.Bind(wx.EVT_TEXT, self.OnUpdate, self.moduleName)
- self.Bind(wx.EVT_TEXT, self.OnUpdate, self.className)
- self.Bind(wx.EVT_TEXT, self.OnUpdate, self.parameters)
- self.Bind(wx.EVT_UPDATE_UI, self.OnEnableCreate, createBtn)
- self.Bind(wx.EVT_UPDATE_UI, self.OnEnableDestroy, destroyBtn)
- self.Bind(wx.EVT_BUTTON, self.OnCreateWidget, createBtn)
- self.Bind(wx.EVT_BUTTON, self.OnDestroyWidget, destroyBtn)
- self.Bind(wx.EVT_BUTTON, self.OnClear, clearBtn)
-
- self.Bind(wx.EVT_CLOSE, self.OnSaveHistory)
-
- self.Bind(wx.EVT_BUTTON, self.OnAddHistory, addBtn)
- self.Bind(wx.EVT_BUTTON, self.OnRemoveHistory, remBtn)
- self.Bind(wx.EVT_BUTTON, self.OnReplaceHistory, repBtn)
- self.Bind(wx.EVT_LISTBOX, self.OnHistorySelect, self.testHistory)
- self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnHistoryActivate, self.testHistory)
-
- if 'wxMac' in wx.PlatformInfo or 'wxGTK' in wx.PlatformInfo:
- self.testHistory.Bind(wx.EVT_KEY_DOWN, self.OnHistoryKey)
-
-
- # setup the layout
- mainSizer = wx.BoxSizer(wx.VERTICAL)
- topSizer = wx.BoxSizer(wx.HORIZONTAL)
- ctlsSizer = wx.FlexGridSizer(2, 2, 5, 5)
- ctlsSizer.AddGrowableCol(1)
- btnSizer = wx.BoxSizer(wx.HORIZONTAL)
-
- topSizer.Add(self.testHistory, 0, wx.RIGHT, 30)
-
- ctlsSizer.Add(wx.StaticText(p, -1, "Module name:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
- mcSizer = wx.BoxSizer(wx.HORIZONTAL)
- mcSizer.Add(self.moduleName, 0, 0)
- mcSizer.Add(wx.StaticText(p, -1, "Class name:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL |wx.LEFT|wx.RIGHT, 10)
- mcSizer.Add(self.className, 1, 0)
- ctlsSizer.Add(mcSizer, 0, wx.EXPAND)
-
- ctlsSizer.Add(wx.StaticText(p, -1, "Parameters:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
- ctlsSizer.Add(self.parameters, 0, wx.EXPAND)
- ctlsSizer.Add(wx.StaticText(p, -1, "Create Expr:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
- ctlsSizer.Add(self.expression, 0, wx.EXPAND)
- ctlsSizer.Add(wx.StaticText(p, -1, "Post create:"), 0, wx.ALIGN_RIGHT)
- ctlsSizer.Add(self.postCreate, 0, wx.EXPAND)
- ctlsSizer.Add(wx.StaticText(p, -1, "DocString:"), 0, wx.ALIGN_RIGHT)
- ctlsSizer.Add(self.docstring, 0, wx.EXPAND)
- ctlsSizer.AddGrowableRow(4)
- topSizer.Add(ctlsSizer, 1, wx.EXPAND)
-
- btnSizer.Add((5,5))
- btnSizer.Add(addBtn, 0, wx.RIGHT, 5)
- btnSizer.Add(remBtn, 0, wx.RIGHT, 5)
- btnSizer.Add(repBtn, 0, wx.RIGHT, 5)
- btnSizer.Add((0,0), 1)
- btnSizer.Add(createBtn, 0, wx.RIGHT, 5)
- btnSizer.Add(destroyBtn, 0, wx.RIGHT, 5)
- btnSizer.Add(clearBtn, 0, wx.RIGHT, 5)
- btnSizer.Add((0,0), 1)
-
- mainSizer.Add(topSizer, 0, wx.EXPAND|wx.ALL, 10)
- mainSizer.Add(btnSizer, 0, wx.EXPAND)
- mainSizer.Add((10,10))
- ##mainSizer.Add(wx.StaticLine(p, -1), 0, wx.EXPAND)
- mainSizer.Add(bottomPanel, 1, wx.EXPAND)
-
- mainSizer.Add(self.infoPane, 0, wx.EXPAND)
-
- self.bottomSizer = sz = wx.BoxSizer(wx.VERTICAL)
- sz.Add((0,0), 1)
- sz.Add(self.testPanel, 0, wx.ALIGN_CENTER)
- sz.Add((0,0), 1)
- bottomPanel.SetSizer(sz)
-
- p.SetSizerAndFit(mainSizer)
- self.Fit()
-
- self.PopulateHistory()
-
-
-
-
- def PopulateHistory(self):
- """
- Load and eval a list of lists from a file, load the contents
- into self.testHistory
- """
- fname = os.path.join(os.path.dirname(sys.argv[0]),
- 'widgetLayoutTest.cfg')
- try:
- self.history = eval(open(fname).read())
- except:
- self.history = []
-
- self.testHistory.Clear()
-
- for idx in range(len(self.history)):
- item = self.history[idx]
- # check if it is too short
- while len(item) < 4:
- item.append('')
-
- # add it to the listbox
- self.testHistory.Append(item[0] + '.' + item[1])
-
- self.needSaved = False
-
-
- def OnSaveHistory(self, evt):
- if self.needSaved:
- fname = os.path.join(os.path.dirname(sys.argv[0]),
- 'widgetLayoutTest.cfg')
- f = open(fname, 'wb')
- f.write('[\n')
- for item in self.history:
- f.write(str(item) + ',\n')
- f.write(']\n')
- f.close()
- evt.Skip()
-
-
- def OnAddHistory(self, evt):
- moduleName = self.moduleName.GetValue()
- className = self.className.GetValue()
- parameters = self.parameters.GetValue()
- postCreate = self.postCreate.GetValue()
-
- item = [str(moduleName), str(className), str(parameters), str(postCreate)]
- self.history.append(item)
- self.testHistory.Append(item[0] + '.' + item[1])
-
- self.testHistory.SetSelection(len(self.history)-1)
- self.needSaved = True
-
-
- def OnRemoveHistory(self, evt):
- idx = self.testHistory.GetSelection()
- if idx != wx.NOT_FOUND:
- del self.history[idx]
- self.testHistory.Delete(idx)
- self.needSaved = True
- self.OnClear(None)
-
-
- def OnReplaceHistory(self, evt):
- idx = self.testHistory.GetSelection()
- if idx != wx.NOT_FOUND:
- moduleName = self.moduleName.GetValue()
- className = self.className.GetValue()
- parameters = self.parameters.GetValue()
- postCreate = self.postCreate.GetValue()
-
- item = [str(moduleName), str(className), str(parameters), str(postCreate)]
- self.history[idx] = item
- self.testHistory.SetString(idx, item[0] + '.' + item[1])
- self.needSaved = True
-
-
- def OnHistorySelect(self, evt):
- #idx = self.testHistory.GetSelection()
- idx = evt.GetInt()
- if idx != wx.NOT_FOUND:
- item = self.history[idx]
- self.moduleName.SetValue(item[0])
- self.className.SetValue(item[1])
- self.parameters.SetValue(item[2])
- self.postCreate.SetValue(item[3])
- if 'wxMac' in wx.PlatformInfo:
- self.OnUpdate(None)
-
-
- def OnHistoryActivate(self, evt):
- btn = self.testHistory.GetParent().GetDefaultItem()
- if btn is not None:
- e = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId())
- btn.Command(e)
-
-
- def OnHistoryKey(self, evt):
- key = evt.GetKeyCode()
- if key == wx.WXK_RETURN:
- self.OnHistoryActivate(None)
- else:
- evt.Skip()
-
-
- def OnUpdate(self, evt):
- # get the details from the form
- moduleName = self.moduleName.GetValue()
- className = self.className.GetValue()
- parameters = self.parameters.GetValue()
-
- expr = "w = %s.%s( testPanel, %s )" % (moduleName, className, parameters)
- self.expression.SetValue(expr)
-
- docstring = None
- try:
- docstring = eval("%s.%s.__init__.__doc__" % (moduleName, className))
- except:
- pass
- if docstring is not None:
- self.docstring.SetValue(docstring)
- else:
- self.docstring.SetValue("")
-
- def OnEnableDestroy(self, evt):
- evt.Enable(self.testWidget is not None)
-
- def OnEnableCreate(self, evt):
- evt.Enable(self.testWidget is None)
-
-
- def OnCreateWidget(self, evt):
- if self.testWidget is not None:
- return
-
- testPanel = self.testPanel
-
- # get the details from the form
- moduleName = self.moduleName.GetValue()
- className = self.className.GetValue()
- parameters = self.parameters.GetValue()
- expr = self.expression.GetValue()[4:]
- postCreate = self.postCreate.GetValue()
- if 'wxMac' in wx.PlatformInfo:
- postCreate = postCreate.replace('\r', '\n')
-
- # make sure the module is imported already
- if not sys.modules.has_key(moduleName):
- try:
- m = __import__(moduleName)
- except importError:
- wx.MessageBox("Unable to import module!", "Error")
- return
-
- # create the widget
- try:
- w = eval(expr)
- except Exception, e:
- wx.MessageBox("Got a '%s' Exception!" % e.__class__.__name__, "Error")
- import traceback
- traceback.print_exc()
- return
-
- # Is there postCreate code?
- if postCreate:
- ns = {}
- ns.update(globals())
- ns.update(locals())
- try:
- exec postCreate in ns
- except Exception, e:
- wx.MessageBox("Got a '%s' Exception!" % e.__class__.__name__, "Error")
- import traceback
- traceback.print_exc()
- w.Destroy()
- return
-
- # Put the widget in a sizer and the sizer in the testPanel
- sizer = wx.BoxSizer(wx.VERTICAL)
- sizer.Add(w, 0, wx.ALL, 5)
- self.testPanel.SetSizer(sizer)
- self.testWidget = w
- self.bottomSizer.Layout()
-
- # make the destroy button be default now
- self.destroyBtn.SetDefault()
-
- self.infoPane.Update(w, testPanel)
-
-
- def OnDestroyWidget(self, evt):
- self.testWidget.Destroy()
- self.testWidget = None
- self.testPanel.SetSizer(None, True)
- self.testPanel.Refresh()
-
- # ensure the panel shrinks again now that it has no sizer
- self.testPanel.SetMinSize((20,20))
- self.bottomSizer.Layout()
- self.testPanel.SetMinSize(wx.DefaultSize)
-
- # make the create button be default now
- self.createBtn.SetDefault()
-
- self.infoPane.Clear()
-
-
- def OnClear(self, evt):
- self.moduleName.SetValue("")
- self.className.SetValue("")
- self.parameters.SetValue("")
- self.expression.SetValue("")
- self.docstring.SetValue("")
- self.postCreate.SetValue("")
-
-
-
-
-
-
-class InfoPane(wx.Panel):
- """
- This class is used to display details of various properties of the
- widget and the testPanel to aid with debugging.
- """
- def __init__(self, parent):
- wx.Panel.__init__(self, parent)
-
- # create subwidgets
- self.wPane = SizeInfoPane(self, "Widget")
- self.tpPane= SizeInfoPane(self, "testPanel")
- self.cPane = ColourInfoPanel(self, "Widget colours")
-
- # Setup the layout
- sizer = wx.BoxSizer(wx.HORIZONTAL)
- sizer.Add(self.wPane, 0, wx.EXPAND|wx.ALL, 5)
- sizer.Add(self.tpPane, 0, wx.EXPAND|wx.ALL, 5)
- sizer.Add(self.cPane, 0, wx.EXPAND|wx.ALL, 5)
-
- self.SetSizer(sizer)
-
-
- def Update(self, w, tp):
- self.wPane.Update(w)
- self.tpPane.Update(tp)
- self.cPane.Update(w)
-
- def Clear(self):
- self.wPane.Clear()
- self.tpPane.Clear()
- self.cPane.Clear()
-
-
-
-class SizeInfoPane(wx.Panel):
- """
- A component of the InfoPane that shows vaious window size attributes.
- """
- def __init__(self, parent, label):
- wx.Panel.__init__(self, parent)
-
- # create subwidgets
- sb = wx.StaticBox(self, -1, label)
- self._size = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
- self._minsize = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
- self._bestsize = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
- self._adjbstsize = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
- self._bestfit = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
-
- # setup the layout
- fgs = wx.FlexGridSizer(2, 2, 5, 5)
- fgs.AddGrowableCol(1)
-
- fgs.Add(wx.StaticText(self, -1, "Size:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
- fgs.Add(self._size, 0, wx.EXPAND)
-
- fgs.Add(wx.StaticText(self, -1, "MinSize:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
- fgs.Add(self._minsize, 0, wx.EXPAND)
-
- fgs.Add(wx.StaticText(self, -1, "BestSize:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
- fgs.Add(self._bestsize, 0, wx.EXPAND)
-
- fgs.Add(wx.StaticText(self, -1, "AdjustedBestSize:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
- fgs.Add(self._adjbstsize, 0, wx.EXPAND)
-
- fgs.Add(wx.StaticText(self, -1, "BestFittingSize:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
- fgs.Add(self._bestfit, 0, wx.EXPAND)
-
- sbs = wx.StaticBoxSizer(sb, wx.VERTICAL)
- sbs.Add(fgs, 0, wx.EXPAND|wx.ALL, 4)
-
- self.SetSizer(sbs)
-
-
- def Update(self, win):
- self._size.SetValue( str(win.GetSize()) )
- self._minsize.SetValue( str(win.GetMinSize()) )
- self._bestsize.SetValue( str(win.GetBestSize()) )
- self._adjbstsize.SetValue( str(win.GetAdjustedBestSize()) )
- self._bestfit.SetValue( str(win.GetEffectiveMinSize()) )
-
- def Clear(self):
- self._size.SetValue("")
- self._minsize.SetValue("")
- self._bestsize.SetValue("")
- self._adjbstsize.SetValue("")
- self._bestfit.SetValue("")
-
-
-
-class ColourInfoPanel(wx.Panel):
- """
- A component of the InfoPane that shows fg and bg colour attributes.
- """
- def __init__(self, parent, label):
- wx.Panel.__init__(self, parent)
-
- # create subwidgets
- sb = wx.StaticBox(self, -1, label)
- self._fgtxt = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
- self._fgclr = wx.Panel(self, style=wx.SIMPLE_BORDER)
- self._fgclr.SetMinSize((20,20))
- self._bgtxt = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
- self._bgclr = wx.Panel(self, style=wx.SIMPLE_BORDER)
- self._bgclr.SetMinSize((20,20))
-
- # setup the layout
- fgs = wx.FlexGridSizer(2, 3, 5, 5)
- fgs.AddGrowableCol(1)
-
- fgs.Add(wx.StaticText(self, -1, "FG colour:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
- fgs.Add(self._fgtxt, 0, wx.EXPAND)
- fgs.Add(self._fgclr)
-
- fgs.Add(wx.StaticText(self, -1, "BG colour:"),
- 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
- fgs.Add(self._bgtxt, 0, wx.EXPAND)
- fgs.Add(self._bgclr)
-
- sbs = wx.StaticBoxSizer(sb, wx.VERTICAL)
- sbs.Add(fgs, 0, wx.EXPAND|wx.ALL, 4)
-
- self.SetSizer(sbs)
-
-
- def Update(self, win):
- def clr2hex(c, cp):
- cp.SetBackgroundColour(c)
- cp.Refresh()
- return "#%02X%02X%02X" % c.Get()
-
- self._fgtxt.SetValue( clr2hex(win.GetForegroundColour(), self._fgclr) )
- self._bgtxt.SetValue( clr2hex(win.GetBackgroundColour(), self._bgclr) )
-
-
- def Clear(self):
- self._fgtxt.SetValue("")
- self._bgtxt.SetValue("")
- self._fgclr.SetBackgroundColour(self.GetBackgroundColour())
- self._bgclr.SetBackgroundColour(self.GetBackgroundColour())
- self._fgclr.Refresh()
- self._bgclr.Refresh()
-
-
-
-
-app = wx.PySimpleApp(redirect=False)
-frame = LayoutTestFrame()
-app.SetTopWindow(frame)
-frame.Show()
-app.MainLoop()
-
| wx.PD_REMAINING_TIME)
self.count += 1
- keepGoing = self.dialog.Update(self.count)
+ if wx.VERSION < (2,7,1,1):
+ keepGoing = self.dialog.Update(self.count)
+ else:
+ (keepGoing, skip) = self.dialog.Update(self.count)
if not keepGoing or self.count == self.progressMax:
self.dialog.Destroy()
self.timer.Stop()
'wx.lib.colourchooser',
'wx.lib.editor',
'wx.lib.floatcanvas',
+ 'wx.lib.floatcanvas.Utilities',
'wx.lib.masked',
'wx.lib.mixins',
'wx.lib.ogl',
during each event loop iteration.", "");
+ DocDeclStr(
+ bool , HasPendingEvents() const,
+ "Check if there are pending events on global pending event list", "");
+
+
DocDeclStr(
virtual bool, Yield(bool onlyIfNeeded = false),
"Process all currently pending events right now, instead of waiting
DocDeclAStrName(
void, GetTextExtent(const wxString& string,
wxCoord *OUTPUT, wxCoord *OUTPUT, wxCoord *OUTPUT, wxCoord* OUTPUT,
- wxFont* font = NULL),
+ const wxFont* font = NULL),
"GetFullTextExtent(wxString string, Font font=None) ->\n (width, height, descent, externalLeading)",
"Get the width, height, decent and leading of the text using the
current or specified font. Only works for single line strings.", "",
DocDeclAStr(
void, GetMultiLineTextExtent(const wxString& text,
wxCoord *OUTPUT, wxCoord *OUTPUT, wxCoord *OUTPUT,
- wxFont *font = NULL),
+ const wxFont *font = NULL),
"GetMultiLineTextExtent(wxString string, Font font=None) ->\n (width, height, lineHeight)",
"Get the width, height, and line height of the text using the
current or specified font. Works for single as well as multi-line
wxITEM_NORMAL,
wxITEM_CHECK,
wxITEM_RADIO,
+ wxITEM_DROPDOWN,
wxITEM_MAX
};
%constant wxEventType wxEVT_COMMAND_COMBOBOX_SELECTED;
%constant wxEventType wxEVT_COMMAND_TOOL_RCLICKED;
%constant wxEventType wxEVT_COMMAND_TOOL_ENTER;
+%constant wxEventType wxEVT_COMMAND_TOOL_DROPDOWN_CLICKED;
// Mouse event types
%constant wxEventType wxEVT_LEFT_DOWN;
EVT_TOOL_RCLICKED = wx.PyEventBinder( wxEVT_COMMAND_TOOL_RCLICKED, 1)
EVT_TOOL_RCLICKED_RANGE = wx.PyEventBinder( wxEVT_COMMAND_TOOL_RCLICKED, 2)
EVT_TOOL_ENTER = wx.PyEventBinder( wxEVT_COMMAND_TOOL_ENTER, 1)
+EVT_TOOL_DROPDOWN = wx.PyEventBinder( wxEVT_COMMAND_TOOL_DROPDOWN_CLICKED, 1)
EVT_CHECKLISTBOX = wx.PyEventBinder( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, 1)
#if 0 // #ifdef __WXMAC__
// A dummy class that raises an exception if used...
-class wxEventLoop
+class wxEventLoopBase
{
public:
- wxEventLoop() { wxPyRaiseNotImplemented(); }
+ wxEventLoopBase() { wxPyRaiseNotImplemented(); }
+ bool IsOk() const { return false; }
int Run() { return 0; }
void Exit(int rc = 0) {}
bool Pending() const { return false; }
bool Dispatch() { return false; }
bool IsRunning() const { return false; }
+ void WakeUp() {}
static wxEventLoop *GetActive() { wxPyRaiseNotImplemented(); return NULL; }
static void SetActive(wxEventLoop* loop) { wxPyRaiseNotImplemented(); }
};
#endif
%}
-class wxEventLoop
+class wxEventLoopBase
{
public:
- wxEventLoop();
+ wxEventLoopBase();
virtual ~wxEventLoop();
+ // use this to check whether the event loop was successfully created before
+ // using it
+ virtual bool IsOk() const;
+
// start the event loop, return the exit code when it is finished
virtual int Run();
// is the event loop running now?
virtual bool IsRunning() const;
+ virtual void WakeUp();
+
// return currently active (running) event loop, may be NULL
static wxEventLoop *GetActive();
};
+class wxEventLoopManual : public wxEventLoopBase
+{
+public:
+ wxEventLoopManual();
+};
+
+
+class wxGUIEventLoop : public wxEventLoopBase
+{
+public:
+ wxGUIEventLoop();
+};
+
+
+%pythoncode {
+ class EventLoop(GUIEventLoop):
+ """Class using the old name for compatibility."""
+ pass
+}
+
+
+class wxModalEventLoop : public wxGUIEventLoop
+{
+public:
+ wxModalEventLoop(wxWindow *winModal)
+};
+
+
// This object sets the wxEventLoop given to the ctor as the currently active
// one and unsets it in its dtor, this is especially useful in presence of
wxWindow* wxGetTopLevelParent(wxWindow *win);
+// flags for wxLaunchDefaultBrowser
+enum
+{
+ wxBROWSER_NEW_WINDOW = 1
+};
+
DocDeclStr(
- bool , wxLaunchDefaultBrowser(const wxString& url),
+ bool , wxLaunchDefaultBrowser(const wxString& url, int flags = 0),
"Launches the user's default browser and tells it to open the location
at ``url``. Returns ``True`` if the application was successfully
launched.", "");
wxPyPanel(wxWindow* parent, const wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
- long style = 0,
+ long style = wxTAB_TRAVERSAL | wxNO_BORDER,
const wxString& name = wxPyPanelNameStr)
: wxPanel(parent, id, pos, size, style, name) {}
wxPyPanel(wxWindow* parent, const wxWindowID id=-1,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
- long style = 0,
+ long style = wxTAB_TRAVERSAL | wxNO_BORDER,
const wxString& name = wxPyPanelNameStr);
%RenameCtor(PrePyPanel, wxPyPanel());
wxPyScrolledWindow(wxWindow* parent, const wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
- long style = 0,
+ long style = wxHSCROLL | wxVSCROLL,
const wxString& name = wxPyPanelNameStr)
: wxScrolledWindow(parent, id, pos, size, style, name) {}
wxPyScrolledWindow(wxWindow* parent, const wxWindowID id=-1,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
- long style = 0,
+ long style = wxHSCROLL | wxVSCROLL,
const wxString& name = wxPyPanelNameStr);
%RenameCtor(PrePyScrolledWindow, wxPyScrolledWindow());
%{
#if !wxUSE_SOUND
-// A C++ stub class for wxWave for platforms that don't have it.
+// A C++ stub class for wxSound for platforms that don't have it.
+
+enum wxSoundFlags
+{
+ wxSOUND_SYNC,
+ wxSOUND_ASYNC,
+ wxSOUND_LOOP
+};
+
class wxSound : public wxObject
{
public:
public:
wxTimerEvent(int timerid = 0, int interval = 0);
int GetInterval() const;
-
+ wxTimer& GetTimer() const;
+
%property(Interval, GetInterval, doc="See `GetInterval`");
+ %property(Timer, GetTimer);
};
void Detach();
void Attach(wxToolBarBase *tbar);
+ // these methods are only for tools of wxITEM_DROPDOWN kind (but even such
+ // tools can have a NULL associated menu)
+ void SetDropdownMenu(wxMenu *menu);
+ wxMenu *GetDropdownMenu() const;
+
//wxObject *GetClientData();
%extend {
// convert the ClientData back to a PyObject
size_t GetToolsCount() const;
+ // Set dropdown menu
+ bool SetDropdownMenu(int toolid, wxMenu *menu);
+
+
%property(Margins, GetMargins, SetMargins, doc="See `GetMargins` and `SetMargins`");
%property(MaxCols, GetMaxCols, doc="See `GetMaxCols`");
%property(MaxRows, GetMaxRows, doc="See `GetMaxRows`");
virtual void RefreshRow(size_t row);
virtual void RefreshRows(size_t from, size_t to);
- virtual int HitTest(wxCoord y) const;
-
-
size_t GetRowCount() const;
size_t GetVisibleRowsBegin() const;
size_t GetVisibleRowsEnd() const;
virtual void RefreshColumn(size_t column);
virtual void RefreshColumns(size_t from, size_t to);
- virtual int HitTest(wxCoord x) const;
-
size_t GetColumnCount() const;
size_t GetVisibleColumnsBegin() const;
const wxPosition& to);
// Override wxPanel::HitTest to use our version
-// virtual wxPosition HitTest(wxCoord x, wxCoord y) const;
- virtual wxPosition HitTest(const wxPoint &pos) const;
+// wxPosition VirtualHitTest(wxCoord x, wxCoord y) const;
+ wxPosition VirtualHitTest(const wxPoint &pos) const;
// replacement implementation of wxWindow::Layout virtual method. To
// properly forward calls to wxWindow::Layout use
const wxString& name = wxPyPanelNameStr);
- int HitTest(const wxPoint& pt) const;
wxCoord GetColumnsWidth(size_t columnMin, size_t columnMax) const;
wxCoord EstimateTotalWidth() const;
};
const wxString& name = wxPyPanelNameStr);
- wxPosition HitTest(const wxPoint& pt) const;
-
wxCoord GetRowsHeight(size_t lineMin, size_t lineMax) const;
wxCoord EstimateTotalHeight() const;
void , PushEventHandler( wxEvtHandler *handler ),
"Pushes this event handler onto the event handler stack for the window.
An event handler is an object that is capable of processing the events
-sent to a window. (In other words, is able to dispatch the events to
+sent to a window. (In other words, is able to dispatch the events to a
handler function.) By default, the window is its own event handler,
but an application may wish to substitute another, for example to
allow central implementation of event-handling for a variety of
**Usage**
The following example shows a simple implementation that utilizes
-`wx.aui.FrameManager` to manage three text controls in a frame window::
+`wx.aui.AuiManager` to manage three text controls in a frame window::
import wx
import wx.aui
const wxPaneInfo& pane_info,
const wxPoint& drop_pos);
-// A typemap for the return value of wxFrameManager::GetAllPanes
+// A typemap for the return value of wxAuiManager::GetAllPanes
%typemap(out) wxAuiPaneInfoArray& {
$result = PyList_New(0);
for (size_t i=0; i < $1->GetCount(); i++) {
}
}
+//%ignore wxAuiManager::~wxAuiManager;
%nokwargs wxAuiTabContainer::SetActivePage;
#undef wxColor
//---------------------------------------------------------------------------
-// Methods to inject into the FrameManager class that will sort out calls to
+// Methods to inject into the AuiManager class that will sort out calls to
// the overloaded versions of GetPane and AddPane
%extend wxAuiManager {
widget reference or by pane name, which acts as a unique id
for a window pane. The returned `PaneInfo` object may then be
modified to change a pane's look, state or position. After one
- or more modifications to the `PaneInfo`, `FrameManager.Update`
+ or more modifications to the `PaneInfo`, `AuiManager.Update`
should be called to realize the changes to the user interface.
If the lookup failed (meaning the pane could not be found in
Pushes this event handler onto the event handler stack for the window.
An event handler is an object that is capable of processing the events
- sent to a window. (In other words, is able to dispatch the events to
+ sent to a window. (In other words, is able to dispatch the events to a
handler function.) By default, the window is its own event handler,
but an application may wish to substitute another, for example to
allow central implementation of event-handling for a variety of
#if !wxUSE_SOUND
// A C++ stub class for wxWave for platforms that don't have it.
+
+enum wxSoundFlags
+{
+ wxSOUND_SYNC,
+ wxSOUND_ASYNC,
+ wxSOUND_LOOP
+};
+
class wxSound : public wxObject
{
public:
//Py_INCREF(deadObjectClass);
Py_DECREF(klass);
Py_DECREF(name);
+ Py_DECREF(dict);
}
}
Pushes this event handler onto the event handler stack for the window.
An event handler is an object that is capable of processing the events
- sent to a window. (In other words, is able to dispatch the events to
+ sent to a window. (In other words, is able to dispatch the events to a
handler function.) By default, the window is its own event handler,
but an application may wish to substitute another, for example to
allow central implementation of event-handling for a variety of
Pushes this event handler onto the event handler stack for the window.
An event handler is an object that is capable of processing the events
- sent to a window. (In other words, is able to dispatch the events to
+ sent to a window. (In other words, is able to dispatch the events to a
handler function.) By default, the window is its own event handler,
but an application may wish to substitute another, for example to
allow central implementation of event-handling for a variety of
-import wx, wx.gizmos, wx.lib.customtreectrl, unittest, treemixin
+import wx, wx.gizmos, wx.lib.customtreectrl, unittest
+try:
+ import treemixin
+except ImportError:
+ from wx.lib.mixins import treemixin
+
# VirtualTree tests
--- /dev/null
+import wx
+
+def OnClick(evt):
+ print 'Click'
+
+def OnDClick(evt):
+ print 'DClick'
+
+def OnMouse(evt):
+ if evt.LeftDClick():
+ print 'DClick'
+ elif evt.LeftDown():
+ print 'Click'
+
+app = wx.App(redirect=False)
+frame = wx.Frame(None, title="Test mouse clicks")
+panel = wx.Panel(frame)
+
+if True:
+ # try separate
+ panel.Bind(wx.EVT_LEFT_DOWN, OnClick)
+ panel.Bind(wx.EVT_LEFT_DCLICK, OnDClick)
+else:
+ # or together
+ panel.Bind(wx.EVT_MOUSE_EVENTS, OnMouse)
+
+frame.Show()
+app.MainLoop()
--- /dev/null
+import wx
+
+class Frame(wx.Frame):
+ def __init__(self):
+ wx.Frame.__init__(self, None, title="Am I transparent?")
+ self.amount = 255
+ self.delta = -3
+
+ p = wx.Panel(self)
+ self.st = wx.StaticText(p, -1, str(self.amount), (25,25))
+ self.st.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.NORMAL))
+
+ self.timer = wx.Timer(self)
+ self.timer.Start(25)
+ self.Bind(wx.EVT_TIMER, self.AlphaCycle)
+
+ self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
+
+
+ def OnCloseWindow(self, evt):
+ self.timer.Stop()
+ del self.timer
+ self.Destroy()
+
+
+ def AlphaCycle(self, evt):
+ self.amount += self.delta
+ if self.amount == 0 or self.amount == 255:
+ self.delta = -self.delta
+ self.st.SetLabel(str(self.amount))
+
+ # Note that we no longer need to use ctypes or win32api to
+ # make transparent windows, however I'm not removing the
+ # MakeTransparent code from this sample as it may be helpful
+ # for somebody someday.
+ #self.MakeTransparent(self.amount)
+
+ # Instead we'll just call the SetTransparent method
+ self.SetTransparent(self.amount)
+
+
+ def MakeTransparent(self, amount):
+ hwnd = self.GetHandle()
+ try:
+ import ctypes
+ _winlib = ctypes.windll.user32
+ style = _winlib.GetWindowLongA(hwnd, 0xffffffecL)
+ style |= 0x00080000
+ _winlib.SetWindowLongA(hwnd, 0xffffffecL, style)
+ _winlib.SetLayeredWindowAttributes(hwnd, 0, amount, 2)
+
+ except ImportError:
+ import win32api, win32con, winxpgui
+ _winlib = win32api.LoadLibrary("user32")
+ pSetLayeredWindowAttributes = win32api.GetProcAddress(
+ _winlib, "SetLayeredWindowAttributes")
+ if pSetLayeredWindowAttributes == None:
+ return
+ exstyle = win32api.GetWindowLong(hwnd, win32con.GWL_EXSTYLE)
+ if 0 == (exstyle & 0x80000):
+ win32api.SetWindowLong(hwnd,
+ win32con.GWL_EXSTYLE,
+ exstyle | 0x80000)
+ winxpgui.SetLayeredWindowAttributes(hwnd, 0, amount, 2)
+
+
+
+app = wx.App(False)
+frm = Frame()
+frm.Show()
+app.MainLoop()
--- /dev/null
+"""
+This test app is meant to help check if a widget has a good
+DoGetBestSize method (in C++) by allowing the user to dynamically
+create any non-toplevel widget which will be placed in a Sizer
+designed to show how the widget will be sized naturally.
+"""
+
+import wx
+import sys
+import os
+
+# stuff for debugging
+print "wx.VERSION_STRING = ", wx.VERSION_STRING
+print "pid:", os.getpid()
+##raw_input("Press Enter...")
+
+testImage = os.path.join(os.path.dirname(sys.argv[0]), 'image.png')
+
+
+class LayoutTestFrame(wx.Frame):
+ def __init__(self):
+ wx.Frame.__init__(self, None, -1, "Widget Layout Tester")
+
+ p = wx.Panel(self)
+
+ # Create control widgets
+ self.testHistory = wx.ListBox(p, -1, size=(150, 300))
+ self.moduleName = wx.TextCtrl(p, -1, "wx")
+ self.className = wx.TextCtrl(p, -1, "")
+ self.parameters = wx.TextCtrl(p, -1, "")
+ self.postCreate = wx.TextCtrl(p, -1, "", size=(1,75),
+ style=wx.TE_MULTILINE|wx.TE_DONTWRAP)
+ self.expression = wx.TextCtrl(p, -1, "", style=wx.TE_READONLY)
+ self.docstring = wx.TextCtrl(p, -1, "", size=(1,75),
+ style=wx.TE_READONLY|wx.TE_MULTILINE|wx.TE_DONTWRAP)
+
+ self.expression.SetBackgroundColour(
+ wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
+ self.docstring.SetBackgroundColour(
+ wx.SystemSettings.GetColour(wx.SYS_COLOUR_INFOBK))
+
+
+ addBtn = wx.Button(p, -1, "Add")
+ remBtn = wx.Button(p, -1, "Remove")
+ repBtn = wx.Button(p, -1, "Replace")
+ createBtn = wx.Button(p, -1, " Create Widget ")
+ createBtn.SetDefault()
+ destroyBtn = wx.Button(p, -1, "Destroy Widget")
+ clearBtn = wx.Button(p, -1, "Clear")
+
+ self.createBtn = createBtn
+ self.destroyBtn = destroyBtn
+
+ bottomPanel = wx.Panel(p, style=wx.SUNKEN_BORDER, name="bottomPanel")
+ bottomPanel.SetMinSize((640,240))
+ bottomPanel.SetOwnBackgroundColour("light blue")
+
+ self.testPanel = wx.Panel(bottomPanel, name="testPanel")
+ self.testPanel.SetOwnBackgroundColour((205, 183, 181)) # mistyrose3
+ self.testWidget = None
+
+ self.infoPane = InfoPane(p)
+
+ # setup event bindings
+ self.Bind(wx.EVT_TEXT, self.OnUpdate, self.moduleName)
+ self.Bind(wx.EVT_TEXT, self.OnUpdate, self.className)
+ self.Bind(wx.EVT_TEXT, self.OnUpdate, self.parameters)
+ self.Bind(wx.EVT_UPDATE_UI, self.OnEnableCreate, createBtn)
+ self.Bind(wx.EVT_UPDATE_UI, self.OnEnableDestroy, destroyBtn)
+ self.Bind(wx.EVT_BUTTON, self.OnCreateWidget, createBtn)
+ self.Bind(wx.EVT_BUTTON, self.OnDestroyWidget, destroyBtn)
+ self.Bind(wx.EVT_BUTTON, self.OnClear, clearBtn)
+
+ self.Bind(wx.EVT_CLOSE, self.OnSaveHistory)
+
+ self.Bind(wx.EVT_BUTTON, self.OnAddHistory, addBtn)
+ self.Bind(wx.EVT_BUTTON, self.OnRemoveHistory, remBtn)
+ self.Bind(wx.EVT_BUTTON, self.OnReplaceHistory, repBtn)
+ self.Bind(wx.EVT_LISTBOX, self.OnHistorySelect, self.testHistory)
+ self.Bind(wx.EVT_LISTBOX_DCLICK, self.OnHistoryActivate, self.testHistory)
+
+ if 'wxMac' in wx.PlatformInfo or 'wxGTK' in wx.PlatformInfo:
+ self.testHistory.Bind(wx.EVT_KEY_DOWN, self.OnHistoryKey)
+
+
+ # setup the layout
+ mainSizer = wx.BoxSizer(wx.VERTICAL)
+ topSizer = wx.BoxSizer(wx.HORIZONTAL)
+ ctlsSizer = wx.FlexGridSizer(2, 2, 5, 5)
+ ctlsSizer.AddGrowableCol(1)
+ btnSizer = wx.BoxSizer(wx.HORIZONTAL)
+
+ topSizer.Add(self.testHistory, 0, wx.RIGHT, 30)
+
+ ctlsSizer.Add(wx.StaticText(p, -1, "Module name:"),
+ 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ mcSizer = wx.BoxSizer(wx.HORIZONTAL)
+ mcSizer.Add(self.moduleName, 0, 0)
+ mcSizer.Add(wx.StaticText(p, -1, "Class name:"),
+ 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL |wx.LEFT|wx.RIGHT, 10)
+ mcSizer.Add(self.className, 1, 0)
+ ctlsSizer.Add(mcSizer, 0, wx.EXPAND)
+
+ ctlsSizer.Add(wx.StaticText(p, -1, "Parameters:"),
+ 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ ctlsSizer.Add(self.parameters, 0, wx.EXPAND)
+ ctlsSizer.Add(wx.StaticText(p, -1, "Create Expr:"),
+ 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ ctlsSizer.Add(self.expression, 0, wx.EXPAND)
+ ctlsSizer.Add(wx.StaticText(p, -1, "Post create:"), 0, wx.ALIGN_RIGHT)
+ ctlsSizer.Add(self.postCreate, 0, wx.EXPAND)
+ ctlsSizer.Add(wx.StaticText(p, -1, "DocString:"), 0, wx.ALIGN_RIGHT)
+ ctlsSizer.Add(self.docstring, 0, wx.EXPAND)
+ ctlsSizer.AddGrowableRow(4)
+ topSizer.Add(ctlsSizer, 1, wx.EXPAND)
+
+ btnSizer.Add((5,5))
+ btnSizer.Add(addBtn, 0, wx.RIGHT, 5)
+ btnSizer.Add(remBtn, 0, wx.RIGHT, 5)
+ btnSizer.Add(repBtn, 0, wx.RIGHT, 5)
+ btnSizer.Add((0,0), 1)
+ btnSizer.Add(createBtn, 0, wx.RIGHT, 5)
+ btnSizer.Add(destroyBtn, 0, wx.RIGHT, 5)
+ btnSizer.Add(clearBtn, 0, wx.RIGHT, 5)
+ btnSizer.Add((0,0), 1)
+
+ mainSizer.Add(topSizer, 0, wx.EXPAND|wx.ALL, 10)
+ mainSizer.Add(btnSizer, 0, wx.EXPAND)
+ mainSizer.Add((10,10))
+ ##mainSizer.Add(wx.StaticLine(p, -1), 0, wx.EXPAND)
+ mainSizer.Add(bottomPanel, 1, wx.EXPAND)
+
+ mainSizer.Add(self.infoPane, 0, wx.EXPAND)
+
+ self.bottomSizer = sz = wx.BoxSizer(wx.VERTICAL)
+ sz.Add((0,0), 1)
+ sz.Add(self.testPanel, 0, wx.ALIGN_CENTER)
+ sz.Add((0,0), 1)
+ bottomPanel.SetSizer(sz)
+
+ p.SetSizerAndFit(mainSizer)
+ self.Fit()
+
+ self.PopulateHistory()
+
+
+
+
+ def PopulateHistory(self):
+ """
+ Load and eval a list of lists from a file, load the contents
+ into self.testHistory
+ """
+ fname = os.path.join(os.path.dirname(sys.argv[0]), 'widgets.cfg')
+ try:
+ self.history = eval(open(fname).read())
+ except:
+ self.history = []
+
+ self.testHistory.Clear()
+
+ for idx in range(len(self.history)):
+ item = self.history[idx]
+ # check if it is too short
+ while len(item) < 4:
+ item.append('')
+
+ # add it to the listbox
+ self.testHistory.Append(item[0] + '.' + item[1])
+
+ self.needSaved = False
+
+
+ def OnSaveHistory(self, evt):
+ if self.needSaved:
+ fname = os.path.join(os.path.dirname(sys.argv[0]), 'widgets.cfg')
+ f = open(fname, 'wb')
+ f.write('[\n')
+ for item in self.history:
+ f.write(str(item) + ',\n')
+ f.write(']\n')
+ f.close()
+ evt.Skip()
+
+
+ def OnAddHistory(self, evt):
+ moduleName = self.moduleName.GetValue()
+ className = self.className.GetValue()
+ parameters = self.parameters.GetValue()
+ postCreate = self.postCreate.GetValue()
+
+ item = [str(moduleName), str(className), str(parameters), str(postCreate)]
+ self.history.append(item)
+ self.testHistory.Append(item[0] + '.' + item[1])
+
+ self.testHistory.SetSelection(len(self.history)-1)
+ self.needSaved = True
+
+
+ def OnRemoveHistory(self, evt):
+ idx = self.testHistory.GetSelection()
+ if idx != wx.NOT_FOUND:
+ del self.history[idx]
+ self.testHistory.Delete(idx)
+ self.needSaved = True
+ self.OnClear(None)
+
+
+ def OnReplaceHistory(self, evt):
+ idx = self.testHistory.GetSelection()
+ if idx != wx.NOT_FOUND:
+ moduleName = self.moduleName.GetValue()
+ className = self.className.GetValue()
+ parameters = self.parameters.GetValue()
+ postCreate = self.postCreate.GetValue()
+
+ item = [str(moduleName), str(className), str(parameters), str(postCreate)]
+ self.history[idx] = item
+ self.testHistory.SetString(idx, item[0] + '.' + item[1])
+ self.needSaved = True
+
+
+ def OnHistorySelect(self, evt):
+ #idx = self.testHistory.GetSelection()
+ idx = evt.GetInt()
+ if idx != wx.NOT_FOUND:
+ item = self.history[idx]
+ self.moduleName.SetValue(item[0])
+ self.className.SetValue(item[1])
+ self.parameters.SetValue(item[2])
+ self.postCreate.SetValue(item[3])
+ if 'wxMac' in wx.PlatformInfo:
+ self.OnUpdate(None)
+
+
+ def OnHistoryActivate(self, evt):
+ btn = self.testHistory.GetTopLevelParent().GetDefaultItem()
+ if btn is not None:
+ e = wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, btn.GetId())
+ btn.Command(e)
+
+
+ def OnHistoryKey(self, evt):
+ key = evt.GetKeyCode()
+ if key == wx.WXK_RETURN:
+ self.OnHistoryActivate(None)
+ else:
+ evt.Skip()
+
+
+ def OnUpdate(self, evt):
+ # get the details from the form
+ moduleName = self.moduleName.GetValue()
+ className = self.className.GetValue()
+ parameters = self.parameters.GetValue()
+
+ expr = "w = %s.%s( testPanel, %s )" % (moduleName, className, parameters)
+ self.expression.SetValue(expr)
+
+ docstring = None
+ try:
+ docstring = eval("%s.%s.__init__.__doc__" % (moduleName, className))
+ except:
+ pass
+ if docstring is not None:
+ self.docstring.SetValue(docstring)
+ else:
+ self.docstring.SetValue("")
+
+ def OnEnableDestroy(self, evt):
+ evt.Enable(self.testWidget is not None)
+
+ def OnEnableCreate(self, evt):
+ evt.Enable(self.testWidget is None)
+
+
+ def OnCreateWidget(self, evt):
+ if self.testWidget is not None:
+ return
+
+ testPanel = self.testPanel
+
+ # get the details from the form
+ moduleName = self.moduleName.GetValue()
+ className = self.className.GetValue()
+ parameters = self.parameters.GetValue()
+ expr = self.expression.GetValue()[4:]
+ postCreate = self.postCreate.GetValue()
+ if 'wxMac' in wx.PlatformInfo:
+ postCreate = postCreate.replace('\r', '\n')
+
+ # make sure the module is imported already
+ if not sys.modules.has_key(moduleName):
+ try:
+ m = __import__(moduleName)
+ except importError:
+ wx.MessageBox("Unable to import module!", "Error")
+ return
+
+ # create the widget
+ try:
+ w = eval(expr)
+ except Exception, e:
+ wx.MessageBox("Got a '%s' Exception!" % e.__class__.__name__, "Error")
+ import traceback
+ traceback.print_exc()
+ return
+
+ # Is there postCreate code?
+ if postCreate:
+ ns = {}
+ ns.update(globals())
+ ns.update(locals())
+ try:
+ exec postCreate in ns
+ except Exception, e:
+ wx.MessageBox("Got a '%s' Exception!" % e.__class__.__name__, "Error")
+ import traceback
+ traceback.print_exc()
+ w.Destroy()
+ return
+
+ # Put the widget in a sizer and the sizer in the testPanel
+ sizer = wx.BoxSizer(wx.VERTICAL)
+ sizer.Add(w, 0, wx.ALL, 5)
+ self.testPanel.SetSizer(sizer)
+ self.testWidget = w
+ self.bottomSizer.Layout()
+
+ # make the destroy button be default now
+ self.destroyBtn.SetDefault()
+
+ self.infoPane.Update(w, testPanel)
+
+
+ def OnDestroyWidget(self, evt):
+ self.testWidget.Destroy()
+ self.testWidget = None
+ self.testPanel.SetSizer(None, True)
+ self.testPanel.Refresh()
+
+ # ensure the panel shrinks again now that it has no sizer
+ self.testPanel.SetMinSize((20,20))
+ self.bottomSizer.Layout()
+ self.testPanel.SetMinSize(wx.DefaultSize)
+
+ # make the create button be default now
+ self.createBtn.SetDefault()
+
+ self.infoPane.Clear()
+
+
+ def OnClear(self, evt):
+ self.moduleName.SetValue("")
+ self.className.SetValue("")
+ self.parameters.SetValue("")
+ self.expression.SetValue("")
+ self.docstring.SetValue("")
+ self.postCreate.SetValue("")
+
+
+
+
+
+
+class InfoPane(wx.Panel):
+ """
+ This class is used to display details of various properties of the
+ widget and the testPanel to aid with debugging.
+ """
+ def __init__(self, parent):
+ wx.Panel.__init__(self, parent)
+
+ # create subwidgets
+ self.wPane = SizeInfoPane(self, "Widget")
+ self.tpPane= SizeInfoPane(self, "testPanel")
+ self.cPane = ColourInfoPanel(self, "Widget colours")
+
+ # Setup the layout
+ sizer = wx.BoxSizer(wx.HORIZONTAL)
+ sizer.Add(self.wPane, 0, wx.EXPAND|wx.ALL, 5)
+ sizer.Add(self.tpPane, 0, wx.EXPAND|wx.ALL, 5)
+ sizer.Add(self.cPane, 0, wx.EXPAND|wx.ALL, 5)
+
+ self.SetSizer(sizer)
+
+
+ def Update(self, w, tp):
+ self.wPane.Update(w)
+ self.tpPane.Update(tp)
+ self.cPane.Update(w)
+
+ def Clear(self):
+ self.wPane.Clear()
+ self.tpPane.Clear()
+ self.cPane.Clear()
+
+
+
+class SizeInfoPane(wx.Panel):
+ """
+ A component of the InfoPane that shows vaious window size attributes.
+ """
+ def __init__(self, parent, label):
+ wx.Panel.__init__(self, parent)
+
+ # create subwidgets
+ sb = wx.StaticBox(self, -1, label)
+ self._size = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+ self._minsize = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+ self._bestsize = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+ self._effmin = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+
+ # setup the layout
+ fgs = wx.FlexGridSizer(2, 2, 5, 5)
+ fgs.AddGrowableCol(1)
+
+ fgs.Add(wx.StaticText(self, -1, "Size:"),
+ 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ fgs.Add(self._size, 0, wx.EXPAND)
+
+ fgs.Add(wx.StaticText(self, -1, "MinSize:"),
+ 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ fgs.Add(self._minsize, 0, wx.EXPAND)
+
+ fgs.Add(wx.StaticText(self, -1, "BestSize:"),
+ 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ fgs.Add(self._bestsize, 0, wx.EXPAND)
+
+ fgs.Add(wx.StaticText(self, -1, "EffectiveMinSize:"),
+ 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ fgs.Add(self._effmin, 0, wx.EXPAND)
+
+ sbs = wx.StaticBoxSizer(sb, wx.VERTICAL)
+ sbs.Add(fgs, 0, wx.EXPAND|wx.ALL, 4)
+
+ self.SetSizer(sbs)
+
+
+ def Update(self, win):
+ self._size.SetValue( str(win.GetSize()) )
+ self._minsize.SetValue( str(win.GetMinSize()) )
+ self._bestsize.SetValue( str(win.GetBestSize()) )
+ self._effmin.SetValue( str(win.GetEffectiveMinSize()) )
+
+ def Clear(self):
+ self._size.SetValue("")
+ self._minsize.SetValue("")
+ self._bestsize.SetValue("")
+ self._effmin.SetValue("")
+
+
+
+class ColourInfoPanel(wx.Panel):
+ """
+ A component of the InfoPane that shows fg and bg colour attributes.
+ """
+ def __init__(self, parent, label):
+ wx.Panel.__init__(self, parent)
+
+ # create subwidgets
+ sb = wx.StaticBox(self, -1, label)
+ self._fgtxt = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+ self._fgclr = wx.Panel(self, style=wx.SIMPLE_BORDER)
+ self._fgclr.SetMinSize((20,20))
+ self._bgtxt = wx.TextCtrl(self, -1, "", style=wx.TE_READONLY)
+ self._bgclr = wx.Panel(self, style=wx.SIMPLE_BORDER)
+ self._bgclr.SetMinSize((20,20))
+
+ # setup the layout
+ fgs = wx.FlexGridSizer(2, 3, 5, 5)
+ fgs.AddGrowableCol(1)
+
+ fgs.Add(wx.StaticText(self, -1, "FG colour:"),
+ 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ fgs.Add(self._fgtxt, 0, wx.EXPAND)
+ fgs.Add(self._fgclr)
+
+ fgs.Add(wx.StaticText(self, -1, "BG colour:"),
+ 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
+ fgs.Add(self._bgtxt, 0, wx.EXPAND)
+ fgs.Add(self._bgclr)
+
+ sbs = wx.StaticBoxSizer(sb, wx.VERTICAL)
+ sbs.Add(fgs, 0, wx.EXPAND|wx.ALL, 4)
+
+ self.SetSizer(sbs)
+
+
+ def Update(self, win):
+ def clr2hex(c, cp):
+ cp.SetBackgroundColour(c)
+ cp.Refresh()
+ return "#%02X%02X%02X" % c.Get()
+
+ self._fgtxt.SetValue( clr2hex(win.GetForegroundColour(), self._fgclr) )
+ self._bgtxt.SetValue( clr2hex(win.GetBackgroundColour(), self._bgclr) )
+
+
+ def Clear(self):
+ self._fgtxt.SetValue("")
+ self._bgtxt.SetValue("")
+ self._fgclr.SetBackgroundColour(self.GetBackgroundColour())
+ self._bgclr.SetBackgroundColour(self.GetBackgroundColour())
+ self._fgclr.Refresh()
+ self._bgclr.Refresh()
+
+
+
+
+app = wx.PySimpleApp(redirect=False)
+frame = LayoutTestFrame()
+app.SetTopWindow(frame)
+frame.Show()
+app.MainLoop()
+
--- /dev/null
+[
+['wx', 'BitmapButton', '-1, wx.Bitmap("image.png")', ''],
+['wx', 'Button', '-1, "default"', 'w.SetDefault()'],
+['wx', 'Button', '-1, "larger font"', 'w.SetFont(wx.Font(20, wx.SWISS, wx.NORMAL, wx.NORMAL))\n\n\n'],
+['wx', 'Button', '-1, "normal"', ''],
+['wx', 'Button', '-1, "with a longer, longer label"', ''],
+['wx', 'Button', 'wx.ID_SAVE', ''],
+['wx', 'CheckBox', '-1, "checkbox with longer label"', ''],
+['wx', 'CheckBox', '-1, "checkbox"', ''],
+['wx', 'CheckListBox', '-1, size=(100,-1), choices="one two three four five six seven eight".split()', ''],
+['wx', 'Choice', '-1, choices="one two three four five six seven eight".split()', ''],
+['wx', 'Choice', '-1, size=(50,-1), choices="one two three four five six seven eight".split()', ''],
+['wx', 'ComboBox', '-1, value="default", choices="one two three four five six seven eight".split()', ''],
+['wx', 'ComboBox', '-1, value="default", size=(75,-1), choices="one two three four five six seven eight".split()', ''],
+['wx', 'ComboBox', '-1, value="read-only", style=wx.CB_READONLY, choices="read-only one two three four five six seven eight".split()', ''],
+['wx', 'Gauge', '-1, 100', 'w.SetValue(40)'],
+['wx', 'Gauge', '-1, 100, style=wx.GA_VERTICAL', 'w.SetValue(60)'],
+['wx', 'ListBox', '-1, choices="one two three four five six seven eight".split()', ''],
+['wx', 'ListCtrl', 'style=wx.LC_REPORT', 'w.InsertColumn(0, "Col1")\nw.InsertStringItem(0, "Item 0")\nw.InsertStringItem(0, "Item 1")\n#w.SetSizeHints((200,100))'],
+['wx', 'ListCtrl', 'style=wx.LC_REPORT, size=(100,100)', 'w.InsertColumn(0, "Col1")\nw.InsertStringItem(0, "Item 0")\nw.InsertStringItem(0, "Item 1")\n#w.SetSizeHints((200,100))'],
+['wx', 'Notebook', '', 'p = wx.Panel(w)\np.SetMinSize((150,150))\nw.AddPage(p, "test")\n'],
+['wx', 'Panel', 'size=(150,150)', ''],
+['wx', 'RadioBox', '-1, "label", choices="one two three four".split()', ''],
+['wx', 'RadioBox', '-1, "label", majorDimension=2, choices="one two three four five six seven eight".split()', ''],
+['wx', 'RadioBox', '-1, "label", style=wx.RA_VERTICAL, choices="one two three four".split()', ''],
+['wx', 'RadioBox', '-1, "label", style=wx.RA_VERTICAL, majorDimension=2, choices="one two three four five six seven eight".split()', ''],
+['wx', 'RadioButton', '-1, "radio button"', ''],
+['wx', 'ScrollBar', '', ''],
+['wx', 'ScrollBar', 'style=wx.SB_VERTICAL', ''],
+['wx', 'Slider', '-1, 20, 0, 100', ''],
+['wx', 'Slider', '-1, 20, 0, 100, size=(400, -1)', ''],
+['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_LABELS', ''],
+['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_LABELS, size=(400,-1)', ''],
+['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_VERTICAL', ''],
+['wx', 'Slider', '-1, 20, 0, 100, style=wx.SL_VERTICAL|wx.SL_LABELS', ''],
+['wx', 'SpinButton', 'style=wx.SP_HORIZONTAL', ''],
+['wx', 'SpinButton', 'style=wx.SP_VERTICAL', ''],
+['wx', 'SpinCtrl', '', ''],
+['wx', 'SpinCtrl', 'size=(50, -1)', ''],
+['wx', 'StaticBitmap', '-1, wx.Bitmap(testImage)', ''],
+['wx', 'StaticBitmap', '-1, wx.Bitmap(testImage), style=wx.SUNKEN_BORDER', ''],
+['wx', 'StaticBox', '-1, "a longer label"', ''],
+['wx', 'StaticBox', '-1, "label"', ''],
+['wx', 'StaticBox', '-1, "with a size", size=(100,75)', ''],
+['wx', 'StaticLine', '-1, size=(-1,100), style=wx.LI_VERTICAL', ''],
+['wx', 'StaticLine', '-1, size=(100,-1), style=wx.LI_HORIZONTAL', ''],
+['wx', 'StaticText', '-1, "New font"', 'f = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)\nw.SetFont(f)\n'],
+['wx', 'StaticText', '-1, "normal"', ''],
+['wx', 'StaticText', '-1, "normal"', 'w.SetLabel("This is a longer label")'],
+['wx', 'TextCtrl', '-1, "default"', ''],
+['wx', 'TextCtrl', '-1, "larger size", size=(200, -1)', ''],
+['wx', 'TextCtrl', '-1, "small", size=(30,-1)', ''],
+['wx', 'TextCtrl', '-1, "some\\ndefault text\\n", size=(200, -1), style=wx.TE_MULTILINE', 'w.AppendText("Here is some more text\\n")'],
+['wx', 'TreeCtrl', '', ''],
+['wx.calendar', 'CalendarCtrl', '-1', ''],
+['wx.calendar', 'CalendarCtrl', '-1', 'w.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL))\n'],
+['wx.calendar', 'CalendarCtrl', '-1, style=wx.calendar.CAL_SEQUENTIAL_MONTH_SELECTION', ''],
+['wx.grid', 'Grid', '', 'w.CreateGrid(5,5)\n'],
+['wx.grid', 'Grid', 'size=(400,200)', 'w.CreateGrid(100,50)\n'],
+['wx.lib.stattext', 'GenStaticText', '-1, "New font"', 'f = wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD)\nw.SetFont(f)\n'],
+]
TR_NO_LINES # don't draw lines at all
TR_LINES_AT_ROOT # connect top-level nodes
TR_TWIST_BUTTONS # draw mac-like twist buttons
- TR_SINGLE # single selection mode
+ TR_SINGLE # single selection mode
TR_MULTIPLE # can select multiple items
TR_EXTENDED # todo: allow extended selection
TR_HAS_VARIABLE_ROW_HEIGHT # allows rows to have variable height
self._vistaselection = False
# Connection lines style
+ grey = (160,160,160)
if wx.Platform != "__WXMAC__":
- self._dottedPen = wx.Pen("grey", 1, wx.USER_DASH)
+ self._dottedPen = wx.Pen(grey, 1, wx.USER_DASH)
self._dottedPen.SetDashes([1,1])
self._dottedPen.SetCap(wx.CAP_BUTT)
else:
- self._dottedPen = wx.Pen("grey", 1)
+ self._dottedPen = wx.Pen(grey, 1)
# Pen Used To Draw The Border Around Selected Items
self._borderPen = wx.BLACK_PEN
if major < 10:
style |= TR_ROW_LINES
- self._windowStyle = style
-
# Create the default check image list
self.SetImageListCheck(13, 13)
dc = wx.ClientDC(self)
self.RefreshLine(item)
- if self._windowStyle & TR_AUTO_CHECK_CHILD:
+ if self.HasFlag(TR_AUTO_CHECK_CHILD):
ischeck = self.IsItemChecked(item)
self.AutoCheckChild(item, ischeck)
- if self._windowStyle & TR_AUTO_CHECK_PARENT:
+ if self.HasFlag(TR_AUTO_CHECK_PARENT):
ischeck = self.IsItemChecked(item)
self.AutoCheckParent(item, ischeck)
- elif self._windowStyle & TR_AUTO_TOGGLE_CHILD:
+ elif self.HasFlag(TR_AUTO_TOGGLE_CHILD):
self.AutoToggleChild(item)
e = TreeEvent(wxEVT_TREE_ITEM_CHECKED, self.GetId())
self._dirty = True
- def HasFlag(self, flag):
- """Returns whether CustomTreeCtrl has a flag."""
-
- return self._windowStyle & flag
-
-
def HasChildren(self, item):
"""Returns whether an item has children or not."""
# want to update the inherited styles, but right now
# none of the parents has updatable styles
- if self._windowStyle & TR_MULTIPLE and not (styles & TR_MULTIPLE):
+ if self.HasFlag(TR_MULTIPLE) and not (styles & TR_MULTIPLE):
selections = self.GetSelections()
for select in selections[0:-1]:
self.SelectItem(select, False)
- self._windowStyle = styles
+ self.SetWindowStyle(styles)
self._dirty = True
def GetTreeStyle(self):
"""Returns the CustomTreeCtrl style."""
- return self._windowStyle
+ return self.GetWindowStyle()
def HasButtons(self):
def DoInsertItem(self, parentId, previous, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
"""Actually inserts an item in the tree."""
- if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
- if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
if ct_type < 0 or ct_type > 2:
if self._anchor:
raise Exception("\nERROR: Tree Can Have Only One Root")
- if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
- if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
if ct_type < 0 or ct_type > 2:
def PrependItem(self, parent, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
"""Appends an item as a first child of parent."""
- if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
- if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
return self.DoInsertItem(parent, 0, text, ct_type, wnd, image, selImage, data)
def InsertItemByItem(self, parentId, idPrevious, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
"""Auxiliary function to cope with the C++ hideous multifunction."""
- if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
- if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
parent = parentId
def InsertItemByIndex(self, parentId, before, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
"""Auxiliary function to cope with the C++ hideous multifunction."""
- if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
- if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
parent = parentId
def InsertItem(self, parentId, input, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
"""Inserts an item after the given previous."""
- if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
- if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
if type(input) == type(1):
def AppendItem(self, parentId, text, ct_type=0, wnd=None, image=-1, selImage=-1, data=None):
"""Appends an item as a last child of its parent."""
- if wnd is not None and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if wnd is not None and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert Controls You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
- if text.find("\n") >= 0 and not (self._windowStyle & TR_HAS_VARIABLE_ROW_HEIGHT):
+ if text.find("\n") >= 0 and not self.HasFlag(TR_HAS_VARIABLE_ROW_HEIGHT):
raise Exception("\nERROR: In Order To Append/Insert A MultiLine Text You Have To Use The Style TR_HAS_VARIABLE_ROW_HEIGHT")
parent = parentId
for child in self._itemWithWindow:
if not self.IsVisible(child):
wnd = child.GetWindow()
- wnd.Hide()
+ if wnd:
+ wnd.Hide()
def Unselect(self):
else: # no custom buttons
- if self._windowStyle & TR_TWIST_BUTTONS:
+ if self.HasFlag(TR_TWIST_BUTTONS):
# We draw something like the Mac twist buttons
dc.SetPen(wx.BLACK_PEN)
+
from __future__ import division
try:
- from Numeric import array,asarray,Float,cos, sin, pi,sum,minimum,maximum,Int32,zeros, ones, concatenate, sqrt, argmin, power, absolute, matrixmultiply, transpose, sometrue, arange, hypot
+ import numpy as N
except ImportError:
- try:
- from numarray import array, asarray, Float, cos, sin, pi, sum, minimum, maximum, Int32, zeros, concatenate, matrixmultiply, transpose, sometrue, arange, hypot
- except ImportError:
- raise ImportError("I could not import either Numeric or numarray")
-
-from time import clock, sleep
-
-import Resources # A file with icons, etc for FloatCanvas
+ raise ImportError("I could not import numpy")
+from time import clock
import wx
-import types
-import os
+from Utilities import BBox
+
## A global variable to hold the Pixels per inch that wxWindows thinks is in use
## This is used for scaling fonts.
## This can't be computed on module __init__, because a wx.App might not have initialized yet.
-global ScreenPPI
+global FontScale
-## a custom Exceptions:
+## Custom Exceptions:
class FloatCanvasError(Exception):
pass
## Create all the mouse events
-# I don't see a need for these two, but maybe some day!
-#EVT_FC_ENTER_WINDOW = wx.NewEventType()
-#EVT_FC_LEAVE_WINDOW = wx.NewEventType()
-EVT_FC_LEFT_DOWN = wx.NewEventType()
+EVT_FC_ENTER_WINDOW = wx.NewEventType()
+EVT_FC_LEAVE_WINDOW = wx.NewEventType()
+EVT_FC_LEFT_DOWN = wx.NewEventType()
EVT_FC_LEFT_UP = wx.NewEventType()
-EVT_FC_LEFT_DCLICK = wx.NewEventType()
-EVT_FC_MIDDLE_DOWN = wx.NewEventType()
-EVT_FC_MIDDLE_UP = wx.NewEventType()
-EVT_FC_MIDDLE_DCLICK = wx.NewEventType()
-EVT_FC_RIGHT_DOWN = wx.NewEventType()
-EVT_FC_RIGHT_UP = wx.NewEventType()
-EVT_FC_RIGHT_DCLICK = wx.NewEventType()
-EVT_FC_MOTION = wx.NewEventType()
-EVT_FC_MOUSEWHEEL = wx.NewEventType()
+EVT_FC_LEFT_DCLICK = wx.NewEventType()
+EVT_FC_MIDDLE_DOWN = wx.NewEventType()
+EVT_FC_MIDDLE_UP = wx.NewEventType()
+EVT_FC_MIDDLE_DCLICK = wx.NewEventType()
+EVT_FC_RIGHT_DOWN = wx.NewEventType()
+EVT_FC_RIGHT_UP = wx.NewEventType()
+EVT_FC_RIGHT_DCLICK = wx.NewEventType()
+EVT_FC_MOTION = wx.NewEventType()
+EVT_FC_MOUSEWHEEL = wx.NewEventType()
## these two are for the hit-test stuff, I never make them real Events
+## fixme: could I use the PyEventBinder for the Object events too?
EVT_FC_ENTER_OBJECT = wx.NewEventType()
EVT_FC_LEAVE_OBJECT = wx.NewEventType()
-##Create all mouse event binding functions
-#def EVT_ENTER_WINDOW( window, function ):
-# window.Connect( -1, -1, EVT_FC_ENTER_WINDOW, function )
-#def EVT_LEAVE_WINDOW( window, function ):
-# window.Connect( -1, -1,EVT_FC_LEAVE_WINDOW , function )
-def EVT_LEFT_DOWN( window, function ):
- window.Connect( -1, -1,EVT_FC_LEFT_DOWN , function )
-def EVT_LEFT_UP( window, function ):
- window.Connect( -1, -1,EVT_FC_LEFT_UP , function )
-def EVT_LEFT_DCLICK ( window, function ):
- window.Connect( -1, -1,EVT_FC_LEFT_DCLICK , function )
-def EVT_MIDDLE_DOWN ( window, function ):
- window.Connect( -1, -1,EVT_FC_MIDDLE_DOWN , function )
-def EVT_MIDDLE_UP ( window, function ):
- window.Connect( -1, -1,EVT_FC_MIDDLE_UP , function )
-def EVT_MIDDLE_DCLICK ( window, function ):
- window.Connect( -1, -1,EVT_FC_MIDDLE_DCLICK , function )
-def EVT_RIGHT_DOWN ( window, function ):
- window.Connect( -1, -1,EVT_FC_RIGHT_DOWN , function )
-def EVT_RIGHT_UP( window, function ):
- window.Connect( -1, -1,EVT_FC_RIGHT_UP , function )
-def EVT_RIGHT_DCLICK( window, function ):
- window.Connect( -1, -1,EVT_FC_RIGHT_DCLICK , function )
-def EVT_MOTION( window, function ):
- window.Connect( -1, -1,EVT_FC_MOTION , function )
-def EVT_MOUSEWHEEL( window, function ):
- window.Connect( -1, -1,EVT_FC_MOUSEWHEEL , function )
+##Create all mouse event binding objects
+EVT_LEFT_DOWN = wx.PyEventBinder(EVT_FC_LEFT_DOWN)
+EVT_LEFT_UP = wx.PyEventBinder(EVT_FC_LEFT_UP)
+EVT_LEFT_DCLICK = wx.PyEventBinder(EVT_FC_LEFT_DCLICK)
+EVT_MIDDLE_DOWN = wx.PyEventBinder(EVT_FC_MIDDLE_DOWN)
+EVT_MIDDLE_UP = wx.PyEventBinder(EVT_FC_MIDDLE_UP)
+EVT_MIDDLE_DCLICK = wx.PyEventBinder(EVT_FC_MIDDLE_DCLICK)
+EVT_RIGHT_DOWN = wx.PyEventBinder(EVT_FC_RIGHT_DOWN)
+EVT_RIGHT_UP = wx.PyEventBinder(EVT_FC_RIGHT_UP)
+EVT_RIGHT_DCLICK = wx.PyEventBinder(EVT_FC_RIGHT_DCLICK)
+EVT_MOTION = wx.PyEventBinder(EVT_FC_MOTION)
+EVT_ENTER_WINDOW = wx.PyEventBinder(EVT_FC_ENTER_WINDOW)
+EVT_LEAVE_WINDOW = wx.PyEventBinder(EVT_FC_LEAVE_WINDOW)
+EVT_MOUSEWHEEL = wx.PyEventBinder(EVT_FC_MOUSEWHEEL)
class _MouseEvent(wx.PyCommandEvent):
- """
+ """!
This event class takes a regular wxWindows mouse event as a parameter,
and wraps it so that there is access to all the original methods. This
self.SetEventType( EventType )
self._NativeEvent = NativeEvent
self.Coords = Coords
-
-# I don't think this is used.
-# def SetCoords(self,Coords):
-# self.Coords = Coords
-
+
def GetCoords(self):
return self.Coords
def __getattr__(self, name):
- #return eval(self.NativeEvent.__getattr__(name) )
return getattr(self._NativeEvent, name)
def _cycleidxs(indexcount, maxvalue, step):
- """
+ """!
Utility function used by _colorGenerator
"""
+
if indexcount == 0:
yield ()
else:
def _colorGenerator():
- """
+ """!
- Generates a seris of unique colors used to do hit-tests with the HIt
+ Generates a series of unique colors used to do hit-tests with the Hit
Test bitmap
-
"""
- import sys
- if sys.platform == 'darwin':
- depth = 24
- else:
- b = wx.EmptyBitmap(1,1)
- depth = b.GetDepth()
- if depth == 16:
- step = 8
- elif depth >= 24:
+
+ depth = wx.GetDisplayDepth()
+## ##there have been problems with 16 bbp displays, to I'm disabling this for now.
+## if depth == 16:
+## print "Warning: There have been problems with hit-testing on 16bbp displays"
+## step = 8
+ if depth >= 24:
step = 1
else:
- raise "ColorGenerator does not work with depth = %s" % depth
+ msg= ["ColorGenerator does not work with depth = %s" % depth]
+ msg.append("It is required for hit testing -- binding events to mouse")
+ msg.append("actions on objects on the Canvas.")
+ msg.append("Please set your display to 24bit")
+ msg.append("Alternatively, the code could be adapted to 16 bit if that's required")
+ raise FloatCanvasError(msg)
return _cycleidxs(indexcount=3, maxvalue=256, step=step)
-
-#### I don't know if the Set objects are useful, beyond the pointset
-#### object. The problem is that when zoomed in, the BB is checked to see
-#### whether to draw the object. A Set object can defeat this. One day
-#### I plan to write some custon C++ code to draw sets of objects
-
-##class ObjectSetMixin:
-## """
-## A mix-in class for draw objects that are sets of objects
-
-## It contains methods for setting lists of pens and brushes
-
-## """
-## def SetPens(self,LineColors,LineStyles,LineWidths):
-## """
-## This method used when an object could have a list of pens, rather than just one
-## It is used for LineSet, and perhaps others in the future.
-
-## fixme: this should be in a mixin
-
-## fixme: this is really kludgy, there has got to be a better way!
-
-## """
-
-## length = 1
-## if type(LineColors) == types.ListType:
-## length = len(LineColors)
-## else:
-## LineColors = [LineColors]
-
-## if type(LineStyles) == types.ListType:
-## length = len(LineStyles)
-## else:
-## LineStyles = [LineStyles]
-
-## if type(LineWidths) == types.ListType:
-## length = len(LineWidths)
-## else:
-## LineWidths = [LineWidths]
-
-## if length > 1:
-## if len(LineColors) == 1:
-## LineColors = LineColors*length
-## if len(LineStyles) == 1:
-## LineStyles = LineStyles*length
-## if len(LineWidths) == 1:
-## LineWidths = LineWidths*length
-
-## self.Pens = []
-## for (LineColor,LineStyle,LineWidth) in zip(LineColors,LineStyles,LineWidths):
-## if LineColor is None or LineStyle is None:
-## self.Pens.append(wx.TRANSPARENT_PEN)
-## # what's this for?> self.LineStyle = 'Transparent'
-## if not self.PenList.has_key((LineColor,LineStyle,LineWidth)):
-## Pen = wx.Pen(LineColor,LineWidth,self.LineStyleList[LineStyle])
-## self.Pens.append(Pen)
-## else:
-## self.Pens.append(self.PenList[(LineColor,LineStyle,LineWidth)])
-## if length == 1:
-## self.Pens = self.Pens[0]
-
class DrawObject:
- """
+ """!
This is the base class for all the objects that can be drawn.
One must subclass from this (and an assortment of Mixins) to create
a new DrawObject.
+ \note This class contain a series of static dictionaries:
+
+ * BrushList
+ * PenList
+ * FillStyleList
+ * LineStyleList
+
+ Is this still necessary?
+
"""
def __init__(self, InForeground = False, IsVisible = True):
+ """! \param InForeground (bool)
+ \param IsVisible (Bool)
+ """
self.InForeground = InForeground
self._Canvas = None
self.Pen = None
self.FillStyle = "Solid"
-
+
self.Visible = IsVisible
# I pre-define all these as class variables to provide an easier
"DotDash" : wx.DOT_DASH,
}
+# def BBFromPoints(self, Points):
+# """!
+# Calculates a Bounding box from a set of points (NX2 array of coordinates)
+# \param Points (array?)
+# """
+#
+# ## fixme: this could be done with array.min() and vstack() in numpy.
+# ## This could use the Utilities.BBox module now.
+# #return N.array( (N.minimum.reduce(Points),
+# # N.maximum.reduce(Points) ),
+# # )
+# return BBox.fromPoints(Points)
+
def Bind(self, Event, CallBackFun):
self.CallBackFuncs[Event] = CallBackFun
self.HitAble = True
self._Canvas.UseHitTest = True
- if not self._Canvas._HTdc:
- self._Canvas.MakeNewHTdc()
+ if self.InForeground and self._Canvas._ForegroundHTBitmap is None:
+ self._Canvas.MakeNewForegroundHTBitmap()
+ elif self._Canvas._HTBitmap is None:
+ self._Canvas.MakeNewHTBitmap()
if not self.HitColor:
if not self._Canvas.HitColorGenerator:
self._Canvas.HitColorGenerator = _colorGenerator()
else:
self.HitPen = self.PenList.setdefault( (HitColor, "solid", self.HitLineWidth), wx.Pen(HitColor, self.HitLineWidth, self.LineStyleList["Solid"]) )
+ ## Just to make sure that they will always be there
+ ## the appropriate ones should be overridden in the subclasses
+ def SetColor(self, Color):
+ pass
+ def SetLineColor(self, LineColor):
+ pass
+ def SetLineStyle(self, LineStyle):
+ pass
+ def SetLineWidth(self, LineWidth):
+ pass
+ def SetFillColor(self, FillColor):
+ pass
+ def SetFillStyle(self, FillStyle):
+ pass
+
def PutInBackground(self):
if self._Canvas and self.InForeground:
self._Canvas._ForeDrawList.remove(self)
self.InForeground = True
def Hide(self):
+ """! \brief Make an object hidden.
+ """
self.Visible = False
def Show(self):
+ """! \brief Make an object visible on the canvas.
+ """
self.Visible = True
+class Group(DrawObject):
+ """
+ A group of other FloatCanvas Objects
+
+ Not all DrawObject methods may apply here. In particular, you can't Bind events to a group.
+
+ Note that if an object is in more than one group, it will get drawn more than once.
+
+ """
+
+ def __init__(self, ObjectList=[], InForeground = False, IsVisible = True):
+ self.ObjectList = list(ObjectList)
+ DrawObject.__init__(self, InForeground, IsVisible)
+ self.CalcBoundingBox()
+
+ def AddObject(self, obj):
+ self.ObjectList.append(obj)
+ self.BoundingBox.Merge(obj.BoundingBox)
+
+ def AddObjects(self, Objects):
+ for o in Objects:
+ self.AddObject(o)
+
+ def CalcBoundingBox(self):
+ if self.ObjectList:
+ BB = BBox.asBBox(self.ObjectList[0].BoundingBox)
+ for obj in self.ObjectList[1:]:
+ BB.Merge(obj.BoundingBox)
+ else:
+ BB = None
+ self.BoundingBox = BB
+
+ def SetColor(self, Color):
+ for o in self.ObjectList:
+ o.SetColor(Color)
+ def SetLineColor(self, Color):
+ for o in self.ObjectList:
+ o.SetLineColor(Color)
+ def SetLineStyle(self, LineStyle):
+ for o in self.ObjectList:
+ o.SetLineStyle(LineStyle)
+ def SetLineWidth(self, LineWidth):
+ for o in self.ObjectList:
+ o.SetLineWidth(LineWidth)
+ def SetFillColor(self, Color):
+ for o in self.ObjectList:
+ o.SetFillColor(Color)
+ def SetFillStyle(self, FillStyle):
+ for o in self.ObjectList:
+ o.SetFillStyle(FillStyle)
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel = None, HTdc=None):
+ for obj in self.ObjectList:
+ obj._Draw(dc, WorldToPixel, ScaleWorldToPixel, HTdc)
+
+
class ColorOnlyMixin:
"""
self.SetPen(Color,"Solid",1)
self.SetBrush(Color,"Solid")
- SetFillColor = SetColor # Just to provide a consistant interface
+ SetFillColor = SetColor # Just to provide a consistant interface
class LineOnlyMixin:
"""
def SetLineColor(self, LineColor):
self.LineColor = LineColor
self.SetPen(LineColor,self.LineStyle,self.LineWidth)
-
+ SetColor = SetLineColor# so that it will do somethign reasonable
+
def SetLineStyle(self, LineStyle):
self.LineStyle = LineStyle
self.SetPen(self.LineColor,LineStyle,self.LineWidth)
def SetFillStyle(self, FillStyle):
self.FillStyle = FillStyle
self.SetBrush(self.FillColor,FillStyle)
-
+
+ def SetUpDraw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
+ dc.SetPen(self.Pen)
+ dc.SetBrush(self.Brush)
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ HTdc.SetBrush(self.HitBrush)
+ return ( WorldToPixel(self.XY),
+ ScaleWorldToPixel(self.WH) )
+
class XYObjectMixin:
"""
(dx,dy) pair. Ideally a Numpy array of shape (2,)
"""
-
- Delta = asarray(Delta, Float)
+
+ Delta = N.asarray(Delta, N.float)
self.XY += Delta
- self.BoundingBox = self.BoundingBox + Delta
-
+ self.BoundingBox += Delta
+
if self._Canvas:
- self._Canvas.BoundingBoxDirty = True
+ self._Canvas.BoundingBoxDirty = True
def CalcBoundingBox(self):
## This may get overwritten in some subclasses
- self.BoundingBox = array( (self.XY, self.XY), Float )
+ self.BoundingBox = N.array( (self.XY, self.XY), N.float )
+ self.BoundingBox = BBox.asBBox((self.XY, self.XY))
def SetPoint(self, xy):
- xy = array( xy, Float)
+ xy = N.array(xy, N.float)
xy.shape = (2,)
- Delta = xy - self.XY
-
+
self.XY = xy
- self.BoundingBox = self.BoundingBox + Delta
+ self.CalcBoundingBox()
- #self.CalcBoundingBox()
if self._Canvas:
- self._Canvas.BoundingBoxDirty = True
+ self._Canvas.BoundingBoxDirty = True
class PointsObjectMixin:
"""
"""
-
-## This is code for the PointsObjectMixin object, it needs to be adapted and tested.
-## Is the neccesary at all: you can always do:
-## Object.SetPoints( Object.Points + delta, copy = False)
-## def Move(self, Delta ):
-## """
-
-## Move(Delta): moves the object by delta, where delta is an (dx,
-## dy) pair. Ideally a Numpy array of shape (2,)
-
-## """
+ def Move(self, Delta):
+ """
+ Move(Delta): moves the object by delta, where delta is an (dx,
+ dy) pair. Ideally a Numpy array of shape (2,)
+ """
-## Delta = array(Delta, Float)
-## self.XY += Delta
-## self.BoundingBox = self.BoundingBox + Delta##array((self.XY, (self.XY + self.WH)), Float)
-## if self._Canvas:
-## self._Canvas.BoundingBoxDirty = True
+ Delta = N.asarray(Delta, N.float)
+ Delta.shape = (2,)
+ self.Points += Delta
+ self.BoundingBox += Delta
+ if self._Canvas:
+ self._Canvas.BoundingBoxDirty = True
def CalcBoundingBox(self):
- self.BoundingBox = array(((min(self.Points[:,0]),
- min(self.Points[:,1]) ),
- (max(self.Points[:,0]),
- max(self.Points[:,1]) ) ), Float )
+ self.BoundingBox = BBox.fromPoints(self.Points)
if self._Canvas:
self._Canvas.BoundingBoxDirty = True
Points = Object.Points
Points += (5,10) # shifts the points 5 in the x dir, and 10 in the y dir.
Object.SetPoints(Points, False) # Sets the points to the same array as it was
-
+
"""
if copy:
- self.Points = array(Points, Float)
+ self.Points = N.array(Points, N.float)
self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
else:
- self.Points = asarray(Points, Float)
+ self.Points = N.asarray(Points, N.float)
self.CalcBoundingBox()
-
-class Polygon(DrawObject,PointsObjectMixin,LineAndFillMixin):
+
+class Polygon(PointsObjectMixin, LineAndFillMixin, DrawObject):
"""
FillColor = None,
FillStyle = "Solid",
InForeground = False):
- DrawObject.__init__(self,InForeground)
- self.Points = array(Points,Float) # this DOES need to make a copy
+ DrawObject.__init__(self, InForeground)
+ self.Points = N.array(Points ,N.float) # this DOES need to make a copy
self.CalcBoundingBox()
self.LineColor = LineColor
HTdc.SetPen(self.HitPen)
HTdc.SetBrush(self.HitBrush)
HTdc.DrawPolygon(Points)
-
-##class PolygonSet(DrawObject):
-## """
-## The PolygonSet class takes a Geometry.Polygon object.
-## so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
-
-## it creates a set of line segments, from (x1,y1) to (x2,y2)
-
-## """
-
-## def __init__(self,PolySet,LineColors,LineStyles,LineWidths,FillColors,FillStyles,InForeground = False):
-## DrawObject.__init__(self, InForeground)
-
-## ##fixme: there should be some error checking for everything being the right length.
-
-
-## self.Points = array(Points,Float)
-## self.BoundingBox = array(((min(self.Points[:,0]),min(self.Points[:,1])),(max(self.Points[:,0]),max(self.Points[:,1]))),Float)
-## self.LineColors = LineColors
-## self.LineStyles = LineStyles
-## self.LineWidths = LineWidths
-## self.FillColors = FillColors
-## self.FillStyles = FillStyles
-
-## self.SetPens(LineColors,LineStyles,LineWidths)
-
-## #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):
-## def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
-## Points = WorldToPixel(self.Points)
-## Points.shape = (-1,4)
-## dc.DrawLineList(Points,self.Pens)
-
-
-class Line(DrawObject,PointsObjectMixin,LineOnlyMixin):
+class Line(PointsObjectMixin, LineOnlyMixin, DrawObject,):
"""
The Line class takes a list of 2-tuples, or a NX2 NumPy Float array
DrawObject.__init__(self, InForeground)
- self.Points = array(Points,Float)
+ self.Points = N.array(Points,N.float)
self.CalcBoundingBox()
self.LineColor = LineColor
self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
-
+
def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
Points = WorldToPixel(self.Points)
dc.SetPen(self.Pen)
HTdc.SetPen(self.HitPen)
HTdc.DrawLines(Points)
-class Arrow(DrawObject,XYObjectMixin,LineOnlyMixin):
+class Spline(Line):
+ def __init__(self, *args, **kwargs):
+ Line.__init__(self, *args, **kwargs)
+
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+ Points = WorldToPixel(self.Points)
+ dc.SetPen(self.Pen)
+ dc.DrawSpline(Points)
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ HTdc.DrawSpline(Points)
+
+
+class Arrow(XYObjectMixin, LineOnlyMixin, DrawObject):
"""
Arrow(XY, # coords of origin of arrow (x,y)
Length, # length of arrow in pixels
theta, # angle of arrow in degrees: zero is straight up
- # angle is to the right
+ # +angle is to the right
LineColor = "Black",
LineStyle = "Solid",
- LineWidth = 1,
- ArrowHeadSize = 4,
- ArrowHeadAngle = 45,
+ LineWidth = 1,
+ ArrowHeadSize = 4, # size of arrowhead in pixels
+ ArrowHeadAngle = 45, # angle of arrow head in degrees
InForeground = False):
It will draw an arrow , starting at the point, (X,Y) pointing in
DrawObject.__init__(self, InForeground)
- self.XY = array(XY, Float)
- self.XY.shape = (2,) # Make sure it is a 1X2 array, even if there is only one point
+ self.XY = N.array(XY, N.float)
+ self.XY.shape = (2,) # Make sure it is a length 2 vector
self.Length = Length
self.Direction = float(Direction)
- self.ArrowHeadSize = ArrowHeadSize
- self.ArrowHeadAngle = float(ArrowHeadAngle)
+ self.ArrowHeadSize = ArrowHeadSize
+ self.ArrowHeadAngle = float(ArrowHeadAngle)
self.CalcArrowPoints()
self.CalcBoundingBox()
def SetDirection(self, Direction):
self.Direction = float(Direction)
self.CalcArrowPoints()
-
+
def SetLength(self, Length):
self.Length = Length
self.CalcArrowPoints()
self.Direction = float(Direction)
self.Length = Length
self.CalcArrowPoints()
-
- def SetLength(self, Length):
- self.Length = Length
- self.CalcArrowPoints()
- ## fixme: cache this?
+## def CalcArrowPoints(self):
+## L = self.Length
+## S = self.ArrowHeadSize
+## phi = self.ArrowHeadAngle * N.pi / 360
+## theta = (self.Direction-90.0) * N.pi / 180
+## ArrowPoints = N.array( ( (0, L, L - S*N.cos(phi),L, L - S*N.cos(phi) ),
+## (0, 0, S*N.sin(phi), 0, -S*N.sin(phi) ) ),
+## N.float )
+## RotationMatrix = N.array( ( ( N.cos(theta), -N.sin(theta) ),
+## ( N.sin(theta), N.cos(theta) ) ),
+## N.float
+## )
+## ArrowPoints = N.matrixmultiply(RotationMatrix, ArrowPoints)
+## self.ArrowPoints = N.transpose(ArrowPoints)
+
def CalcArrowPoints(self):
L = self.Length
S = self.ArrowHeadSize
- phi = self.ArrowHeadAngle * pi / 360
- theta = (self.Direction-90.0) * pi / 180
- ArrowPoints = array( ( (0, L, L - S*cos(phi),L, L - S*cos(phi) ),
- (0, 0, S*sin(phi), 0, -S*sin(phi) ) ),
- Float )
- RotationMatrix = array( ( ( cos(theta), -sin(theta) ),
- ( sin(theta), cos(theta) ) ),
- Float
- )
- ArrowPoints = matrixmultiply(RotationMatrix, ArrowPoints)
- self.ArrowPoints = transpose(ArrowPoints)
+ phi = self.ArrowHeadAngle * N.pi / 360
+ theta = (270 - self.Direction) * N.pi / 180
+ AP = N.array( ( (0,0),
+ (0,0),
+ (N.cos(theta - phi), -N.sin(theta - phi) ),
+ (0,0),
+ (N.cos(theta + phi), -N.sin(theta + phi) ),
+ ), N.float )
+ AP *= S
+ shift = (-L*N.cos(theta), L*N.sin(theta) )
+ AP[1:,:] += shift
+ self.ArrowPoints = AP
def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
dc.SetPen(self.Pen)
HTdc.SetPen(self.HitPen)
HTdc.DrawLines(ArrowPoints)
-##class LineSet(DrawObject, ObjectSetMixin):
-## """
-## The LineSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
-## so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
-
-## it creates a set of line segments, from (x1,y1) to (x2,y2)
-
-## """
-
-## def __init__(self,Points,LineColors,LineStyles,LineWidths,InForeground = False):
-## DrawObject.__init__(self, InForeground)
-## NumLines = len(Points) / 2
-## ##fixme: there should be some error checking for everything being the right length.
+class ArrowLine(PointsObjectMixin, LineOnlyMixin, DrawObject):
+ """
+
+ ArrowLine(Points, # coords of points
+ LineColor = "Black",
+ LineStyle = "Solid",
+ LineWidth = 1,
+ ArrowHeadSize = 4, # in pixels
+ ArrowHeadAngle = 45,
+ InForeground = False):
-
-## 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)
+ It will draw a set of arrows from point to point.
+
+ It takes a list of 2-tuples, or a NX2 NumPy Float array
+ of point coordinates.
-## self.LineColors = LineColors
-## self.LineStyles = LineStyles
-## self.LineWidths = LineWidths
-## self.SetPens(LineColors,LineStyles,LineWidths)
+ """
+
+ def __init__(self,
+ Points,
+ LineColor = "Black",
+ LineStyle = "Solid",
+ LineWidth = 1, # pixels
+ ArrowHeadSize = 8, # pixels
+ ArrowHeadAngle = 30, # degrees
+ InForeground = False):
+
+ DrawObject.__init__(self, InForeground)
+
+ self.Points = N.asarray(Points,N.float)
+ self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
+ self.ArrowHeadSize = ArrowHeadSize
+ self.ArrowHeadAngle = float(ArrowHeadAngle)
+
+ self.CalcArrowPoints()
+ self.CalcBoundingBox()
+
+ self.LineColor = LineColor
+ self.LineStyle = LineStyle
+ self.LineWidth = LineWidth
+
+ self.SetPen(LineColor,LineStyle,LineWidth)
+
+ self.HitLineWidth = max(LineWidth,self.MinHitLineWidth)
+
+ def CalcArrowPoints(self):
+ S = self.ArrowHeadSize
+ phi = self.ArrowHeadAngle * N.pi / 360
+ Points = self.Points
+ n = Points.shape[0]
+ self.ArrowPoints = N.zeros((n-1, 3, 2), N.float)
+ for i in xrange(n-1):
+ dx, dy = self.Points[i] - self.Points[i+1]
+ theta = N.arctan2(dy, dx)
+ AP = N.array( (
+ (N.cos(theta - phi), -N.sin(theta-phi)),
+ (0,0),
+ (N.cos(theta + phi), -N.sin(theta + phi))
+ ),
+ N.float )
+ self.ArrowPoints[i,:,:] = AP
+ self.ArrowPoints *= S
+
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+ Points = WorldToPixel(self.Points)
+ ArrowPoints = Points[1:,N.newaxis,:] + self.ArrowPoints
+ dc.SetPen(self.Pen)
+ dc.DrawLines(Points)
+ for arrow in ArrowPoints:
+ dc.DrawLines(arrow)
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ HTdc.DrawLines(Points)
+ for arrow in ArrowPoints:
+ HTdc.DrawLines(arrow)
-## #def _Draw(self,dc,WorldToPixel,ScaleWorldToPixel):
-## def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
-## Points = WorldToPixel(self.Points)
-## Points.shape = (-1,4)
-## dc.DrawLineList(Points,self.Pens)
-class PointSet(DrawObject,PointsObjectMixin, ColorOnlyMixin):
+class PointSet(PointsObjectMixin, ColorOnlyMixin, DrawObject):
"""
The PointSet class takes a list of 2-tuples, or a NX2 NumPy array of
def __init__(self, Points, Color = "Black", Diameter = 1, InForeground = False):
DrawObject.__init__(self,InForeground)
- self.Points = array(Points,Float)
+ self.Points = N.array(Points,N.float)
self.Points.shape = (-1,2) # Make sure it is a NX2 array, even if there is only one point
self.CalcBoundingBox()
self.Diameter = Diameter
- self.HitLineWidth = self.MinHitLineWidth
+ self.HitLineWidth = min(self.MinHitLineWidth, Diameter)
self.SetColor(Color)
def SetDiameter(self,Diameter):
self.Diameter = Diameter
-
+
def FindClosestPoint(self, XY):
"""
-
+
Returns the index of the closest point to the point, XY, given
in World coordinates. It's essentially random which you get if
there are more than one that are the same.
"""
d = self.Points - XY
- return argmin(hypot(d[:,0],d[:,1]))
-
+ return N.argmin(N.hypot(d[:,0],d[:,1]))
+
def DrawD2(self, dc, Points):
# A Little optimization for a diameter2 - point
##fixme: I really should add a DrawCircleList to wxPython
if len(Points) > 100:
xy = Points
- xywh = concatenate((xy-radius, ones(xy.shape) * self.Diameter ), 1 )
+ xywh = N.concatenate((xy-radius, N.ones(xy.shape) * self.Diameter ), 1 )
dc.DrawEllipseList(xywh)
else:
for xy in Points:
else:
if len(Points) > 100:
xy = Points
- xywh = concatenate((xy-radius, ones(xy.shape) * self.Diameter ), 1 )
+ xywh = N.concatenate((xy-radius, N.ones(xy.shape) * self.Diameter ), 1 )
HTdc.DrawEllipseList(xywh)
else:
for xy in Points:
HTdc.DrawCircle(xy[0],xy[1], radius)
-class Point(DrawObject,XYObjectMixin,ColorOnlyMixin):
+class Point(XYObjectMixin, ColorOnlyMixin, DrawObject):
"""
-
+
The Point class takes a 2-tuple, or a (2,) NumPy array of point
coordinates.
"""
def __init__(self, XY, Color = "Black", Diameter = 1, InForeground = False):
DrawObject.__init__(self, InForeground)
-
- self.XY = array(XY, Float)
- self.XY.shape = (2,) # Make sure it is a 1X2 array, even if there is only one point
+
+ self.XY = N.array(XY, N.float)
+ self.XY.shape = (2,) # Make sure it is a length 2 vector
self.CalcBoundingBox()
self.SetColor(Color)
self.Diameter = Diameter
HTdc.SetBrush(self.HitBrush)
HTdc.DrawCircle(xy[0],xy[1], radius)
-class SquarePoint(DrawObject,XYObjectMixin,ColorOnlyMixin):
+class SquarePoint(XYObjectMixin, ColorOnlyMixin, DrawObject):
"""
-
+
The SquarePoint class takes a 2-tuple, or a (2,) NumPy array of point
coordinates. It produces a square dot, centered on Point
"""
def __init__(self, Point, Color = "Black", Size = 4, InForeground = False):
DrawObject.__init__(self, InForeground)
-
- self.XY = array(Point, Float)
- self.XY.shape = (2,) # Make sure it is a 1X2 array, even if there is only one point
+
+ self.XY = N.array(Point, N.float)
+ self.XY.shape = (2,) # Make sure it is a length 2 vector
self.CalcBoundingBox()
self.SetColor(Color)
self.Size = Size
Size = self.Size
dc.SetPen(self.Pen)
xc,yc = WorldToPixel(self.XY)
-
+
if self.Size <= 1:
dc.DrawPoint(xc, yc)
else:
HTdc.SetBrush(self.HitBrush)
HTdc.DrawRectangle(x, y, Size, Size)
-class RectEllipse(DrawObject, XYObjectMixin, LineAndFillMixin):
+class RectEllipse(XYObjectMixin, LineAndFillMixin, DrawObject):
def __init__(self, XY, WH,
LineColor = "Black",
LineStyle = "Solid",
FillColor = None,
FillStyle = "Solid",
InForeground = False):
-
+
DrawObject.__init__(self,InForeground)
- self.XY = array( XY, Float)
- self.XY.shape = (2,)
- self.WH = array( WH, Float )
- self.WH.shape = (2,)
- self.BoundingBox = array((self.XY, (self.XY + self.WH)), Float)
+ self.SetShape(XY, WH)
self.LineColor = LineColor
self.LineStyle = LineStyle
self.LineWidth = LineWidth
self.SetBrush(FillColor,FillStyle)
def SetShape(self, XY, WH):
- self.XY = array( XY, Float)
- self.WH = array( WH, Float )
+ self.XY = N.array( XY, N.float)
+ self.XY.shape = (2,)
+ self.WH = N.array( WH, N.float)
+ self.WH.shape = (2,)
self.CalcBoundingBox()
- def SetUpDraw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
- dc.SetPen(self.Pen)
- dc.SetBrush(self.Brush)
- if HTdc and self.HitAble:
- HTdc.SetPen(self.HitPen)
- HTdc.SetBrush(self.HitBrush)
- return ( WorldToPixel(self.XY),
- ScaleWorldToPixel(self.WH) )
-
def CalcBoundingBox(self):
- self.BoundingBox = array((self.XY, (self.XY + self.WH) ), Float)
- self._Canvas.BoundingBoxDirty = True
+ # you need this in case Width or Height are negative
+ corners = N.array((self.XY, (self.XY + self.WH) ), N.float)
+ self.BoundingBox = BBox.fromPoints(corners)
+ if self._Canvas:
+ self._Canvas.BoundingBoxDirty = True
class Rectangle(RectEllipse):
if HTdc and self.HitAble:
HTdc.DrawRectanglePointSize(XY, WH)
+
+
class Ellipse(RectEllipse):
def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
HTdc.DrawEllipsePointSize(XY, WH)
class Circle(Ellipse):
-
+ ## fixme: this should probably be use the DC.DrawCircle!
def __init__(self, XY, Diameter, **kwargs):
- self.Center = array(XY, Float)
+ self.Center = N.array(XY, N.float)
Diameter = float(Diameter)
RectEllipse.__init__(self ,
self.Center - Diameter/2.0,
self.SetShape(XY,
(Diameter, Diameter)
)
-
+
class TextObjectMixin(XYObjectMixin):
"""
the Text objects
"""
-
+
## I'm caching fonts, because on GTK, getting a new font can take a
## while. However, it gets cleared after every full draw as hanging
## on to a bunch of large fonts takes a massive amount of memory.
FontList = {}
- LayoutFontSize = 12 # font size used for calculating layout
+ LayoutFontSize = 16 # font size used for calculating layout
- def SetFont(self, Size, Family, Style, Weight, Underline, FaceName):
+ def SetFont(self, Size, Family, Style, Weight, Underlined, FaceName):
self.Font = self.FontList.setdefault( (Size,
Family,
Style,
Weight,
- Underline,
+ Underlined,
FaceName),
- wx.Font(Size,
+ #wx.FontFromPixelSize((0.45*Size,Size), # this seemed to give a decent height/width ratio on Windows
+ wx.Font(Size,
Family,
- Style,
+ Style,
Weight,
- Underline,
+ Underlined,
FaceName) )
- return self.Font
def SetColor(self, Color):
self.Color = Color
## pad is the extra space around the text
## if world = 1, the vertical shift is done in y-up coordinates
ShiftFunDict = {'tl': lambda x, y, w, h, world=0, pad=0: (x + pad, y + pad - 2*world*pad),
- 'tc': lambda x, y, w, h, world=0, pad=0: (x - w/2, y + pad - 2*world*pad),
- 'tr': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y + pad - 2*world*pad),
- 'cl': lambda x, y, w, h, world=0, pad=0: (x + pad, y - h/2 + world*h),
- 'cc': lambda x, y, w, h, world=0, pad=0: (x - w/2, y - h/2 + world*h),
+ 'tc': lambda x, y, w, h, world=0, pad=0: (x - w/2, y + pad - 2*world*pad),
+ 'tr': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y + pad - 2*world*pad),
+ 'cl': lambda x, y, w, h, world=0, pad=0: (x + pad, y - h/2 + world*h),
+ 'cc': lambda x, y, w, h, world=0, pad=0: (x - w/2, y - h/2 + world*h),
'cr': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y - h/2 + world*h),
'bl': lambda x, y, w, h, world=0, pad=0: (x + pad, y - h + 2*world*h - pad + world*2*pad) ,
- 'bc': lambda x, y, w, h, world=0, pad=0: (x - w/2, y - h + 2*world*h - pad + world*2*pad) ,
+ 'bc': lambda x, y, w, h, world=0, pad=0: (x - w/2, y - h + 2*world*h - pad + world*2*pad) ,
'br': lambda x, y, w, h, world=0, pad=0: (x - w - pad, y - h + 2*world*h - pad + world*2*pad)}
-class Text(DrawObject, TextObjectMixin):
+class Text(TextObjectMixin, DrawObject, ):
"""
This class creates a text object, placed at the coordinates,
x,y. the "Position" argument is a two charactor string, indicating
Family:
Font family, a generic way of referring to fonts without
specifying actual facename. One of:
- wx.DEFAULT: Chooses a default font.
- wx.DECORATIVE: A decorative font.
- wx.ROMAN: A formal, serif font.
- wx.SCRIPT: A handwriting font.
- wx.SWISS: A sans-serif font.
+ wx.DEFAULT: Chooses a default font.
+ wx.DECORATIVE: A decorative font.
+ wx.ROMAN: A formal, serif font.
+ wx.SCRIPT: A handwriting font.
+ wx.SWISS: A sans-serif font.
wx.MODERN: A fixed pitch font.
NOTE: these are only as good as the wxWindows defaults, which aren't so good.
Style:
One of wx.NORMAL, wx.SLANT and wx.ITALIC.
Weight:
One of wx.NORMAL, wx.LIGHT and wx.BOLD.
- Underline:
+ Underlined:
The value can be True or False. At present this may have an an
effect on Windows only.
Alternatively, you can set the kw arg: Font, to a wx.Font, and the
above will be ignored.
-
+
The size is fixed, and does not scale with the drawing.
The hit-test is done on the entire text extent
"""
-
+
def __init__(self,String, xy,
- Size = 12,
+ Size = 14,
Color = "Black",
BackgroundColor = None,
Family = wx.MODERN,
Style = wx.NORMAL,
Weight = wx.NORMAL,
- Underline = False,
+ Underlined = False,
Position = 'tl',
InForeground = False,
Font = None):
-
+
DrawObject.__init__(self,InForeground)
self.String = String
- # Input size in in Pixels, compute points size from PPI info.
+ # Input size in in Pixels, compute points size from FontScaleinfo.
# fixme: for printing, we'll have to do something a little different
- self.Size = int(round(72.0 * Size / ScreenPPI))
+ self.Size = Size * FontScale
self.Color = Color
self.BackgroundColor = BackgroundColor
if not Font:
FaceName = ''
else:
- FaceName = Font.GetFaceName()
+ FaceName = Font.GetFaceName()
Family = Font.GetFamily()
- Size = Font.GetPointSize()
+ Size = Font.GetPointSize()
Style = Font.GetStyle()
- Underlined = Font.GetUnderlined()
+ Underlined = Font.GetUnderlined()
Weight = Font.GetWeight()
- self.SetFont(Size, Family, Style, Weight, Underline, FaceName)
+ self.SetFont(Size, Family, Style, Weight, Underlined, FaceName)
- self.BoundingBox = array((xy, xy),Float)
+ self.BoundingBox = BBox.asBBox((xy, xy))
- self.XY = asarray(xy)
+ self.XY = N.asarray(xy)
self.XY.shape = (2,)
(self.TextWidth, self.TextHeight) = (None, None)
HTdc.SetBrush(self.HitBrush)
HTdc.DrawRectanglePointSize(XY, (self.TextWidth, self.TextHeight) )
-class ScaledText(DrawObject, TextObjectMixin):
+class ScaledText(TextObjectMixin, DrawObject, ):
"""
This class creates a text object that is scaled when zoomed. It is
placed at the coordinates, x,y. the "Position" argument is a two
Family:
Font family, a generic way of referring to fonts without
specifying actual facename. One of:
- wx.DEFAULT: Chooses a default font.
- wx.DECORATI: A decorative font.
- wx.ROMAN: A formal, serif font.
- wx.SCRIPT: A handwriting font.
- wx.SWISS: A sans-serif font.
+ wx.DEFAULT: Chooses a default font.
+ wx.DECORATI: A decorative font.
+ wx.ROMAN: A formal, serif font.
+ wx.SCRIPT: A handwriting font.
+ wx.SWISS: A sans-serif font.
wx.MODERN: A fixed pitch font.
NOTE: these are only as good as the wxWindows defaults, which aren't so good.
Style:
One of wx.NORMAL, wx.SLANT and wx.ITALIC.
Weight:
One of wx.NORMAL, wx.LIGHT and wx.BOLD.
- Underline:
+ Underlined:
The value can be True or False. At present this may have an an
effect on Windows only.
Alternatively, you can set the kw arg: Font, to a wx.Font, and the
above will be ignored. The size of the font you specify will be
ignored, but the rest of its attributes will be preserved.
-
+
The size will scale as the drawing is zoomed.
Bugs/Limitations:
As fonts are scaled, the do end up a little different, so you don't
get exactly the same picture as you scale up and doen, but it's
pretty darn close.
-
+
On wxGTK1 on my Linux system, at least, using a font of over about
3000 pts. brings the system to a halt. It's the Font Server using
huge amounts of memory. My work around is to max the font size to
optional, but I haven't gotten around to it.
"""
-
- def __init__(self, String, XY , Size,
+
+ def __init__(self,
+ String,
+ XY,
+ Size,
Color = "Black",
BackgroundColor = None,
Family = wx.MODERN,
Style = wx.NORMAL,
Weight = wx.NORMAL,
- Underline = False,
+ Underlined = False,
Position = 'tl',
Font = None,
InForeground = False):
-
+
DrawObject.__init__(self,InForeground)
self.String = String
- self.XY = array( XY, Float)
+ self.XY = N.array( XY, N.float)
self.XY.shape = (2,)
- self.Size = Size
+ self.Size = Size
self.Color = Color
self.BackgroundColor = BackgroundColor
- self.Family = Family
- self.Style = Style
- self.Weight = Weight
- self.Underline = Underline
+ self.Family = Family
+ self.Style = Style
+ self.Weight = Weight
+ self.Underlined = Underlined
if not Font:
self.FaceName = ''
else:
- self.FaceName = Font.GetFaceName()
- self.Family = Font.GetFamily()
- self.Style = Font.GetStyle()
- self.Underlined = Font.GetUnderlined()
- self.Weight = Font.GetWeight()
+ self.FaceName = Font.GetFaceName()
+ self.Family = Font.GetFamily()
+ self.Style = Font.GetStyle()
+ self.Underlined = Font.GetUnderlined()
+ self.Weight = Font.GetWeight()
# Experimental max font size value on wxGTK2: this works OK on
# my system. If it's a lot larger, there is a crash, with the
# Windows and OS-X seem to be better behaved in this regard.
# They may not draw it, but they don't crash either!
self.MaxFontSize = 1000
-
+
self.ShiftFun = self.ShiftFunDict[Position]
self.CalcBoundingBox()
dc.SelectObject(bitmap) #wxMac needs a Bitmap selected for GetTextExtent to work.
DrawingSize = 40 # pts This effectively determines the resolution that the BB is computed to.
ScaleFactor = float(self.Size) / DrawingSize
- dc.SetFont(self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underline, self.FaceName) )
+ self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underlined, self.FaceName)
+ dc.SetFont(self.Font)
(w,h) = dc.GetTextExtent(self.String)
w = w * ScaleFactor
h = h * ScaleFactor
x, y = self.ShiftFun(self.XY[0], self.XY[1], w, h, world = 1)
- self.BoundingBox = array(((x, y-h ),(x + w, y)),Float)
-
+ self.BoundingBox = BBox.asBBox(((x, y-h ),(x + w, y)))
+
def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
(X,Y) = WorldToPixel( (self.XY) )
## If so, limit it. Would it be better just to not draw it?
## note that this limit is dependent on how much memory you have, etc.
Size = min(Size, self.MaxFontSize)
- dc.SetFont(self.SetFont(Size, self.Family, self.Style, self.Weight, self.Underline, self.FaceName))
+ self.SetFont(Size, self.Family, self.Style, self.Weight, self.Underlined, self.FaceName)
+ dc.SetFont(self.Font)
dc.SetTextForeground(self.Color)
if self.BackgroundColor:
dc.SetBackgroundMode(wx.SOLID)
HTdc.SetBrush(self.HitBrush)
HTdc.DrawRectanglePointSize(xy, (w, h) )
-class ScaledTextBox(DrawObject, TextObjectMixin):
+class ScaledTextBox(TextObjectMixin, DrawObject):
"""
This class creates a TextBox object that is scaled when zoomed. It is
placed at the coordinates, x,y.
If the Width parameter is defined, the text will be wrapped to the width given.
A Box can be drawn around the text, be specifying:
- LineWidth and/or FillColor
+ LineWidth and/or FillColor
A space(margin) can be put all the way around the text, be specifying:
the PadSize argument in world coordinates.
Family:
Font family, a generic way of referring to fonts without
specifying actual facename. One of:
- wx.DEFAULT: Chooses a default font.
- wx.DECORATIVE: A decorative font.
- wx.ROMAN: A formal, serif font.
- wx.SCRIPT: A handwriting font.
- wx.SWISS: A sans-serif font.
+ wx.DEFAULT: Chooses a default font.
+ wx.DECORATIVE: A decorative font.
+ wx.ROMAN: A formal, serif font.
+ wx.SCRIPT: A handwriting font.
+ wx.SWISS: A sans-serif font.
wx.MODERN: A fixed pitch font.
NOTE: these are only as good as the wxWindows defaults, which aren't so good.
Style:
One of wx.NORMAL, wx.SLANT and wx.ITALIC.
Weight:
One of wx.NORMAL, wx.LIGHT and wx.BOLD.
- Underline:
+ Underlined:
The value can be True or False. At present this may have an an
effect on Windows only.
Alternatively, you can set the kw arg: Font, to a wx.Font, and the
above will be ignored. The size of the font you specify will be
ignored, but the rest of its attributes will be preserved.
-
+
The size will scale as the drawing is zoomed.
Bugs/Limitations:
As fonts are scaled, they do end up a little different, so you don't
get exactly the same picture as you scale up and down, but it's
pretty darn close.
-
+
On wxGTK1 on my Linux system, at least, using a font of over about
1000 pts. brings the system to a halt. It's the Font Server using
huge amounts of memory. My work around is to max the font size to
optional, but I haven't gotten around to it.
"""
-
+
def __init__(self, String,
Point,
Size,
Family = wx.MODERN,
Style = wx.NORMAL,
Weight = wx.NORMAL,
- Underline = False,
+ Underlined = False,
Position = 'tl',
Alignment = "left",
Font = None,
LineSpacing = 1.0,
InForeground = False):
-
+
DrawObject.__init__(self,InForeground)
- self.XY = array(Point, Float)
- self.Size = Size
+ self.XY = N.array(Point, N.float)
+ self.Size = Size
self.Color = Color
self.BackgroundColor = BackgroundColor
self.LineColor = LineColor
self.PadSize = Size/10.0
else:
self.PadSize = float(PadSize)
- self.Family = Family
- self.Style = Style
- self.Weight = Weight
- self.Underline = Underline
+ self.Family = Family
+ self.Style = Style
+ self.Weight = Weight
+ self.Underlined = Underlined
self.Alignment = Alignment.lower()
self.LineSpacing = float(LineSpacing)
self.Position = Position
-
+
if not Font:
self.FaceName = ''
else:
- self.FaceName = Font.GetFaceName()
- self.Family = Font.GetFamily()
- self.Style = Font.GetStyle()
- self.Underlined = Font.GetUnderlined()
- self.Weight = Font.GetWeight()
+ self.FaceName = Font.GetFaceName()
+ self.Family = Font.GetFamily()
+ self.Style = Font.GetStyle()
+ self.Underlined = Font.GetUnderlined()
+ self.Weight = Font.GetWeight()
# Experimental max font size value on wxGTK2: this works OK on
# my system. If it's a lot larger, there is a crash, with the
DrawingSize = self.LayoutFontSize # pts This effectively determines the resolution that the BB is computed to.
ScaleFactor = float(self.Size) / DrawingSize
Width = (self.Width - 2*self.PadSize) / ScaleFactor #Width to wrap to
- dc.SetFont(self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underline, self.FaceName) )
-
+ self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underlined, self.FaceName)
+ dc.SetFont(self.Font)
NewStrings = []
for s in self.Strings:
#beginning = True
DrawingSize = self.LayoutFontSize # pts This effectively determines the resolution that the BB is computed to.
ScaleFactor = float(self.Size) / DrawingSize
- dc.SetFont(self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underline, self.FaceName) )
-
+ self.SetFont(DrawingSize, self.Family, self.Style, self.Weight, self.Underlined, self.FaceName)
+ dc.SetFont(self.Font)
TextHeight = dc.GetTextExtent("X")[1]
SpaceWidth = dc.GetTextExtent(" ")[0]
LineHeight = TextHeight * self.LineSpacing
- LineWidths = zeros((len(self.Strings),), Float)
+ LineWidths = N.zeros((len(self.Strings),), N.float)
y = 0
Words = []
AllLinePoints = []
for i, s in enumerate(self.Strings):
LineWidths[i] = 0
LineWords = s.split(" ")
- LinePoints = zeros((len(LineWords),2), Float)
+ LinePoints = N.zeros((len(LineWords),2), N.float)
for j, word in enumerate(LineWords):
if j > 0:
LineWidths[i] += SpaceWidth
LineWidths[i] += w
y -= LineHeight
AllLinePoints.append(LinePoints)
- TextWidth = maximum.reduce(LineWidths)
+ TextWidth = N.maximum.reduce(LineWidths)
self.Words = Words
if self.Width is None:
BoxWidth = TextWidth * ScaleFactor + 2*self.PadSize
else: # use the defined Width
BoxWidth = self.Width
- Points = zeros((0,2), Float)
+ Points = N.zeros((0,2), N.float)
for i, LinePoints in enumerate(AllLinePoints):
## Scale to World Coords.
LinePoints[:,0] += (BoxWidth - LineWidths[i]*ScaleFactor)/2.0
elif self.Alignment == 'right':
LinePoints[:,0] += (BoxWidth - LineWidths[i]*ScaleFactor-self.PadSize)
- Points = concatenate((Points, LinePoints))
+ Points = N.concatenate((Points, LinePoints))
BoxHeight = -(Points[-1,1] - (TextHeight * ScaleFactor)) + 2*self.PadSize
- (x,y) = self.ShiftFun(self.XY[0], self.XY[1], BoxWidth, BoxHeight, world=1)
+ #(x,y) = self.ShiftFun(self.XY[0], self.XY[1], BoxWidth, BoxHeight, world=1)
Points += (0, -self.PadSize)
self.Points = Points
self.BoxWidth = BoxWidth
def CalcBoundingBox(self):
"""
-
+
Calculates the Bounding Box
"""
w, h = self.BoxWidth, self.BoxHeight
x, y = self.ShiftFun(self.XY[0], self.XY[1], w, h, world=1)
- self.BoundingBox = array(((x, y-h ),(x + w, y)),Float)
+ self.BoundingBox = BBox.asBBox(((x, y-h ),(x + w, y)))
def GetBoxRect(self):
wh = (self.BoxWidth, self.BoxHeight)
def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
xy, wh = self.GetBoxRect()
-
- Points = self.Points + xy
- Points = WorldToPixel(Points)
+
+ Points = self.Points + xy
+ Points = WorldToPixel(Points)
xy = WorldToPixel(xy)
wh = ScaleWorldToPixel(wh) * (1,-1)
## If so, limit it. Would it be better just to not draw it?
## note that this limit is dependent on how much memory you have, etc.
Size = min(Size, self.MaxFontSize)
-
- font = self.SetFont(Size, self.Family, self.Style, self.Weight, self.Underline, self.FaceName)
- dc.SetFont(font)
+
+ self.SetFont(Size, self.Family, self.Style, self.Weight, self.Underlined, self.FaceName)
+ dc.SetFont(self.Font)
dc.SetTextForeground(self.Color)
dc.SetBackgroundMode(wx.TRANSPARENT)
dc.SetBrush(self.Brush)
dc.SetPen(self.Pen)
dc.DrawRectanglePointSize(xy , wh)
-
+
# Draw the Text
dc.DrawTextList(self.Words, Points)
HTdc.SetBrush(self.HitBrush)
HTdc.DrawRectanglePointSize(xy, wh)
-class Bitmap(DrawObject, TextObjectMixin):
+class Bitmap(TextObjectMixin, DrawObject, ):
"""
This class creates a bitmap object, placed at the coordinates,
x,y. the "Position" argument is a two charactor string, indicating
The size is fixed, and does not scale with the drawing.
"""
-
+
def __init__(self,Bitmap,XY,
Position = 'tl',
InForeground = False):
-
+
DrawObject.__init__(self,InForeground)
if type(Bitmap) == wx._gdi.Bitmap:
self.Bitmap = Bitmap
elif type(Bitmap) == wx._core.Image:
self.Bitmap = wx.BitmapFromImage(Bitmap)
-
+
# Note the BB is just the point, as the size in World coordinates is not fixed
- self.BoundingBox = array((XY,XY),Float)
+ self.BoundingBox = BBox.asBBox( (XY,XY) )
self.XY = XY
HTdc.SetBrush(self.HitBrush)
HTdc.DrawRectanglePointSize(XY, (self.Width, self.Height) )
-class ScaledBitmap(DrawObject, TextObjectMixin):
+class ScaledBitmap(TextObjectMixin, DrawObject, ):
"""
-
+
This class creates a bitmap object, placed at the coordinates, XY,
of Height, H, in World coorsinates. The width is calculated from the
aspect ratio of the bitmap.
-
+
the "Position" argument is a two charactor string, indicating
where in relation to the coordinates the bitmap should be oriented.
The size scales with the drawing
"""
-
+
def __init__(self,
Bitmap,
XY,
Height,
Position = 'tl',
InForeground = False):
-
+
DrawObject.__init__(self,InForeground)
if type(Bitmap) == wx._gdi.Bitmap:
self.Image = Bitmap.ConvertToImage()
elif type(Bitmap) == wx._core.Image:
self.Image = Bitmap
-
+
self.XY = XY
- self.Height = Height
+ self.Height = Height
(self.bmpWidth, self.bmpHeight) = self.Image.GetWidth(), self.Image.GetHeight()
self.Width = self.bmpWidth / self.bmpHeight * Height
self.ShiftFun = self.ShiftFunDict[Position]
def CalcBoundingBox(self):
## this isn't exact, as fonts don't scale exactly.
- w,h = self.Width, self.Height
+ w, h = self.Width, self.Height
x, y = self.ShiftFun(self.XY[0], self.XY[1], w, h, world = 1)
- self.BoundingBox = array(((x, y-h ),(x + w, y)),Float)
+ self.BoundingBox = BBox.asBBox( ( (x, y-h ), (x + w, y) ) )
+
def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
XY = WorldToPixel(self.XY)
HTdc.SetBrush(self.HitBrush)
HTdc.DrawRectanglePointSize(XY, (W, H) )
+class ScaledBitmap2(TextObjectMixin, DrawObject, ):
+ """
+
+ An alternative scaled bitmap that only scaled the required amount of
+ the main bitmap when zoomed in: EXPERIMENTAL!
+
+ """
+
+ def __init__(self,
+ Bitmap,
+ XY,
+ Height,
+ Width=None,
+ Position = 'tl',
+ InForeground = False):
+
+ DrawObject.__init__(self,InForeground)
+
+ if type(Bitmap) == wx._gdi.Bitmap:
+ self.Image = Bitmap.ConvertToImage()
+ elif type(Bitmap) == wx._core.Image:
+ self.Image = Bitmap
+
+ self.XY = N.array(XY, N.float)
+ self.Height = Height
+ (self.bmpWidth, self.bmpHeight) = self.Image.GetWidth(), self.Image.GetHeight()
+ self.bmpWH = N.array((self.bmpWidth, self.bmpHeight), N.int32)
+ ## fixme: this should all accommodate different scales for X and Y
+ if Width is None:
+ self.BmpScale = float(self.bmpHeight) / Height
+ self.Width = self.bmpWidth / self.BmpScale
+ self.WH = N.array((self.Width, Height), N.float)
+ ##fixme: should this have a y = -1 to shift to y-up?
+ self.BmpScale = self.bmpWH / self.WH
+
+ print "bmpWH:", self.bmpWH
+ print "Width, Height:", self.WH
+ print "self.BmpScale", self.BmpScale
+ self.ShiftFun = self.ShiftFunDict[Position]
+ self.CalcBoundingBox()
+ self.ScaledBitmap = None # cache of the last existing scaled bitmap
+
+ def CalcBoundingBox(self):
+ ## this isn't exact, as fonts don't scale exactly.
+ w,h = self.Width, self.Height
+ x, y = self.ShiftFun(self.XY[0], self.XY[1], w, h, world = 1)
+ self.BoundingBox = BBox.asBBox( ((x, y-h ), (x + w, y)) )
+
+ def WorldToBitmap(self, Pw):
+ """
+ computes bitmap coords from World coords
+ """
+ delta = Pw - self.XY
+ Pb = delta * self.BmpScale
+ Pb *= (1, -1) ##fixme: this may only works for Yup projection!
+ ## and may only work for top left position
+
+ return Pb.astype(N.int_)
+
+ def _DrawEntireBitmap(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
+ """
+ this is pretty much the old code
+
+ Scales and Draws the entire bitmap.
+
+ """
+ XY = WorldToPixel(self.XY)
+ H = ScaleWorldToPixel(self.Height)[0]
+ W = H * (self.bmpWidth / self.bmpHeight)
+ if (self.ScaledBitmap is None) or (self.ScaledBitmap[0] != (0, 0, self.bmpWidth, self.bmpHeight, W, H) ):
+ #if True: #fixme: (self.ScaledBitmap is None) or (H <> self.ScaledHeight) :
+ self.ScaledHeight = H
+ print "Scaling to:", W, H
+ Img = self.Image.Scale(W, H)
+ bmp = wx.BitmapFromImage(Img)
+ self.ScaledBitmap = ((0, 0, self.bmpWidth, self.bmpHeight , W, H), bmp)# this defines the cached bitmap
+ else:
+ print "Using Cached bitmap"
+ bmp = self.ScaledBitmap[1]
+ XY = self.ShiftFun(XY[0], XY[1], W, H)
+ dc.DrawBitmapPoint(bmp, XY, True)
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ HTdc.SetBrush(self.HitBrush)
+ HTdc.DrawRectanglePointSize(XY, (W, H) )
+
+ def _DrawSubBitmap(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc):
+ """
+ Subsets just the part of the bitmap that is visible
+ then scales and draws that.
+
+ """
+ BBworld = BBox.asBBox(self._Canvas.ViewPortBB)
+ BBbitmap = BBox.fromPoints(self.WorldToBitmap(BBworld))
+
+ XYs = WorldToPixel(self.XY)
+ # figure out subimage:
+ # fixme: this should be able to be done more succinctly!
+
+ if BBbitmap[0,0] < 0:
+ Xb = 0
+ elif BBbitmap[0,0] > self.bmpWH[0]: # off the bitmap
+ Xb = 0
+ else:
+ Xb = BBbitmap[0,0]
+ XYs[0] = 0 # draw at origin
+
+ if BBbitmap[0,1] < 0:
+ Yb = 0
+ elif BBbitmap[0,1] > self.bmpWH[1]: # off the bitmap
+ Yb = 0
+ ShouldDraw = False
+ else:
+ Yb = BBbitmap[0,1]
+ XYs[1] = 0 # draw at origin
+
+ if BBbitmap[1,0] < 0:
+ #off the screen -- This should never happen!
+ Wb = 0
+ elif BBbitmap[1,0] > self.bmpWH[0]:
+ Wb = self.bmpWH[0] - Xb
+ else:
+ Wb = BBbitmap[1,0] - Xb
+
+ if BBbitmap[1,1] < 0:
+ # off the screen -- This should never happen!
+ Hb = 0
+ ShouldDraw = False
+ elif BBbitmap[1,1] > self.bmpWH[1]:
+ Hb = self.bmpWH[1] - Yb
+ else:
+ Hb = BBbitmap[1,1] - Yb
+
+ FullHeight = ScaleWorldToPixel(self.Height)[0]
+ scale = FullHeight / self.bmpWH[1]
+ Ws = int(scale * Wb + 0.5) # add the 0.5 to round
+ Hs = int(scale * Hb + 0.5)
+ if (self.ScaledBitmap is None) or (self.ScaledBitmap[0] != (Xb, Yb, Wb, Hb, Ws, Ws) ):
+ Img = self.Image.GetSubImage(wx.Rect(Xb, Yb, Wb, Hb))
+ Img.Rescale(Ws, Hs)
+ bmp = wx.BitmapFromImage(Img)
+ self.ScaledBitmap = ((Xb, Yb, Wb, Hb, Ws, Ws), bmp)# this defines the cached bitmap
+ #XY = self.ShiftFun(XY[0], XY[1], W, H)
+ #fixme: get the shiftfun working!
+ else:
+ print "Using cached bitmap"
+ ##fixme: The cached bitmap could be used if the one needed is the same scale, but
+ ## a subset of the cached one.
+ bmp = self.ScaledBitmap[1]
+ dc.DrawBitmapPoint(bmp, XYs, True)
+
+ if HTdc and self.HitAble:
+ HTdc.SetPen(self.HitPen)
+ HTdc.SetBrush(self.HitBrush)
+ HTdc.DrawRectanglePointSize(XYs, (Ws, Hs) )
+
+ def _Draw(self, dc , WorldToPixel, ScaleWorldToPixel, HTdc=None):
+ BBworld = BBox.asBBox(self._Canvas.ViewPortBB)
+ ## first see if entire bitmap is displayed:
+ if BBworld.Inside(self.BoundingBox):
+ print "Drawing entire bitmap with old code"
+ self._DrawEntireBitmap(dc , WorldToPixel, ScaleWorldToPixel, HTdc)
+ return None
+ elif BBworld.Overlaps(self.BoundingBox):
+ #BBbitmap = BBox.fromPoints(self.WorldToBitmap(BBworld))
+ print "Drawing a sub-bitmap"
+ self._DrawSubBitmap(dc , WorldToPixel, ScaleWorldToPixel, HTdc)
+ else:
+ print "Not Drawing -- no part of image is showing"
+
+class DotGrid:
+ """
+ An example of a Grid Object -- it is set on teh FloatCAnvas with one of:
+
+ FloatCanvas.GridUnder = Grid
+ FloatCanvas.GridOver = Grid
+
+ It will be drawn every time, regardless of the viewport.
+
+ In its _Draw method, it computes what to draw, given the ViewPortBB
+ of the Canvas it's being drawn on.
+
+ """
+ def __init__(self, Spacing, Size = 2, Color = "Black", Cross=False, CrossThickness = 1):
+
+ self.Spacing = N.array(Spacing, N.float)
+ self.Spacing.shape = (2,)
+ self.Size = Size
+ self.Color = Color
+ self.Cross = Cross
+ self.CrossThickness = CrossThickness
+
+ def CalcPoints(self, Canvas):
+ ViewPortBB = Canvas.ViewPortBB
+
+ Spacing = self.Spacing
+
+ minx, miny = N.floor(ViewPortBB[0] / Spacing) * Spacing
+ maxx, maxy = N.ceil(ViewPortBB[1] / Spacing) * Spacing
+
+ ##fixme: this could use vstack or something with numpy
+ x = N.arange(minx, maxx+Spacing[0], Spacing[0]) # making sure to get the last point
+ y = N.arange(miny, maxy+Spacing[1], Spacing[1]) # an extra is OK
+ Points = N.zeros((len(y), len(x), 2), N.float)
+ x.shape = (1,-1)
+ y.shape = (-1,1)
+ Points[:,:,0] += x
+ Points[:,:,1] += y
+ Points.shape = (-1,2)
+
+ return Points
+
+ def _Draw(self, dc, Canvas):
+ Points = self.CalcPoints(Canvas)
+
+ Points = Canvas.WorldToPixel(Points)
+
+ dc.SetPen(wx.Pen(self.Color,self.CrossThickness))
+
+ if self.Cross: # Use cross shaped markers
+ #Horizontal lines
+ LinePoints = N.concatenate((Points + (self.Size,0),Points + (-self.Size,0)),1)
+ dc.DrawLineList(LinePoints)
+ # Vertical Lines
+ LinePoints = N.concatenate((Points + (0,self.Size),Points + (0,-self.Size)),1)
+ dc.DrawLineList(LinePoints)
+ pass
+ else: # use dots
+ ## Note: this code borrowed from Pointset -- itreally shouldn't be repeated here!.
+ if self.Size <= 1:
+ dc.DrawPointList(Points)
+ elif self.Size <= 2:
+ dc.DrawPointList(Points + (0,-1))
+ dc.DrawPointList(Points + (0, 1))
+ dc.DrawPointList(Points + (1, 0))
+ dc.DrawPointList(Points + (-1,0))
+ else:
+ dc.SetBrush(wx.Brush(self.Color))
+ radius = int(round(self.Size/2))
+ ##fixme: I really should add a DrawCircleList to wxPython
+ if len(Points) > 100:
+ xy = Points
+ xywh = N.concatenate((xy-radius, N.ones(xy.shape) * self.Size ), 1 )
+ dc.DrawEllipseList(xywh)
+ else:
+ for xy in Points:
+ dc.DrawCircle(xy[0],xy[1], radius)
+
+
#---------------------------------------------------------------------------
class FloatCanvas(wx.Panel):
- ## fixme: could this be a wx.Window?
"""
FloatCanvas.py
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
+ 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.
Mouse Events:
At this point, there are a full set of custom mouse events. They are
- just like the rebulsr mouse events, but include an extra attribute:
+ just like the regular mouse events, but include an extra attribute:
Event.GetCoords(), that returns the (x,y) position in world
coordinates, as a length-2 NumPy vector of Floats.
-
+
Copyright: Christopher Barker
License: Same as the version of wxPython you are using it with
Chris.Barker@noaa.gov
- """
-
+ """
+
def __init__(self, parent, id = -1,
size = wx.DefaultSize,
ProjectionFun = None,
Debug = False):
wx.Panel.__init__( self, parent, id, wx.DefaultPosition, size)
-
- global ScreenPPI ## A global variable to hold the Pixels per inch that wxWindows thinks is in use.
- dc = wx.ScreenDC()
- ScreenPPI = dc.GetPPI()[1] # Pixel height
- del dc
- self.HitColorGenerator = None
- self.UseHitTest = None
-
- self.NumBetweenBlits = 500
+ self.ComputeFontScale()
+ self.InitAll()
self.BackgroundBrush = wx.Brush(BackgroundColor,wx.SOLID)
wx.EVT_PAINT(self, self.OnPaint)
wx.EVT_SIZE(self, self.OnSize)
-
- wx.EVT_LEFT_DOWN(self, self.LeftDownEvent )
- wx.EVT_LEFT_UP(self, self.LeftUpEvent )
- wx.EVT_LEFT_DCLICK(self, self.LeftDoubleClickEvent )
- wx.EVT_MIDDLE_DOWN(self, self.MiddleDownEvent )
- wx.EVT_MIDDLE_UP(self, self.MiddleUpEvent )
- wx.EVT_MIDDLE_DCLICK(self, self.MiddleDoubleClickEvent )
+
+ wx.EVT_LEFT_DOWN(self, self.LeftDownEvent)
+ wx.EVT_LEFT_UP(self, self.LeftUpEvent)
+ wx.EVT_LEFT_DCLICK(self, self.LeftDoubleClickEvent)
+ wx.EVT_MIDDLE_DOWN(self, self.MiddleDownEvent)
+ wx.EVT_MIDDLE_UP(self, self.MiddleUpEvent)
+ wx.EVT_MIDDLE_DCLICK(self, self.MiddleDoubleClickEvent)
wx.EVT_RIGHT_DOWN(self, self.RightDownEvent)
- wx.EVT_RIGHT_UP(self, self.RightUpEvent )
- wx.EVT_RIGHT_DCLICK(self, self.RightDoubleCLickEvent )
- wx.EVT_MOTION(self, self.MotionEvent )
- wx.EVT_MOUSEWHEEL(self, self.WheelEvent )
+ wx.EVT_RIGHT_UP(self, self.RightUpEvent)
+ wx.EVT_RIGHT_DCLICK(self, self.RightDoubleCLickEvent)
+ wx.EVT_MOTION(self, self.MotionEvent)
+ wx.EVT_MOUSEWHEEL(self, self.WheelEvent)
## CHB: I'm leaving these out for now.
- #wx.EVT_ENTER_WINDOW(self, self. )
- #wx.EVT_LEAVE_WINDOW(self, self. )
+ #wx.EVT_ENTER_WINDOW(self, self. )
+ #wx.EVT_LEAVE_WINDOW(self, self. )
+
+ self.SetProjectionFun(ProjectionFun)
+ self.GUIMode = None
+
+ # timer to give a delay when re-sizing so that buffers aren't re-built too many times.
+ self.SizeTimer = wx.PyTimer(self.OnSizeTimer)
+
+ self.InitializePanel()
+ self.MakeNewBuffers()
+
+# self.CreateCursors()
+
+ def ComputeFontScale(self):
+ ## A global variable to hold the scaling from pixel size to point size.
+ global FontScale
+ dc = wx.ScreenDC()
+ dc.SetFont(wx.Font(16, wx.ROMAN, wx.NORMAL, wx.NORMAL))
+ E = dc.GetTextExtent("X")
+ FontScale = 16/E[1]
+ del dc
+
+ def InitAll(self):
+ """
+ InitAll() sets everything in the Canvas to default state.
+
+ It can be used to reset the Canvas
+
+ """
+
+ self.HitColorGenerator = None
+ self.UseHitTest = False
+
+ self.NumBetweenBlits = 500
## create the Hit Test Dicts:
self.HitDict = None
self._ForegroundBuffer = None
self.BoundingBox = None
self.BoundingBoxDirty = False
- self.ViewPortCenter= array( (0,0), Float)
+ self.MinScale = None
+ self.MaxScale = None
+ self.ViewPortCenter= N.array( (0,0), N.float)
- self.SetProjectionFun(ProjectionFun)
-
- self.MapProjectionVector = array( (1,1), Float) # No Projection to start!
- self.TransformVector = array( (1,-1), Float) # default Transformation
-
- self.Scale = 1
+ self.SetProjectionFun(None)
- self.GUIMode = None
- self.StartRBBox = None
- self.PrevRBBox = None
- self.StartMove = None
- self.PrevMoveXY = None
+ self.MapProjectionVector = N.array( (1,1), N.float) # No Projection to start!
+ self.TransformVector = N.array( (1,-1), N.float) # default Transformation
+
+ self.Scale = 1
self.ObjectUnderMouse = None
-
- # called just to make sure everything is initialized
- # this is a bug on OS-X, maybe it's not required?
- self.SizeTimer = wx.PyTimer(self.OnSizeTimer) # timer to give a delay when re-sizing so that bufferes aren't re-built too many times.
-
- self.InitializePanel()
- self.MakeNewBuffers()
-
- self.InHereNum = 0
- self.CreateCursors()
-
- def CreateCursors(self):
-
- ## create all the Cursors, so they don't need to be created each time.
- ##
- if "wxMac" in wx.PlatformInfo: # use 16X16 cursors for wxMac
- self.HandCursor = wx.CursorFromImage(Resources.getHand16Image())
- self.GrabHandCursor = wx.CursorFromImage(Resources.getGrabHand16Image())
-
- img = Resources.getMagPlus16Image()
- img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
- img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
- self.MagPlusCursor = wx.CursorFromImage(img)
-
- img = Resources.getMagMinus16Image()
- img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
- img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
- self.MagMinusCursor = wx.CursorFromImage(img)
- else: # use 24X24 cursors for GTK and Windows
- self.HandCursor = wx.CursorFromImage(Resources.getHandImage())
- self.GrabHandCursor = wx.CursorFromImage(Resources.getGrabHandImage())
-
- img = Resources.getMagPlusImage()
- img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
- img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
- self.MagPlusCursor = wx.CursorFromImage(img)
-
- img = Resources.getMagMinusImage()
- img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
- img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
- self.MagMinusCursor = wx.CursorFromImage(img)
+ self.GridUnder = None
+ self.GridOver = None
+
+ self._BackgroundDirty = True
def SetProjectionFun(self,ProjectionFun):
if ProjectionFun == 'FlatEarth':
- self.ProjectionFun = self.FlatEarthProjection
+ self.ProjectionFun = self.FlatEarthProjection
elif callable(ProjectionFun):
- self.ProjectionFun = ProjectionFun
+ self.ProjectionFun = ProjectionFun
elif ProjectionFun is None:
- self.ProjectionFun = lambda x=None: array( (1,1), Float)
+ self.ProjectionFun = lambda x=None: N.array( (1,1), N.float)
else:
- raise FloatCanvasError('Projectionfun must be either: "FlatEarth", None, or a callable object (function, for instance) that takes the ViewPortCenter and returns a MapProjectionVector')
+ raise FloatCanvasError('Projectionfun must be either:'
+ ' "FlatEarth", None, or a callable object '
+ '(function, for instance) that takes the '
+ 'ViewPortCenter and returns a MapProjectionVector')
def FlatEarthProjection(self, CenterPoint):
- return array((cos(pi*CenterPoint[1]/180),1),Float)
-
- def SetMode(self,Mode):
- if Mode in ["ZoomIn","ZoomOut","Move","Mouse", None]:
- if Mode == "Move":
- self.SetCursor(self.HandCursor)
- elif Mode == "ZoomIn":
- self.SetCursor(self.MagPlusCursor)
- elif Mode == "ZoomOut":
- self.SetCursor(self.MagMinusCursor)
- else:
- self.SetCursor(wx.NullCursor)
-
+ MaxLatitude = 75 # these were determined essentially arbitrarily
+ MinLatitude = -75
+ Lat = min(CenterPoint[1],MaxLatitude)
+ Lat = max(Lat,MinLatitude)
+ return N.array((N.cos(N.pi*Lat/180),1),N.float)
+
+ def SetMode(self, Mode):
+ '''
+ Set the GUImode to any of the availble mode.
+ '''
+ # Set mode
self.GUIMode = Mode
-
- else:
- raise FloatCanvasError('"%s" is Not a valid Mode'%Mode)
+ #self.GUIMode.SetCursor()
+ self.SetCursor(self.GUIMode.Cursor)
def MakeHitDict(self):
- ##fixme: Should this just be None if nothing has been bound?
+ ##fixme: Should this just be None if nothing has been bound?
self.HitDict = {EVT_FC_LEFT_DOWN: {},
EVT_FC_LEFT_UP: {},
EVT_FC_LEFT_DCLICK: {},
EVT_FC_RIGHT_DCLICK: {},
EVT_FC_ENTER_OBJECT: {},
EVT_FC_LEAVE_OBJECT: {},
- }
-
+ }
+
def _RaiseMouseEvent(self, Event, EventType):
"""
This is called in various other places to raise a Mouse Event
"""
- #print "in Raise Mouse Event", Event
pt = self.PixelToWorld( Event.GetPosition() )
evt = _MouseEvent(EventType, Event, self.GetId(), pt)
- self.GetEventHandler().ProcessEvent(evt)
+ self.GetEventHandler().ProcessEvent(evt)
+
+ if wx.__version__ >= "2.8":
+ HitTestBitmapDepth = 32
+ #print "Using hit test code for 2.8"
+ def GetHitTestColor(self, xy):
+ if self._ForegroundHTBitmap:
+ pdata = wx.AlphaPixelData(self._ForegroundHTBitmap)
+ else:
+ pdata = wx.AlphaPixelData(self._HTBitmap)
+ if not pdata:
+ raise RuntimeError("Trouble Accessing Hit Test bitmap")
+ pacc = pdata.GetPixels()
+ pacc.MoveTo(pdata, xy[0], xy[1])
+ return pacc.Get()[:3]
+ else:
+ HitTestBitmapDepth = 24
+ #print "using pre-2.8 hit test code"
+ def GetHitTestColor(self, xy ):
+ dc = wx.MemoryDC()
+ if self._ForegroundHTBitmap:
+ dc.SelectObject(self._ForegroundHTBitmap)
+ else:
+ dc.SelectObject(self._HTBitmap)
+ hitcolor = dc.GetPixelPoint( xy )
+ return hitcolor.Get()
def HitTest(self, event, HitEvent):
if self.HitDict:
# check if there are any objects in the dict for this event
if self.HitDict[ HitEvent ]:
xy = event.GetPosition()
- if self._ForegroundHTdc:
- hitcolor = self._ForegroundHTdc.GetPixelPoint( xy )
- else:
- hitcolor = self._HTdc.GetPixelPoint( xy )
- color = ( hitcolor.Red(), hitcolor.Green(), hitcolor.Blue() )
+ color = self.GetHitTestColor( xy )
if color in self.HitDict[ HitEvent ]:
Object = self.HitDict[ HitEvent ][color]
## Add the hit coords to the Object
def MouseOverTest(self, event):
##fixme: Can this be cleaned up?
- if self.HitDict:
+ if (self.HitDict and
+
+ (self.HitDict[EVT_FC_ENTER_OBJECT ] or
+ self.HitDict[EVT_FC_LEAVE_OBJECT ] )
+ ):
xy = event.GetPosition()
- if self._ForegroundHTdc:
- hitcolor = self._ForegroundHTdc.GetPixelPoint( xy )
- else:
- hitcolor = self._HTdc.GetPixelPoint( xy )
- color = ( hitcolor.Red(), hitcolor.Green(), hitcolor.Blue() )
+ color = self.GetHitTestColor( xy )
OldObject = self.ObjectUnderMouse
ObjectCallbackCalled = False
if color in self.HitDict[ EVT_FC_ENTER_OBJECT ]:
except KeyError:
pass # this means the leave event isn't bound for that object
return ObjectCallbackCalled
-
+ return False
## fixme: There is a lot of repeated code here
- ## Is there a better way?
- def LeftDoubleClickEvent(self,event):
- if self.GUIMode == "Mouse":
- EventType = EVT_FC_LEFT_DCLICK
- if not self.HitTest(event, EventType):
- self._RaiseMouseEvent(event, EventType)
-
- def MiddleDownEvent(self,event):
- if self.GUIMode == "Mouse":
- EventType = EVT_FC_MIDDLE_DOWN
- if not self.HitTest(event, EventType):
- self._RaiseMouseEvent(event, EventType)
-
- def MiddleUpEvent(self,event):
- if self.GUIMode == "Mouse":
- EventType = EVT_FC_MIDDLE_UP
- if not self.HitTest(event, EventType):
- self._RaiseMouseEvent(event, EventType)
-
- def MiddleDoubleClickEvent(self,event):
- if self.GUIMode == "Mouse":
- EventType = EVT_FC_MIDDLE_DCLICK
- if not self.HitTest(event, EventType):
- self._RaiseMouseEvent(event, EventType)
-
- def RightUpEvent(self,event):
- if self.GUIMode == "Mouse":
- EventType = EVT_FC_RIGHT_UP
- if not self.HitTest(event, EventType):
- self._RaiseMouseEvent(event, EventType)
-
- def RightDoubleCLickEvent(self,event):
- if self.GUIMode == "Mouse":
- EventType = EVT_FC_RIGHT_DCLICK
- if not self.HitTest(event, EventType):
- self._RaiseMouseEvent(event, EventType)
-
- def WheelEvent(self,event):
- ##if self.GUIMode == "Mouse":
- ## Why not always raise this?
- self._RaiseMouseEvent(event, EVT_FC_MOUSEWHEEL)
-
-
- def LeftDownEvent(self,event):
+ ## Is there a better way?
+ def LeftDoubleClickEvent(self, event):
if self.GUIMode:
- if self.GUIMode == "ZoomIn":
- self.StartRBBox = array( event.GetPosition() )
- self.PrevRBBox = None
- self.CaptureMouse()
- elif self.GUIMode == "ZoomOut":
- Center = self.PixelToWorld( event.GetPosition() )
- self.Zoom(1/1.5,Center)
- elif self.GUIMode == "Move":
- self.SetCursor(self.GrabHandCursor)
- self.StartMove = array( event.GetPosition() )
- self.PrevMoveXY = (0,0)
- elif self.GUIMode == "Mouse":
- ## check for a hit
- if not self.HitTest(event, EVT_FC_LEFT_DOWN):
- self._RaiseMouseEvent(event,EVT_FC_LEFT_DOWN)
- else:
- pass
+ self.GUIMode.OnLeftDouble(event)
+ event.Skip()
+
+ def MiddleDownEvent(self, event):
+ if self.GUIMode:
+ self.GUIMode.OnMiddleDown(event)
+ event.Skip()
+
+ def MiddleUpEvent(self, event):
+ if self.GUIMode:
+ self.GUIMode.OnMiddleUp(event)
+ event.Skip()
+
+ def MiddleDoubleClickEvent(self, event):
+ if self.GUIMode:
+ self.GUIMode.OnMiddleDouble(event)
+ event.Skip()
- def LeftUpEvent(self,event):
+ def RightDoubleCLickEvent(self, event):
+ if self.GUIMode:
+ self.GUIMode.OnRightDouble(event)
+ event.Skip()
+
+ def WheelEvent(self, event):
+ if self.GUIMode:
+ self.GUIMode.OnWheel(event)
+ event.Skip()
+
+ def LeftDownEvent(self, event):
+ if self.GUIMode:
+ self.GUIMode.OnLeftDown(event)
+ event.Skip()
+
+ def LeftUpEvent(self, event):
if self.HasCapture():
self.ReleaseMouse()
if self.GUIMode:
- if self.GUIMode == "ZoomIn":
- if event.LeftUp() and not self.StartRBBox is None:
- self.PrevRBBox = None
- EndRBBox = event.GetPosition()
- StartRBBox = self.StartRBBox
- # if mouse has moved less that ten pixels, don't use the box.
- if ( abs(StartRBBox[0] - EndRBBox[0]) > 10
- and abs(StartRBBox[1] - EndRBBox[1]) > 10 ):
- EndRBBox = self.PixelToWorld(EndRBBox)
- StartRBBox = self.PixelToWorld(StartRBBox)
- BB = array(((min(EndRBBox[0],StartRBBox[0]),
- min(EndRBBox[1],StartRBBox[1])),
- (max(EndRBBox[0],StartRBBox[0]),
- max(EndRBBox[1],StartRBBox[1]))),Float)
- self.ZoomToBB(BB)
- else:
- Center = self.PixelToWorld(StartRBBox)
- self.Zoom(1.5,Center)
- self.StartRBBox = None
- elif self.GUIMode == "Move":
- self.SetCursor(self.HandCursor)
- if self.StartMove is not None:
- StartMove = self.StartMove
- EndMove = array((event.GetX(),event.GetY()))
- if sum((StartMove-EndMove)**2) > 16:
- self.MoveImage(StartMove-EndMove,'Pixel')
- self.StartMove = None
- elif self.GUIMode == "Mouse":
- EventType = EVT_FC_LEFT_UP
- if not self.HitTest(event, EventType):
- self._RaiseMouseEvent(event, EventType)
- else:
- pass
+ self.GUIMode.OnLeftUp(event)
+ event.Skip()
- def MotionEvent(self,event):
+ def MotionEvent(self, event):
if self.GUIMode:
- if self.GUIMode == "ZoomIn":
- if event.Dragging() and event.LeftIsDown() and not (self.StartRBBox is None):
- xy0 = self.StartRBBox
- xy1 = array( event.GetPosition() )
- wh = abs(xy1 - xy0)
- wh[0] = max(wh[0], int(wh[1]*self.AspectRatio))
- wh[1] = int(wh[0] / self.AspectRatio)
- xy_c = (xy0 + xy1) / 2
- dc = wx.ClientDC(self)
- dc.BeginDrawing()
- dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
- dc.SetBrush(wx.TRANSPARENT_BRUSH)
- dc.SetLogicalFunction(wx.XOR)
- if self.PrevRBBox:
- dc.DrawRectanglePointSize(*self.PrevRBBox)
- self.PrevRBBox = ( xy_c - wh/2, wh )
- dc.DrawRectanglePointSize( *self.PrevRBBox )
- dc.EndDrawing()
- elif self.GUIMode == "Move":
- if event.Dragging() and event.LeftIsDown() and not self.StartMove is None:
- xy1 = array( event.GetPosition() )
- wh = self.PanelSize
- xy_tl = xy1 - self.StartMove
- dc = wx.ClientDC(self)
- dc.BeginDrawing()
- x1,y1 = self.PrevMoveXY
- x2,y2 = xy_tl
- w,h = self.PanelSize
- ##fixme: This sure could be cleaner!
- if x2 > x1 and y2 > y1:
- xa = xb = x1
- ya = yb = y1
- wa = w
- ha = y2 - y1
- wb = x2- x1
- hb = h
- elif x2 > x1 and y2 <= y1:
- xa = x1
- ya = y1
- wa = x2 - x1
- ha = h
- xb = x1
- yb = y2 + h
- wb = w
- hb = y1 - y2
- elif x2 <= x1 and y2 > y1:
- xa = x1
- ya = y1
- wa = w
- ha = y2 - y1
- xb = x2 + w
- yb = y1
- wb = x1 - x2
- hb = h - y2 + y1
- elif x2 <= x1 and y2 <= y1:
- xa = x2 + w
- ya = y1
- wa = x1 - x2
- ha = h
- xb = x1
- yb = y2 + h
- wb = w
- hb = y1 - y2
-
- dc.SetPen(wx.TRANSPARENT_PEN)
- dc.SetBrush(self.BackgroundBrush)
- dc.DrawRectangle(xa, ya, wa, ha)
- dc.DrawRectangle(xb, yb, wb, hb)
- self.PrevMoveXY = xy_tl
- if self._ForeDrawList:
- ##if self._ForegroundBuffer:
- dc.DrawBitmapPoint(self._ForegroundBuffer,xy_tl)
- else:
- dc.DrawBitmapPoint(self._Buffer,xy_tl)
- dc.EndDrawing()
- elif self.GUIMode == "Mouse":
- ## Only do something if there are mouse over events bound
- if self.HitDict and (self.HitDict[ EVT_FC_ENTER_OBJECT ] or self.HitDict[ EVT_FC_LEAVE_OBJECT ] ):
- if not self.MouseOverTest(event):
- self._RaiseMouseEvent(event,EVT_FC_MOTION)
- else:
- pass
- self._RaiseMouseEvent(event,EVT_FC_MOTION)
- else:
- pass
+ self.GUIMode.OnMove(event)
+ event.Skip()
- def RightDownEvent(self,event):
+ def RightDownEvent(self, event):
if self.GUIMode:
- if self.GUIMode == "ZoomIn":
- Center = self.PixelToWorld((event.GetX(),event.GetY()))
- self.Zoom(1/1.5,Center)
- elif self.GUIMode == "ZoomOut":
- Center = self.PixelToWorld((event.GetX(),event.GetY()))
- self.Zoom(1.5,Center)
- elif self.GUIMode == "Mouse":
- EventType = EVT_FC_RIGHT_DOWN
- if not self.HitTest(event, EventType):
- self._RaiseMouseEvent(event, EventType)
- else:
- pass
-
+ self.GUIMode.OnRightDown(event)
+ event.Skip()
+
+ def RightUpEvent(self, event):
+ if self.GUIMode:
+ self.GUIMode.OnRightUp(event)
+ event.Skip()
+
def MakeNewBuffers(self):
self._BackgroundDirty = True
# Make new offscreen bitmap:
self._Buffer = wx.EmptyBitmap(*self.PanelSize)
- #dc = wx.MemoryDC()
- #dc.SelectObject(self._Buffer)
- #dc.Clear()
if self._ForeDrawList:
self._ForegroundBuffer = wx.EmptyBitmap(*self.PanelSize)
+ if self.UseHitTest:
+ self.MakeNewHTBitmap()
+ else:
+ self._ForegroundHTBitmap = None
else:
self._ForegroundBuffer = None
+ self._ForegroundHTBitmap = None
+
if self.UseHitTest:
- self.MakeNewHTdc()
+ self.MakeNewHTBitmap()
else:
- self._HTdc = None
- self._ForegroundHTdc = None
-
- def MakeNewHTdc(self):
- ## Note: While it's considered a "bad idea" to keep a
- ## MemoryDC around I'm doing it here because a wx.Bitmap
- ## doesn't have a GetPixel method so a DC is needed to do
- ## the hit-test. It didn't seem like a good idea to re-create
- ## a wx.MemoryDC on every single mouse event, so I keep it
- ## around instead
- self._HTdc = wx.MemoryDC()
- self._HTBitmap = wx.EmptyBitmap(*self.PanelSize)
- self._HTdc.SelectObject( self._HTBitmap )
- self._HTdc.SetBackground(wx.BLACK_BRUSH)
- if self._ForeDrawList:
- self._ForegroundHTdc = wx.MemoryDC()
- self._ForegroundHTBitmap = wx.EmptyBitmap(*self.PanelSize)
- self._ForegroundHTdc.SelectObject( self._ForegroundHTBitmap )
- self._ForegroundHTdc.SetBackground(wx.BLACK_BRUSH)
- else:
- self._ForegroundHTdc = None
-
+ self._HTBitmap = None
+ self._ForegroundHTBitmap = None
+
+ def MakeNewHTBitmap(self):
+ """
+ Off screen Bitmap used for Hit tests on background objects
+
+ """
+ self._HTBitmap = wx.EmptyBitmap(self.PanelSize[0],
+
+ self.PanelSize[1],
+
+ depth=self.HitTestBitmapDepth)
+
+ def MakeNewForegroundHTBitmap(self):
+ ## Note: the foreground and backround HT bitmaps are in separate functions
+ ## so that they can be created separate --i.e. when a foreground is
+ ## added after the backgound is drawn
+ """
+ Off screen Bitmap used for Hit tests on foreground objects
+
+ """
+ self._ForegroundHTBitmap = wx.EmptyBitmap(self.PanelSize[0],
+
+ self.PanelSize[1],
+
+ depth=self.HitTestBitmapDepth)
+
def OnSize(self, event=None):
self.InitializePanel()
self.SizeTimer.Start(50, oneShot=True)
if self.PanelSize == (0,0):
## OS-X sometimes gives a Size event when the panel is size (0,0)
self.PanelSize = (2,2)
- self.PanelSize = array(self.PanelSize, Int32)
+ self.PanelSize = N.array(self.PanelSize, N.int32)
self.HalfPanelSize = self.PanelSize / 2 # lrk: added for speed in WorldToPixel
if self.PanelSize[0] == 0 or self.PanelSize[1] == 0:
self.AspectRatio = 1.0
else:
self.AspectRatio = float(self.PanelSize[0]) / self.PanelSize[1]
-
+
def OnPaint(self, event):
dc = wx.PaintDC(self)
if self._ForegroundBuffer:
def Draw(self, Force=False):
"""
+
+ Canvas.Draw(Force=False)
+
+ Re-draws the canvas.
+
+ Note that the buffer will not be re-drawn unless something has
+ changed. If you change a DrawObject directly, then the canvas
+ will not know anything has changed. In this case, you can force
+ a re-draw by passing int True for the Force flag:
+
+ Canvas.Draw(Force=True)
+
There is a main buffer set up to double buffer the screen, so
you can get quick re-draws when the window gets uncovered.
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 sometrue(self.PanelSize <= 2 ): # it's possible for this to get called before being properly initialized.
+
+ if N.sometrue(self.PanelSize <= 2 ):
+ # it's possible for this to get called before being properly initialized.
return
if self.Debug: start = clock()
ScreenDC = wx.ClientDC(self)
- ViewPortWorld = ( self.PixelToWorld((0,0)),
- self.PixelToWorld(self.PanelSize) )
- ViewPortBB = array( ( minimum.reduce(ViewPortWorld),
- maximum.reduce(ViewPortWorld) ) )
+ ViewPortWorld = N.array(( self.PixelToWorld((0,0)),
+ self.PixelToWorld(self.PanelSize) )
+ )
+ self.ViewPortBB = N.array( ( N.minimum.reduce(ViewPortWorld),
+ N.maximum.reduce(ViewPortWorld) ) )
+ #self.ViewPortWorld = ViewPortWorld
+
dc = wx.MemoryDC()
dc.SelectObject(self._Buffer)
if self._BackgroundDirty or Force:
- #print "Background is Dirty"
dc.SetBackground(self.BackgroundBrush)
dc.Clear()
- if self._HTdc:
- self._HTdc.Clear()
- self._DrawObjects(dc, self._DrawList, ScreenDC, ViewPortBB, self._HTdc)
+ if self._HTBitmap is not None:
+ HTdc = wx.MemoryDC()
+ HTdc.SelectObject(self._HTBitmap)
+ HTdc.Clear()
+ else:
+ HTdc = None
+ if self.GridUnder is not None:
+ self.GridUnder._Draw(dc, self)
+ self._DrawObjects(dc, self._DrawList, ScreenDC, self.ViewPortBB, HTdc)
self._BackgroundDirty = False
+ del HTdc
if self._ForeDrawList:
## If an object was just added to the Foreground, there might not yet be a buffer
dc = wx.MemoryDC() ## I got some strange errors (linewidths wrong) if I didn't make a new DC here
dc.SelectObject(self._ForegroundBuffer)
dc.DrawBitmap(self._Buffer,0,0)
- if self._ForegroundHTdc is None:
- self._ForegroundHTdc = wx.MemoryDC()
- self._ForegroundHTdc.SelectObject( wx.EmptyBitmap(
- self.PanelSize[0],
- self.PanelSize[1]) )
- if self._HTdc:
- ## blit the background HT buffer to the foreground HT buffer
- self._ForegroundHTdc.Blit(0, 0,
- self.PanelSize[0], self.PanelSize[1],
- self._HTdc, 0, 0)
+ if self._ForegroundHTBitmap is not None:
+ ForegroundHTdc = wx.MemoryDC()
+ ForegroundHTdc.SelectObject( self._ForegroundHTBitmap)
+ ForegroundHTdc.Clear()
+ if self._HTBitmap is not None:
+ #Draw the background HT buffer to the foreground HT buffer
+ ForegroundHTdc.DrawBitmap(self._HTBitmap, 0, 0)
+ else:
+ ForegroundHTdc = None
self._DrawObjects(dc,
self._ForeDrawList,
ScreenDC,
- ViewPortBB,
- self._ForegroundHTdc)
+ self.ViewPortBB,
+ ForegroundHTdc)
+ if self.GridOver is not None:
+ self.GridOver._Draw(dc, self)
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
- # This seeems out of place, but it works.
- if self.PrevRBBox:
- ScreenDC.SetPen(wx.Pen('WHITE', 2,wx.SHORT_DASH))
- ScreenDC.SetBrush(wx.TRANSPARENT_BRUSH)
- ScreenDC.SetLogicalFunction(wx.XOR)
- ScreenDC.DrawRectanglePointSize(*self.PrevRBBox)
+ # If the canvas is in the middle of a zoom or move,
+ # the Rubber Band box needs to be re-drawn
+ ##fixme: maybe GUIModes should never be None, and rather have a Do-nothing GUI-Mode.
+ if self.GUIMode is not None:
+ self.GUIMode.UpdateScreen()
+
if self.Debug: print "Drawing took %f seconds of CPU time"%(clock()-start)
- ## Clear the font cache
- ## IF you don't do this, the X font server starts to take up Massive amounts of memory
- ## This is mostly a problem with very large fonts, that you get with scaled text when zoomed in.
+ ## Clear the font cache. If you don't do this, the X font server
+ ## starts to take up Massive amounts of memory This is mostly a
+ ## problem with very large fonts, that you get with scaled text
+ ## when zoomed in.
DrawObject.FontList = {}
def _ShouldRedraw(DrawList, ViewPortBB): # lrk: adapted code from BBCheck
# lrk: Returns the objects that should be redrawn
+ ## fixme: should this check be moved into the object?
+ ## also: a BB object would make this cleaner too
BB2 = ViewPortBB
redrawlist = []
for Object in DrawList:
BB1 = Object.BoundingBox
+ ## note: this could use the Utilities.BBCheck function
+ ## butthis saves a function call
if (BB1[1,0] > BB2[0,0] and BB1[0,0] < BB2[1,0] and
BB1[1,1] > BB2[0,1] and BB1[0,1] < BB2[1,1]):
redrawlist.append(Object)
- return redrawlist
+ #return redrawlist
+ ##fixme: disabled this!!!!
+ return redrawlist
_ShouldRedraw = staticmethod(_ShouldRedraw)
-
-## def BBCheck(self, BB1, BB2):
-## """
-
-## BBCheck(BB1, BB2) returns True is the Bounding boxes intesect, False otherwise
-
-## """
-## if ( (BB1[1,0] > BB2[0,0]) and (BB1[0,0] < BB2[1,0]) and
-## (BB1[1,1] > BB2[0,1]) and (BB1[0,1] < BB2[1,1]) ):
-## return True
-## else:
-## return False
-
def MoveImage(self,shift,CoordType):
"""
move the image in the window.
in Floating point world coordinates
"""
-
- shift = asarray(shift,Float)
- #print "shifting by:", shift
+ shift = N.asarray(shift,N.float)
if CoordType == 'Panel':# convert from panel coordinates
- shift = shift * array((-1,1),Float) *self.PanelSize/self.TransformVector
+ shift = shift * N.array((-1,1),N.float) *self.PanelSize/self.TransformVector
elif CoordType == 'Pixel': # convert from pixel coordinates
shift = shift/self.TransformVector
elif CoordType == 'World': # No conversion
pass
else:
raise FloatCanvasError('CoordType must be either "Panel", "Pixel", or "World"')
-
- self.ViewPortCenter = self.ViewPortCenter + shift
+
+ self.ViewPortCenter = self.ViewPortCenter + shift
self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
- self.TransformVector = array((self.Scale,-self.Scale),Float) * self.MapProjectionVector
+ self.TransformVector = N.array((self.Scale,-self.Scale),N.float) * self.MapProjectionVector
self._BackgroundDirty = True
self.Draw()
- def Zoom(self,factor,center = None):
-
+ def Zoom(self, factor, center = None, centerCoords="world"):
+
"""
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.
+
+ 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.
+
+ centerCoords is a flag indicating whether the center given is in pixel or world
+ coords. Options are: "world" or "pixel"
"""
self.Scale = self.Scale*factor
if not center is None:
- self.ViewPortCenter = array(center,Float)
- self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
- self.TransformVector = array((self.Scale,-self.Scale),Float) * self.MapProjectionVector
- self._BackgroundDirty = True
- self.Draw()
-
- def ZoomToBB(self, NewBB = None, DrawFlag = True):
+ if centerCoords == "pixel":
+ center = self.PixelToWorld( center )
+ else:
+ center = N.array(center,N.float)
+ self.ViewPortCenter = center
+ self.SetToNewScale()
+
+ def ZoomToBB(self, NewBB=None, DrawFlag=True):
"""
"""
- if not NewBB is None:
+ if NewBB is not None:
BoundingBox = NewBB
else:
if self.BoundingBoxDirty:
self._ResetBoundingBox()
BoundingBox = self.BoundingBox
- if not BoundingBox is None:
- self.ViewPortCenter = array(((BoundingBox[0,0]+BoundingBox[1,0])/2,
- (BoundingBox[0,1]+BoundingBox[1,1])/2 ),Float)
+ if BoundingBox is not None:
+ self.ViewPortCenter = N.array(((BoundingBox[0,0]+BoundingBox[1,0])/2,
+ (BoundingBox[0,1]+BoundingBox[1,1])/2 ),N.float_)
self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
# Compute the new Scale
- BoundingBox = BoundingBox * self.MapProjectionVector
+ BoundingBox = BoundingBox*self.MapProjectionVector # this does need to make a copy!
try:
self.Scale = min(abs(self.PanelSize[0] / (BoundingBox[1,0]-BoundingBox[0,0])),
abs(self.PanelSize[1] / (BoundingBox[1,1]-BoundingBox[0,1])) )*0.95
self.Scale = (self.PanelSize[1] / (BoundingBox[1,1]-BoundingBox[0,1]))*0.95
except ZeroDivisionError: #zero size! (must be a single point)
self.Scale = 1
-
- self.TransformVector = array((self.Scale,-self.Scale),Float)* self.MapProjectionVector
+
if DrawFlag:
self._BackgroundDirty = True
- self.Draw()
else:
# Reset the shifting and scaling to defaults when there is no BB
- self.ViewPortCenter= array( (0,0), Float)
- self.MapProjectionVector = array( (1,1), Float) # No Projection to start!
- self.TransformVector = array( (1,-1), Float) # default Transformation
- self.Scale = 1
-
+ self.ViewPortCenter= N.array( (0,0), N.float)
+ self.Scale= 1
+ self.SetToNewScale(DrawFlag=DrawFlag)
+
+ def SetToNewScale(self, DrawFlag=True):
+ Scale = self.Scale
+ if self.MinScale is not None:
+ Scale = max(Scale, self.MinScale)
+ if self.MaxScale is not None:
+ Scale = min(Scale, self.MaxScale)
+ self.MapProjectionVector = self.ProjectionFun(self.ViewPortCenter)
+ self.TransformVector = N.array((Scale,-Scale),N.float) * self.MapProjectionVector
+ self.Scale = Scale
+ self._BackgroundDirty = True
+ if DrawFlag:
+ self.Draw()
+
def RemoveObjects(self, Objects):
for Object in Objects:
- self.RemoveObject(Object, ResetBB = False)
+ self.RemoveObject(Object, ResetBB=False)
self.BoundingBoxDirty = True
-
+
def RemoveObject(self, Object, ResetBB = True):
##fixme: Using the list.remove method is kind of slow
if Object.InForeground:
if ResetBB:
self.BoundingBoxDirty = True
- def ClearAll(self, ResetBB = True):
+ def ClearAll(self, ResetBB=True):
+ """
+ ClearAll(ResetBB=True)
+
+ Removes all DrawObjects from the Canvas
+
+ If ResetBB is set to False, the original bounding box will remain
+
+ """
self._DrawList = []
self._ForeDrawList = []
self._BackgroundDirty = True
self.MakeNewBuffers()
self.HitDict = None
-## No longer called
-## def _AddBoundingBox(self,NewBB):
-## if self.BoundingBox is None:
-## self.BoundingBox = NewBB
-## self.ZoomToBB(NewBB,DrawFlag = False)
-## else:
-## self.BoundingBox = array( ( (min(self.BoundingBox[0,0],NewBB[0,0]),
-## min(self.BoundingBox[0,1],NewBB[0,1])),
-## (max(self.BoundingBox[1,0],NewBB[1,0]),
-## max(self.BoundingBox[1,1],NewBB[1,1]))),
-## Float)
-
- def _getboundingbox(bboxarray): # lrk: added this
-
- upperleft = minimum.reduce(bboxarray[:,0])
- lowerright = maximum.reduce(bboxarray[:,1])
- return array((upperleft, lowerright), Float)
-
- _getboundingbox = staticmethod(_getboundingbox)
-
def _ResetBoundingBox(self):
if self._DrawList or self._ForeDrawList:
- bboxarray = zeros((len(self._DrawList)+len(self._ForeDrawList), 2, 2),Float)
- i = -1 # just in case _DrawList is empty
- for (i, BB) in enumerate(self._DrawList):
- bboxarray[i] = BB.BoundingBox
- for (j, BB) in enumerate(self._ForeDrawList):
- bboxarray[i+j+1] = BB.BoundingBox
- self.BoundingBox = self._getboundingbox(bboxarray)
+ bblist = []
+ for (i, obj) in enumerate(self._DrawList):
+ bblist.append(obj.BoundingBox)
+ for (j, obj) in enumerate(self._ForeDrawList):
+ bblist.append(obj.BoundingBox)
+ self.BoundingBox = BBox.fromBBArray(bblist)
else:
self.BoundingBox = None
- self.ViewPortCenter= array( (0,0), Float)
- self.TransformVector = array( (1,-1), Float)
- self.MapProjectionVector = array( (1,1), Float)
- self.Scale = 1
+ self.ViewPortCenter= N.array( (0,0), N.float)
+ self.TransformVector = N.array( (1,-1), N.float)
+ self.MapProjectionVector = N.array( (1,1), N.float)
+ self.Scale = 1
self.BoundingBoxDirty = False
- def PixelToWorld(self,Points):
+ 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.
-
+
+ Points is a tuple of (x,y) coordinates, or a list of such tuples,
+ or a NX2 Numpy array of x,y coordinates.
+
"""
- return (((asarray(Points,Float) - (self.PanelSize/2))/self.TransformVector) + self.ViewPortCenter)
-
+ return (((N.asarray(Points, N.float) -
+ (self.PanelSize/2))/self.TransformVector) +
+ self.ViewPortCenter)
+
def WorldToPixel(self,Coordinates):
"""
This function will get passed to the drawing functions of the objects,
Coordinates should be a NX2 array of (x,y) coordinates, or
a 2-tuple, or sequence of 2-tuples.
"""
- #Note: this can be called by users code for various reasons, so asarray is needed.
- return (((asarray(Coordinates,Float) -
+ #Note: this can be called by users code for various reasons, so N.asarray is needed.
+ return (((N.asarray(Coordinates,N.float) -
self.ViewPortCenter)*self.TransformVector)+
(self.HalfPanelSize)).astype('i')
"""
This function will get passed to the drawing functions of the objects,
to Change a length from world to pixel coordinates.
-
+
Lengths should be a NX2 array of (x,y) coordinates, or
a 2-tuple, or sequence of 2-tuples.
"""
- return ( (asarray(Lengths,Float)*self.TransformVector) ).astype('i')
+ return ( (N.asarray(Lengths, N.float)*self.TransformVector) ).astype('i')
def ScalePixelToWorld(self,Lengths):
"""
This function computes a pair of x.y lengths,
to change then from pixel to world coordinates.
-
+
Lengths should be a NX2 array of (x,y) coordinates, or
a 2-tuple, or sequence of 2-tuples.
"""
- return (asarray(Lengths,Float) / self.TransformVector)
-
- def AddObject(self,obj):
+ return (N.asarray(Lengths,N.float) / self.TransformVector)
+
+ def AddObject(self, obj):
# put in a reference to the Canvas, so remove and other stuff can work
obj._Canvas = self
if obj.InForeground:
self.BoundingBoxDirty = True
return True
+ def AddObjects(self, Objects):
+ for Object in Objects:
+ self.AddObject(Object)
+
def _DrawObjects(self, dc, DrawList, ScreenDC, ViewPortBB, HTdc = None):
"""
This is a convenience function;
This function takes the list of objects and draws them to specified
- device context.
+ device context.
"""
dc.SetBackground(self.BackgroundBrush)
dc.BeginDrawing()
def SaveAsImage(self, filename, ImageType=wx.BITMAP_TYPE_PNG):
"""
-
+
Saves the current image as an image file. The default is in the
- PNG format. Other formats can be spcified using the wx flags:
+ PNG format. Other formats can be specified using the wx flags:
+ wx.BITMAP_TYPE_PNG
+ wx.BITMAP_TYPE_JPG
wx.BITMAP_TYPE_BMP
wx.BITMAP_TYPE_XBM
wx.BITMAP_TYPE_XPM
etc. (see the wx docs for the complete list)
"""
-
+
self._Buffer.SaveFile(filename, ImageType)
def _makeFloatCanvasAddMethods(): ## lrk's code for doing this in module __init__
classnames = ["Circle", "Ellipse", "Rectangle", "ScaledText", "Polygon",
- "Line", "Text", "PointSet","Point", "Arrow","ScaledTextBox",
- "SquarePoint","Bitmap", "ScaledBitmap"]
+ "Line", "Text", "PointSet","Point", "Arrow", "ArrowLine", "ScaledTextBox",
+ "SquarePoint","Bitmap", "ScaledBitmap", "Spline", "Group"]
for classname in classnames:
klass = globals()[classname]
def getaddshapemethod(klass=klass):
docstring += ", whose docstring is:\n%s" % klass.__doc__
FloatCanvas.__dict__[methodname].__doc__ = docstring
-_makeFloatCanvasAddMethods()
+_makeFloatCanvasAddMethods()
--- /dev/null
+"""
+
+Module that holds the GUI modes used by FloatCanvas
+
+
+Note that this can only be imported after a wx.App() has been created.
+
+This approach was inpired by Christian Blouin, who also wrote the initial
+version of the code.
+
+"""
+
+import wx
+## fixme: events should live in their own module, so all of FloatCanvas
+## wouldn't have to be imported here.
+import FloatCanvas, Resources
+import numpy as N
+
+## create all the Cursors, so they don't need to be created each time.
+if "wxMac" in wx.PlatformInfo: # use 16X16 cursors for wxMac
+ HandCursor = wx.CursorFromImage(Resources.getHand16Image())
+ GrabHandCursor = wx.CursorFromImage(Resources.getGrabHand16Image())
+
+ img = Resources.getMagPlus16Image()
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
+ MagPlusCursor = wx.CursorFromImage(img)
+
+ img = Resources.getMagMinus16Image()
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 6)
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 6)
+ MagMinusCursor = wx.CursorFromImage(img)
+else: # use 24X24 cursors for GTK and Windows
+ HandCursor = wx.CursorFromImage(Resources.getHandImage())
+ GrabHandCursor = wx.CursorFromImage(Resources.getGrabHandImage())
+
+ img = Resources.getMagPlusImage()
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
+ MagPlusCursor = wx.CursorFromImage(img)
+
+ img = Resources.getMagMinusImage()
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_X, 9)
+ img.SetOptionInt(wx.IMAGE_OPTION_CUR_HOTSPOT_Y, 9)
+ MagMinusCursor = wx.CursorFromImage(img)
+
+
+class GUIBase:
+ """
+ Basic Mouse mode and baseclass for other GUImode.
+
+ This one does nothing with any event
+
+ """
+ def __init__(self, parent):
+ self.parent = parent
+
+ Cursor = wx.NullCursor
+
+ # Handlers
+ def OnLeftDown(self, event):
+ pass
+ def OnLeftUp(self, event):
+ pass
+ def OnLeftDouble(self, event):
+ pass
+ def OnRightDown(self, event):
+ pass
+ def OnRightUp(self, event):
+ pass
+ def OnRightDouble(self, event):
+ pass
+ def OnMiddleDown(self, event):
+ pass
+ def OnMiddleUp(self, event):
+ pass
+ def OnMiddleDouble(self, event):
+ pass
+ def OnWheel(self, event):
+ pass
+ def OnMove(self, event):
+ pass
+
+ def UpdateScreen(self):
+ """
+ Update gets called if the screen has been repainted in the middle of a zoom in
+ so the Rubber Band Box can get updated
+ """
+ pass
+
+class GUIMouse(GUIBase):
+ """
+
+ Mouse mode checks for a hit test, and if nothing is hit,
+ raises a FloatCanvas mouse event for each event.
+
+ """
+
+ Cursor = wx.NullCursor
+
+ # Handlers
+ def OnLeftDown(self, event):
+ EventType = FloatCanvas.EVT_FC_LEFT_DOWN
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnLeftUp(self, event):
+ EventType = FloatCanvas.EVT_FC_LEFT_UP
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnLeftDouble(self, event):
+ EventType = FloatCanvas.EVT_FC_LEFT_DCLICK
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnMiddleDown(self, event):
+ EventType = FloatCanvas.EVT_FC_MIDDLE_DOWN
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnMiddleUp(self, event):
+ EventType = FloatCanvas.EVT_FC_MIDDLE_UP
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnMiddleDouble(self, event):
+ EventType = FloatCanvas.EVT_FC_MIDDLE_DCLICK
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnRightDown(self, event):
+ EventType = FloatCanvas.EVT_FC_RIGHT_DOWN
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnRightUp(self, event):
+ EventType = FloatCanvas.EVT_FC_RIGHT_UP
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnRightDouble(self, event):
+ EventType = FloatCanvas.EVT_FC_RIGHT_DCLICK
+ if not self.parent.HitTest(event, EventType):
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnWheel(self, event):
+ EventType = FloatCanvas.EVT_FC_MOUSEWHEEL
+ self.parent._RaiseMouseEvent(event, EventType)
+
+ def OnMove(self, event):
+ ## The Move event always gets raised, even if there is a hit-test
+ self.parent.MouseOverTest(event)
+ self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+
+
+class GUIMove(GUIBase):
+
+ Cursor = HandCursor
+ GrabCursor = GrabHandCursor
+ def __init__(self, parent):
+ GUIBase.__init__(self, parent)
+ self.StartMove = None
+ self.PrevMoveXY = None
+
+ def OnLeftDown(self, event):
+ self.parent.SetCursor(self.GrabCursor)
+ self.parent.CaptureMouse()
+ self.StartMove = N.array( event.GetPosition() )
+ self.PrevMoveXY = (0,0)
+
+ def OnLeftUp(self, event):
+ if self.StartMove is not None:
+ StartMove = self.StartMove
+ EndMove = N.array(event.GetPosition())
+ DiffMove = StartMove-EndMove
+ if N.sum(DiffMove**2) > 16:
+ self.parent.MoveImage(DiffMove, 'Pixel')
+ self.StartMove = None
+ self.parent.SetCursor(self.Cursor)
+
+ def OnMove(self, event):
+ # Allways raise the Move event.
+ self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+ if event.Dragging() and event.LeftIsDown() and not self.StartMove is None:
+ xy1 = N.array( event.GetPosition() )
+ wh = self.parent.PanelSize
+ xy_tl = xy1 - self.StartMove
+ dc = wx.ClientDC(self.parent)
+ dc.BeginDrawing()
+ x1,y1 = self.PrevMoveXY
+ x2,y2 = xy_tl
+ w,h = self.parent.PanelSize
+ ##fixme: This sure could be cleaner!
+ if x2 > x1 and y2 > y1:
+ xa = xb = x1
+ ya = yb = y1
+ wa = w
+ ha = y2 - y1
+ wb = x2- x1
+ hb = h
+ elif x2 > x1 and y2 <= y1:
+ xa = x1
+ ya = y1
+ wa = x2 - x1
+ ha = h
+ xb = x1
+ yb = y2 + h
+ wb = w
+ hb = y1 - y2
+ elif x2 <= x1 and y2 > y1:
+ xa = x1
+ ya = y1
+ wa = w
+ ha = y2 - y1
+ xb = x2 + w
+ yb = y1
+ wb = x1 - x2
+ hb = h - y2 + y1
+ elif x2 <= x1 and y2 <= y1:
+ xa = x2 + w
+ ya = y1
+ wa = x1 - x2
+ ha = h
+ xb = x1
+ yb = y2 + h
+ wb = w
+ hb = y1 - y2
+
+ dc.SetPen(wx.TRANSPARENT_PEN)
+ dc.SetBrush(self.parent.BackgroundBrush)
+ dc.DrawRectangle(xa, ya, wa, ha)
+ dc.DrawRectangle(xb, yb, wb, hb)
+ self.PrevMoveXY = xy_tl
+ if self.parent._ForeDrawList:
+ dc.DrawBitmapPoint(self.parent._ForegroundBuffer,xy_tl)
+ else:
+ dc.DrawBitmapPoint(self.parent._Buffer,xy_tl)
+ dc.EndDrawing()
+
+ def OnWheel(self, event):
+ """
+ By default, zoom in/out by a 0.1 factor per Wheel event.
+ """
+ if event.GetWheelRotation() < 0:
+ self.parent.Zoom(0.9)
+ else:
+ self.parent.Zoom(1.1)
+
+class GUIZoomIn(GUIBase):
+
+ Cursor = MagPlusCursor
+
+ def __init__(self, parent):
+ GUIBase.__init__(self, parent)
+ self.StartRBBox = None
+ self.PrevRBBox = None
+
+ def OnLeftDown(self, event):
+ self.StartRBBox = N.array( event.GetPosition() )
+ self.PrevRBBox = None
+ self.parent.CaptureMouse()
+
+ def OnLeftUp(self, event):
+ #if self.parent.HasCapture():
+ # self.parent.ReleaseMouse()
+ if event.LeftUp() and not self.StartRBBox is None:
+ self.PrevRBBox = None
+ EndRBBox = event.GetPosition()
+ StartRBBox = self.StartRBBox
+ # if mouse has moved less that ten pixels, don't use the box.
+ if ( abs(StartRBBox[0] - EndRBBox[0]) > 10
+ and abs(StartRBBox[1] - EndRBBox[1]) > 10 ):
+ EndRBBox = self.parent.PixelToWorld(EndRBBox)
+ StartRBBox = self.parent.PixelToWorld(StartRBBox)
+ BB = N.array(((min(EndRBBox[0],StartRBBox[0]),
+ min(EndRBBox[1],StartRBBox[1])),
+ (max(EndRBBox[0],StartRBBox[0]),
+ max(EndRBBox[1],StartRBBox[1]))),N.float_)
+ self.parent.ZoomToBB(BB)
+ else:
+ Center = self.parent.PixelToWorld(StartRBBox)
+ self.parent.Zoom(1.5,Center)
+ self.StartRBBox = None
+
+ def OnMove(self, event):
+ # Allways raise the Move event.
+ self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+ if event.Dragging() and event.LeftIsDown() and not (self.StartRBBox is None):
+ xy0 = self.StartRBBox
+ xy1 = N.array( event.GetPosition() )
+ wh = abs(xy1 - xy0)
+ wh[0] = max(wh[0], int(wh[1]*self.parent.AspectRatio))
+ wh[1] = int(wh[0] / self.parent.AspectRatio)
+ xy_c = (xy0 + xy1) / 2
+ dc = wx.ClientDC(self.parent)
+ dc.BeginDrawing()
+ dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
+ dc.SetBrush(wx.TRANSPARENT_BRUSH)
+ dc.SetLogicalFunction(wx.XOR)
+ if self.PrevRBBox:
+ dc.DrawRectanglePointSize(*self.PrevRBBox)
+ self.PrevRBBox = ( xy_c - wh/2, wh )
+ dc.DrawRectanglePointSize( *self.PrevRBBox )
+ dc.EndDrawing()
+
+ def UpdateScreen(self):
+ """
+ Update gets called if the screen has been repainted in the middle of a zoom in
+ so the Rubber Band Box can get updated
+ """
+ if self.PrevRBBox is not None:
+ dc = wx.ClientDC(self.parent)
+ dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
+ dc.SetBrush(wx.TRANSPARENT_BRUSH)
+ dc.SetLogicalFunction(wx.XOR)
+ dc.DrawRectanglePointSize(*self.PrevRBBox)
+
+ def OnRightDown(self, event):
+ self.parent.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
+
+ def OnWheel(self, event):
+ if event.GetWheelRotation() < 0:
+ self.parent.Zoom(0.9)
+ else:
+ self.parent.Zoom(1.1)
+
+class GUIZoomOut(GUIBase):
+
+ Cursor = MagMinusCursor
+
+ def OnLeftDown(self, event):
+ self.parent.Zoom(1/1.5, event.GetPosition(), centerCoords="pixel")
+
+ def OnRightDown(self, event):
+ self.parent.Zoom(1.5, event.GetPosition(), centerCoords="pixel")
+
+ def OnWheel(self, event):
+ if event.GetWheelRotation() < 0:
+ self.parent.Zoom(0.9)
+ else:
+ self.parent.Zoom(1.1)
+
+ def OnMove(self, event):
+ # Allways raise the Move event.
+ self.parent._RaiseMouseEvent(event,FloatCanvas.EVT_FC_MOTION)
+
"""
import wx
-
import FloatCanvas, Resources
-ID_ZOOM_IN_BUTTON = wx.NewId()
-ID_ZOOM_OUT_BUTTON = wx.NewId()
-ID_ZOOM_TO_FIT_BUTTON = wx.NewId()
-ID_MOVE_MODE_BUTTON = wx.NewId()
-ID_POINTER_BUTTON = wx.NewId()
-
-#---------------------------------------------------------------------------
-
class NavCanvas(wx.Panel):
"""
NavCanvas.py
This is a high level window that encloses the FloatCanvas in a panel
and adds a Navigation toolbar.
- Copyright: Christopher Barker)
-
- License: Same as the version of wxPython you are using it with
-
- Please let me know if you're using this!!!
+ """
- Contact me at:
+ def __init__(self,
+ parent,
+ id = wx.ID_ANY,
+ size = wx.DefaultSize,
+ **kwargs): # The rest just get passed into FloatCanvas
+ wx.Panel.__init__(self, parent, id, size=size)
- Chris.Barker@noaa.gov
+ self.BuildToolbar()
+ ## Create the vertical sizer for the toolbar and Panel
+ box = wx.BoxSizer(wx.VERTICAL)
+ box.Add(self.ToolBar, 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)
- """
-
- def __init__(self, parent, id = -1,
- size = wx.DefaultSize,
- **kwargs): # The rest just get passed into FloatCanvas
+ self.Canvas = FloatCanvas.FloatCanvas(self, **kwargs)
+ box.Add(self.Canvas, 1, wx.GROW)
- wx.Panel.__init__( self, parent, id, wx.DefaultPosition, size)
+ self.SetSizerAndFit(box)
- ## Create the vertical sizer for the toolbar and Panel
- box = wx.BoxSizer(wx.VERTICAL)
- box.Add(self.BuildToolbar(), 0, wx.ALL | wx.ALIGN_LEFT | wx.GROW, 4)
-
- self.Canvas = FloatCanvas.FloatCanvas( self, wx.NewId(),
- size = wx.DefaultSize,
- **kwargs)
- box.Add(self.Canvas,1,wx.GROW)
- box.Fit(self)
- self.SetSizer(box)
+ import GUIMode # here so that it doesn't get imported before wx.App()
+ self.GUIZoomIn = GUIMode.GUIZoomIn(self.Canvas)
+ self.GUIZoomOut = GUIMode.GUIZoomOut(self.Canvas)
+ self.GUIMove = GUIMode.GUIMove(self.Canvas)
+ self.GUIMouse = GUIMode.GUIMouse(self.Canvas)
# default to Mouse mode
- self.ToolBar.ToggleTool(ID_POINTER_BUTTON,1)
- self.Canvas.SetMode("Mouse")
-
- return None
+ self.ToolBar.ToggleTool(self.PointerTool.GetId(), True)
+ self.Canvas.SetMode(self.GUIMouse)
- def __getattr__(self, name):
- """
- Delegate all extra methods to the Canvas
- """
- attrib = getattr(self.Canvas, name)
- ## add the attribute to this module's dict for future calls
- self.__dict__[name] = attrib
- return attrib
+ return None
def BuildToolbar(self):
- tb = wx.ToolBar(self,-1)
+ tb = wx.ToolBar(self)
self.ToolBar = tb
-
tb.SetToolBitmapSize((24,24))
-
- tb.AddTool(ID_POINTER_BUTTON, Resources.getPointerBitmap(), isToggle=True, shortHelpString = "Pointer")
- wx.EVT_TOOL(self, ID_POINTER_BUTTON, self.SetToolMode)
-
- tb.AddTool(ID_ZOOM_IN_BUTTON, Resources.getMagPlusBitmap(), isToggle=True, shortHelpString = "Zoom In")
- wx.EVT_TOOL(self, ID_ZOOM_IN_BUTTON, self.SetToolMode)
-
- tb.AddTool(ID_ZOOM_OUT_BUTTON, Resources.getMagMinusBitmap(), isToggle=True, shortHelpString = "Zoom Out")
- wx.EVT_TOOL(self, ID_ZOOM_OUT_BUTTON, self.SetToolMode)
-
- tb.AddTool(ID_MOVE_MODE_BUTTON, Resources.getHandBitmap(), isToggle=True, shortHelpString = "Move")
- wx.EVT_TOOL(self, ID_MOVE_MODE_BUTTON, self.SetToolMode)
-
+
+ self.PointerTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getPointerBitmap(), shortHelp = "Pointer")
+ self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIMouse), self.PointerTool)
+
+ self.ZoomInTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getMagPlusBitmap(), shortHelp = "Zoom In")
+ self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIZoomIn), self.ZoomInTool)
+
+ self.ZoomOutTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getMagMinusBitmap(), shortHelp = "Zoom Out")
+ self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIZoomOut), self.ZoomOutTool)
+
+ self.MoveTool = tb.AddRadioTool(wx.ID_ANY, bitmap=Resources.getHandBitmap(), shortHelp = "Move")
+ self.Bind(wx.EVT_TOOL, lambda evt : self.SetMode(Mode=self.GUIMove), self.MoveTool)
+
tb.AddSeparator()
-
- tb.AddControl(wx.Button(tb, ID_ZOOM_TO_FIT_BUTTON, "Zoom To Fit",wx.DefaultPosition, wx.DefaultSize))
- wx.EVT_BUTTON(self, ID_ZOOM_TO_FIT_BUTTON, self.ZoomToFit)
+
+ self.ZoomButton = wx.Button(tb, label="Zoom To Fit")
+ tb.AddControl(self.ZoomButton)
+ self.ZoomButton.Bind(wx.EVT_BUTTON, self.ZoomToFit)
tb.Realize()
- S = tb.GetSize()
- tb.SetSizeHints(S[0],S[1])
+ ## fixme: remove this when the bug is fixed!
+ wx.CallAfter(self.HideShowHack) # this required on wxPython 2.8.3 on OS-X
+
return tb
- def SetToolMode(self,event):
- for id in [ID_ZOOM_IN_BUTTON,
- ID_ZOOM_OUT_BUTTON,
- ID_MOVE_MODE_BUTTON,
- ID_POINTER_BUTTON]:
- self.ToolBar.ToggleTool(id,0)
- self.ToolBar.ToggleTool(event.GetId(),1)
- if event.GetId() == ID_ZOOM_IN_BUTTON:
- self.Canvas.SetMode("ZoomIn")
- elif event.GetId() == ID_ZOOM_OUT_BUTTON:
- self.Canvas.SetMode("ZoomOut")
- elif event.GetId() == ID_MOVE_MODE_BUTTON:
- self.Canvas.SetMode("Move")
- elif event.GetId() == ID_POINTER_BUTTON:
- self.Canvas.SetMode("Mouse")
+ def HideShowHack(self):
+ ##fixme: remove this when the bug is fixed!
+ """
+ Hack to hide and show button on toolbar to get around OS-X bug on
+ wxPython2.8 on OS-X
+ """
+ self.ZoomButton.Hide()
+ self.ZoomButton.Show()
+ def SetMode(self, Mode):
+ self.Canvas.SetMode(Mode)
def ZoomToFit(self,Event):
self.Canvas.ZoomToBB()
+ self.Canvas.SetFocus() # Otherwise the focus stays on the Button, and wheel events are lost.
--- /dev/null
+"""
+A Bounding Box object and assorted utilities , subclassed from a numpy array
+
+"""
+
+import numpy as N
+
+class BBox(N.ndarray):
+ """
+ A Bounding Box object:
+
+ Takes Data as an array. Data is any python sequence that can be turned into a
+ 2x2 numpy array of floats:
+
+ [[MinX, MinY ],
+ [MaxX, MaxY ]]
+
+ It is a subclass of numpy.ndarray, so for the most part it can be used as
+ an array, and arrays that fit the above description can be used in its place.
+
+ Usually created by the factory functions:
+
+ asBBox
+
+ and
+
+ fromPoints
+
+ """
+ def __new__(subtype, data):
+ """
+ Takes Data as an array. Data is any python sequence that can be turned into a
+ 2x2 numpy array of floats:
+
+ [[MinX, MinY ],
+ [MaxX, MaxY ]]
+
+ You don't usually call this directly. BBox objects are created with the factory functions:
+
+ asBBox
+
+ and
+
+ fromPoints
+
+ """
+ arr = N.array(data, N.float)
+ arr.shape = (2,2)
+ if arr[0,0] > arr[1,0] or arr[0,1] > arr[1,1]:
+ # note: zero sized BB OK.
+ raise ValueError("BBox values not aligned: \n minimum values must be less that maximum values")
+ return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
+
+ def Overlaps(self, BB):
+ """
+ Overlap(BB):
+
+ Tests if the given Bounding Box overlaps with this one.
+ Returns True is the Bounding boxes overlap, False otherwise
+ If they are just touching, returns True
+ """
+
+ if ( (self[1,0] >= BB[0,0]) and (self[0,0] <= BB[1,0]) and
+ (self[1,1] >= BB[0,1]) and (self[0,1] <= BB[1,1]) ):
+ return True
+ else:
+ return False
+
+ def Inside(self, BB):
+ """
+ Inside(BB):
+
+ Tests if the given Bounding Box is entirely inside this one.
+
+ Returns True if it is entirely inside, or touching the
+ border.
+
+ Returns False otherwise
+ """
+ if ( (BB[0,0] >= self[0,0]) and (BB[1,0] <= self[1,0]) and
+ (BB[0,1] >= self[0,1]) and (BB[1,1] <= self[1,1]) ):
+ return True
+ else:
+ return False
+
+ def Merge(self, BB):
+ """
+ Joins this bounding box with the one passed in, maybe making this one bigger
+
+ """
+
+ if BB[0,0] < self[0,0]: self[0,0] = BB[0,0]
+ if BB[0,1] < self[0,1]: self[0,1] = BB[0,1]
+ if BB[1,0] > self[1,0]: self[1,0] = BB[1,0]
+ if BB[1,1] > self[1,1]: self[1,1] = BB[1,1]
+
+ ### This could be used for a make BB from a bunch of BBs
+
+ #~ def _getboundingbox(bboxarray): # lrk: added this
+ #~ # returns the bounding box of a bunch of bounding boxes
+ #~ upperleft = N.minimum.reduce(bboxarray[:,0])
+ #~ lowerright = N.maximum.reduce(bboxarray[:,1])
+ #~ return N.array((upperleft, lowerright), N.float)
+ #~ _getboundingbox = staticmethod(_getboundingbox)
+
+
+ ## Save the ndarray __eq__ for internal use.
+ Array__eq__ = N.ndarray.__eq__
+ def __eq__(self, BB):
+ """
+ __eq__(BB) The equality operator
+
+ A == B if and only if all the entries are the same
+
+ """
+ return N.all(self.Array__eq__(BB))
+
+
+def asBBox(data):
+ """
+ returns a BBox object.
+
+ If object is a BBox, it is returned unaltered
+
+ If object is a numpy array, a BBox object is returned that shares a
+ view of the data with that array
+
+ """
+
+ if isinstance(data, BBox):
+ return data
+ arr = N.asarray(data, N.float)
+ return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
+
+def fromPoints(Points):
+ """
+ fromPoints (Points).
+
+ reruns the bounding box of the set of points in Points. Points can
+ be any python object that can be turned into a numpy NX2 array of Floats.
+
+ If a single point is passed in, a zero-size Bounding Box is returned.
+
+ """
+ Points = N.asarray(Points, N.float).reshape(-1,2)
+
+ arr = N.vstack( (Points.min(0), Points.max(0)) )
+ return N.ndarray.__new__(BBox, shape=arr.shape, dtype=arr.dtype, buffer=arr)
+
+def fromBBArray(BBarray):
+ """
+ Builds a BBox object from an array of Bounding Boxes.
+ The resulting Bounding Box encompases all the included BBs.
+
+ The BBarray is in the shape: (Nx2x2) where BBarray[n] is a 2x2 array that represents a BBox
+ """
+
+ #upperleft = N.minimum.reduce(BBarray[:,0])
+ #lowerright = N.maximum.reduce(BBarray[:,1])
+
+# BBarray = N.asarray(BBarray, N.float).reshape(-1,2)
+# arr = N.vstack( (BBarray.min(0), BBarray.max(0)) )
+ BBarray = N.asarray(BBarray, N.float).reshape(-1,2,2)
+ arr = N.vstack( (BBarray[:,0,:].min(0), BBarray[:,1,:].max(0)) )
+ return asBBox(arr)
+ #return asBBox( (upperleft, lowerright) ) * 2
+
+
+
+
--- /dev/null
+
+"""
+Test code for the BBox Object
+
+"""
+
+import unittest
+
+from BBox import *
+
+class testCreator(unittest.TestCase):
+ def testCreates(self):
+ B = BBox(((0,0),(5,5)))
+ self.failUnless(isinstance(B, BBox))
+
+ def testType(self):
+ B = N.array(((0,0),(5,5)))
+ self.failIf(isinstance(B, BBox))
+
+ def testDataType(self):
+ B = BBox(((0,0),(5,5)))
+ self.failUnless(B.dtype == N.float)
+
+ def testShape(self):
+ B = BBox((0,0,5,5))
+ self.failUnless(B.shape == (2,2))
+
+ def testShape2(self):
+ self.failUnlessRaises(ValueError, BBox, (0,0,5) )
+
+ def testShape3(self):
+ self.failUnlessRaises(ValueError, BBox, (0,0,5,6,7) )
+
+ def testArrayConstruction(self):
+ A = N.array(((4,5),(10,12)), N.float_)
+ B = BBox(A)
+ self.failUnless(isinstance(B, BBox))
+
+ def testMinMax(self):
+ self.failUnlessRaises(ValueError, BBox, (0,0,-1,6) )
+
+ def testMinMax2(self):
+ self.failUnlessRaises(ValueError, BBox, (0,0,1,-6) )
+
+ def testMinMax(self):
+ # OK to have a zero-sized BB
+ B = BBox(((0,0),(0,5)))
+ self.failUnless(isinstance(B, BBox))
+
+ def testMinMax2(self):
+ # OK to have a zero-sized BB
+ B = BBox(((10.0,-34),(10.0,-34.0)))
+ self.failUnless(isinstance(B, BBox))
+
+ def testMinMax3(self):
+ # OK to have a tiny BB
+ B = BBox(((0,0),(1e-20,5)))
+ self.failUnless(isinstance(B, BBox))
+
+ def testMinMax4(self):
+ # Should catch tiny difference
+ self.failUnlessRaises(ValueError, BBox, ((0,0), (-1e-20,5)) )
+
+class testAsBBox(unittest.TestCase):
+
+ def testPassThrough(self):
+ B = BBox(((0,0),(5,5)))
+ C = asBBox(B)
+ self.failUnless(B is C)
+
+ def testPassThrough2(self):
+ B = (((0,0),(5,5)))
+ C = asBBox(B)
+ self.failIf(B is C)
+
+ def testPassArray(self):
+ # Different data type
+ A = N.array( (((0,0),(5,5))) )
+ C = asBBox(A)
+ self.failIf(A is C)
+
+ def testPassArray2(self):
+ # same data type -- should be a view
+ A = N.array( (((0,0),(5,5))), N.float_ )
+ C = asBBox(A)
+ A[0,0] = -10
+ self.failUnless(C[0,0] == A[0,0])
+
+class testIntersect(unittest.TestCase):
+
+ def testSame(self):
+ B = BBox(((-23.5, 456),(56, 532.0)))
+ C = BBox(((-23.5, 456),(56, 532.0)))
+ self.failUnless(B.Overlaps(C) )
+
+ def testUpperLeft(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (0, 12),(10, 32.0) ) )
+ self.failUnless(B.Overlaps(C) )
+
+ def testUpperRight(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (12, 12),(25, 32.0) ) )
+ self.failUnless(B.Overlaps(C) )
+
+ def testLowerRight(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (12, 5),(25, 15) ) )
+ self.failUnless(B.Overlaps(C) )
+
+ def testLowerLeft(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (-10, 5),(8.5, 15) ) )
+ self.failUnless(B.Overlaps(C) )
+
+ def testBelow(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (-10, 5),(8.5, 9.2) ) )
+ self.failIf(B.Overlaps(C) )
+
+ def testAbove(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (-10, 25.001),(8.5, 32) ) )
+ self.failIf(B.Overlaps(C) )
+
+ def testLeft(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (4, 8),(4.95, 32) ) )
+ self.failIf(B.Overlaps(C) )
+
+ def testRight(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (17.1, 8),(17.95, 32) ) )
+ self.failIf(B.Overlaps(C) )
+
+ def testInside(self):
+ B = BBox( ( (-15, -25),(-5, -10) ) )
+ C = BBox( ( (-12, -22), (-6, -8) ) )
+ self.failUnless(B.Overlaps(C) )
+
+ def testOutside(self):
+ B = BBox( ( (-15, -25),(-5, -10) ) )
+ C = BBox( ( (-17, -26), (3, 0) ) )
+ self.failUnless(B.Overlaps(C) )
+
+ def testTouch(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (15, 8),(17.95, 32) ) )
+ self.failUnless(B.Overlaps(C) )
+
+ def testCorner(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (15, 25),(17.95, 32) ) )
+ self.failUnless(B.Overlaps(C) )
+
+ def testZeroSize(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (15, 25),(15, 25) ) )
+ self.failUnless(B.Overlaps(C) )
+
+ def testZeroSize2(self):
+ B = BBox( ( (5, 10),(5, 10) ) )
+ C = BBox( ( (15, 25),(15, 25) ) )
+ self.failIf(B.Overlaps(C) )
+
+ def testZeroSize3(self):
+ B = BBox( ( (5, 10),(5, 10) ) )
+ C = BBox( ( (0, 8),(10, 12) ) )
+ self.failUnless(B.Overlaps(C) )
+
+ def testZeroSize4(self):
+ B = BBox( ( (5, 1),(10, 25) ) )
+ C = BBox( ( (8, 8),(8, 8) ) )
+ self.failUnless(B.Overlaps(C) )
+
+
+
+class testEquality(unittest.TestCase):
+ def testSame(self):
+ B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ C = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ self.failUnless(B == C)
+
+ def testIdentical(self):
+ B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ self.failUnless(B == B)
+
+ def testNotSame(self):
+ B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ C = BBox( ( (1.0, 2.0), (5.0, 10.1) ) )
+ self.failIf(B == C)
+
+ def testWithArray(self):
+ B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ C = N.array( ( (1.0, 2.0), (5.0, 10.0) ) )
+ self.failUnless(B == C)
+
+ def testWithArray2(self):
+ B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ C = N.array( ( (1.0, 2.0), (5.0, 10.0) ) )
+ self.failUnless(C == B)
+
+ def testWithArray2(self):
+ B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ C = N.array( ( (1.01, 2.0), (5.0, 10.0) ) )
+ self.failIf(C == B)
+
+class testInside(unittest.TestCase):
+ def testSame(self):
+ B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ C = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ self.failUnless(B.Inside(C))
+
+ def testPoint(self):
+ B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ C = BBox( ( (3.0, 4.0), (3.0, 4.0) ) )
+ self.failUnless(B.Inside(C))
+
+ def testPointOutside(self):
+ B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ C = BBox( ( (-3.0, 4.0), (0.10, 4.0) ) )
+ self.failIf(B.Inside(C))
+
+ def testUpperLeft(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (0, 12),(10, 32.0) ) )
+ self.failIf(B.Inside(C) )
+
+ def testUpperRight(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (12, 12),(25, 32.0) ) )
+ self.failIf(B.Inside(C) )
+
+ def testLowerRight(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (12, 5),(25, 15) ) )
+ self.failIf(B.Inside(C) )
+
+ def testLowerLeft(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (-10, 5),(8.5, 15) ) )
+ self.failIf(B.Inside(C) )
+
+ def testBelow(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (-10, 5),(8.5, 9.2) ) )
+ self.failIf(B.Inside(C) )
+
+ def testAbove(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (-10, 25.001),(8.5, 32) ) )
+ self.failIf(B.Inside(C) )
+
+ def testLeft(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (4, 8),(4.95, 32) ) )
+ self.failIf(B.Inside(C) )
+
+ def testRight(self):
+ B = BBox( ( (5, 10),(15, 25) ) )
+ C = BBox( ( (17.1, 8),(17.95, 32) ) )
+ self.failIf(B.Inside(C) )
+
+class testFromPoints(unittest.TestCase):
+
+ def testCreate(self):
+ Pts = N.array( ((5,2),
+ (3,4),
+ (1,6),
+ ), N.float_ )
+ B = fromPoints(Pts)
+ #B = BBox( ( (1.0, 2.0), (5.0, 10.0) ) )
+ self.failUnless(B[0,0] == 1.0 and
+ B[0,1] == 2.0 and
+ B[1,0] == 5.0 and
+ B[1,1] == 6.0
+ )
+ def testCreateInts(self):
+ Pts = N.array( ((5,2),
+ (3,4),
+ (1,6),
+ ) )
+ B = fromPoints(Pts)
+ self.failUnless(B[0,0] == 1.0 and
+ B[0,1] == 2.0 and
+ B[1,0] == 5.0 and
+ B[1,1] == 6.0
+ )
+
+ def testSinglePoint(self):
+ Pts = N.array( (5,2), N.float_ )
+ B = fromPoints(Pts)
+ self.failUnless(B[0,0] == 5.0 and
+ B[0,1] == 2.0 and
+ B[1,0] == 5.0 and
+ B[1,1] == 2.0
+ )
+
+ def testListTuples(self):
+ Pts = [ (3, 6.5),
+ (13, 43.2),
+ (-4.32, -4),
+ (65, -23),
+ (-0.0001, 23.432),
+ ]
+ B = fromPoints(Pts)
+ self.failUnless(B[0,0] == -4.32 and
+ B[0,1] == -23.0 and
+ B[1,0] == 65.0 and
+ B[1,1] == 43.2
+ )
+class testMerge(unittest.TestCase):
+ A = BBox( ((-23.5, 456), (56, 532.0)) )
+ B = BBox( ((-20.3, 460), (54, 465 )) )# B should be completely inside A
+ C = BBox( ((-23.5, 456), (58, 540.0)) )# up and to the right or A
+ D = BBox( ((-26.5, 12), (56, 532.0)) )
+
+ def testInside(self):
+ C = self.A.copy()
+ C.Merge(self.B)
+ self.failUnless(C == self.A)
+
+ def testFullOutside(self):
+ C = self.B.copy()
+ C.Merge(self.A)
+ self.failUnless(C == self.A)
+
+ def testUpRight(self):
+ A = self.A.copy()
+ A.Merge(self.C)
+ self.failUnless(A[0] == self.A[0] and A[1] == self.C[1])
+
+ def testDownLeft(self):
+ A = self.A.copy()
+ A.Merge(self.D)
+ self.failUnless(A[0] == self.D[0] and A[1] == self.A[1])
+
+class testBBarray(unittest.TestCase):
+ BBarray = N.array( ( ((-23.5, 456), (56, 532.0)),
+ ((-20.3, 460), (54, 465 )),
+ ((-23.5, 456), (58, 540.0)),
+ ((-26.5, 12), (56, 532.0)),
+ ),
+ dtype=N.float)
+ print BBarray
+ BB = asBBox( ((-26.5, 12.), ( 58. , 540.)) )
+
+ def testJoin(self):
+ BB = fromBBArray(self.BBarray)
+ self.failUnless(BB == self.BB, "Wrong BB was created. It was:\n%s \nit should have been:\n%s"%(BB, self.BB))
+
+
+if __name__ == "__main__":
+ unittest.main()
--- /dev/null
+"""
+
+Part of the floatcanvas.Utilities package.
+
+This module contains assorted GUI-related utilities that can be used
+with FloatCanvas
+
+So far, they are:
+
+RubberBandBox: used to draw a RubberBand Box on the screen
+
+"""
+import wx
+from floatcanvas import FloatCanvas
+
+class RubberBandBox:
+ """
+ Class to provide a rubber band box that can be drawn on a Window
+
+ """
+
+ def __init__(self, Canvas, CallBack, Tol=5):
+
+ """
+ To initialize:
+
+ RubberBandBox(Canvas, CallBack)
+
+ Canvas: the FloatCanvas you want the Rubber band box to be used on
+
+ CallBack: is the method you want called when the mouse is
+ released. That method will be called, passing in a rect
+ parameter, where rect is: (Point, WH) of the rect in
+ world coords.
+
+ Tol: The tolerance for the smallest rectangle allowed. defaults
+ to 5. In pixels
+
+ Methods:
+
+ Enable() : Enables the Rubber Band Box (Binds the events)
+
+ Disable() : Enables the Rubber Band Box (Unbinds the events)
+
+ Attributes:
+
+ CallBack: The callback function, if it's replaced you need to
+ call Enable() again.
+
+ """
+
+ self.Canvas = Canvas
+ self.CallBack = CallBack
+ self.Tol = Tol
+
+ self.Drawing = False
+ self.RBRect = None
+ self.StartPointWorld = None
+
+ return None
+
+ def Enable(self):
+ """
+ Called when you want the rubber band box to be enabled
+
+ """
+
+ # bind events:
+ self.Canvas.Bind(FloatCanvas.EVT_MOTION, self.OnMove )
+ self.Canvas.Bind(FloatCanvas.EVT_LEFT_DOWN, self.OnLeftDown)
+ self.Canvas.Bind(FloatCanvas.EVT_LEFT_UP, self.OnLeftUp )
+
+ def Disable(self):
+ """
+ Called when you don't want the rubber band box to be enabled
+
+ """
+
+ # unbind events:
+ self.Canvas.Unbind(FloatCanvas.EVT_MOTION)
+ self.Canvas.Unbind(FloatCanvas.EVT_LEFT_DOWN)
+ self.Canvas.Unbind(FloatCanvas.EVT_LEFT_UP)
+
+ def OnMove(self, event):
+ if self.Drawing:
+ x, y = self.StartPoint
+ Cornerx, Cornery = event.GetPosition()
+ w, h = ( Cornerx - x, Cornery - y)
+ if abs(w) > self.Tol and abs(h) > self.Tol:
+ # draw the RB box
+ dc = wx.ClientDC(self.Canvas)
+ dc.SetPen(wx.Pen('WHITE', 2, wx.SHORT_DASH))
+ dc.SetBrush(wx.TRANSPARENT_BRUSH)
+ dc.SetLogicalFunction(wx.XOR)
+ if self.RBRect:
+ dc.DrawRectangle(*self.RBRect)
+ self.RBRect = (x, y, w, h )
+ dc.DrawRectangle(*self.RBRect)
+ event.Skip() # skip so that other events can catch these
+
+ def OnLeftDown(self, event):
+ # Start drawing
+ self.Drawing = True
+ self.StartPoint = event.GetPosition()
+ self.StartPointWorld = event.Coords
+
+ def OnLeftUp(self, event):
+ # Stop Drawing
+ if self.Drawing:
+ self.Drawing = False
+ if self.RBRect:
+ WH = event.Coords - self.StartPointWorld
+ wx.CallAfter(self.CallBack, (self.StartPointWorld, WH))
+ self.RBRect = None
+ self.StartPointWorld = None
--- /dev/null
+"""
+__init__ for the floatcanvas Utilities package
+
+"""
+pass
+
+
something else are very quick.
It relies on NumPy, which is needed for speed (maybe, I haven't profiled
-it). It will also use numarray, if you don't have Numeric, but it is
-slower.
+properly) and convenience.
Bugs and Limitations: Lots: patches, fixes welcome
drawing it. This makes it a great deal faster when there are a lot of
objects and you are zoomed in so that only a few are shown.
-One solution is to be able to pass some sort of object set to the DC
-directly. I've used DC.DrawPointList(Points), and it helped a lot with
-drawing lots of points. However, when zoomed in, the Bounding boxes need
-to be checked, so I may some day write C++ code that does the loop and
-checks the BBs.
-
Mouse Events:
There are a full set of custom mouse events. They are just like the
See the Demo for what it can do, and how to use it.
Copyright: Christopher Barker
-
License: Same as the version of wxPython you are using it with.
-Check for updates or answers to questions, send me an email.
+TRAC site for some docs and updates:
+http://morticia.cs.dal.ca/FloatCanvas/
-Please let me know if you're using this!!!
+SVN for latest code:
+svn://morticia.cs.dal.ca/FloatCanvas
+Mailing List:
+http://mail.mithis.com/cgi-bin/mailman/listinfo/floatcanvas
+
+Check for updates or answers to questions, send me an email.
+Please let me know if you're using this!!!
Contact me at:
Chris.Barker@noaa.gov
"""
-__version__ = "0.9.10"
+__version__ = "0.9.18"
if image is None:
return
- bmp = image.ConvertToBitmap()
+ try:
+ bmp = image.ConvertToBitmap()
+ except:
+ return
iwidth = bmp.GetWidth() # dimensions of image file
iheight = bmp.GetHeight()
def OnClose(self, evt):
self.SaveSettings(self.config)
+ self.mgr.UnInit()
+ del self.mgr
evt.Skip()
def FmtSizerItem(self, obj):
+ if obj is None:
+ return ['SizerItem: None']
+
st = ['SizerItem:']
st.append(self.Fmt('proportion', obj.GetProportion()))
st.append(self.Fmt('flag',
this event was generated."""
return self.__selection
+class MaskedComboBoxEventHandler(wx.EvtHandler):
+ """
+ This handler ensures that the derived control can react to events
+ from the base control before any external handlers run, to ensure
+ proper behavior.
+ """
+ def __init__(self, combobox):
+ wx.EvtHandler.__init__(self)
+ self.combobox = combobox
+ combobox.PushEventHandler(self)
+ self.Bind(wx.EVT_SET_FOCUS, self.combobox._OnFocus ) ## defeat automatic full selection
+ self.Bind(wx.EVT_KILL_FOCUS, self.combobox._OnKillFocus ) ## run internal validator
+ self.Bind(wx.EVT_LEFT_DCLICK, self.combobox._OnDoubleClick) ## select field under cursor on dclick
+ self.Bind(wx.EVT_RIGHT_UP, self.combobox._OnContextMenu ) ## bring up an appropriate context menu
+ self.Bind(wx.EVT_CHAR, self.combobox._OnChar ) ## handle each keypress
+ self.Bind(wx.EVT_KEY_DOWN, self.combobox._OnKeyDownInComboBox ) ## for special processing of up/down keys
+ self.Bind(wx.EVT_KEY_DOWN, self.combobox._OnKeyDown ) ## for processing the rest of the control keys
+ ## (next in evt chain)
+ self.Bind(wx.EVT_COMBOBOX, self.combobox._OnDropdownSelect ) ## to bring otherwise completely independent base
+ ## ctrl selection into maskededit framework
+ self.Bind(wx.EVT_TEXT, self.combobox._OnTextChange ) ## color control appropriately & keep
+ ## track of previous value for undo
+
+
class BaseMaskedComboBox( wx.ComboBox, MaskedEditMixin ):
"""
self._SetKeycodeHandler(wx.WXK_UP, self._OnSelectChoice)
self._SetKeycodeHandler(wx.WXK_DOWN, self._OnSelectChoice)
+ self.replace_next_combobox_event = False
+ self.correct_selection = -1
+
if setupEventHandling:
- ## Setup event handlers
- self.Bind(wx.EVT_SET_FOCUS, self._OnFocus ) ## defeat automatic full selection
- self.Bind(wx.EVT_KILL_FOCUS, self._OnKillFocus ) ## run internal validator
- self.Bind(wx.EVT_LEFT_DCLICK, self._OnDoubleClick) ## select field under cursor on dclick
- self.Bind(wx.EVT_RIGHT_UP, self._OnContextMenu ) ## bring up an appropriate context menu
- self.Bind(wx.EVT_CHAR, self._OnChar ) ## handle each keypress
- self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDownInComboBox ) ## for special processing of up/down keys
- self.Bind(wx.EVT_KEY_DOWN, self._OnKeyDown ) ## for processing the rest of the control keys
- ## (next in evt chain)
- self.Bind(wx.EVT_TEXT, self._OnTextChange ) ## color control appropriately & keep
- ## track of previous value for undo
+ ## Setup event handling functions through event handler object,
+ ## to guarantee processing prior to giving event callbacks from
+ ## outside the class:
+ self.evt_handler = MaskedComboBoxEventHandler(self)
+ self.Bind(wx.EVT_WINDOW_DESTROY, self.OnWindowDestroy )
return "<MaskedComboBox: %s>" % self.GetValue()
+ def OnWindowDestroy(self, event):
+ # clean up associated event handler object:
+ if self.RemoveEventHandler(self.evt_handler):
+ self.evt_handler.Destroy()
+ event.Skip()
+
+
def _CalcSize(self, size=None):
"""
Calculate automatic size if allowed; augment base mixin function
# Record current selection and insertion point, for undo
self._prevSelection = self._GetSelection()
self._prevInsertionPoint = self._GetInsertionPoint()
+## dbg('MaskedComboBox::_SetValue(%s), selection beforehand: %d' % (value, self.GetSelection()))
wx.ComboBox.SetValue(self, value)
+## dbg('MaskedComboBox::_SetValue(%s), selection now: %d' % (value, self.GetSelection()))
# text change events don't always fire, so we check validity here
# to make certain formatting is applied:
self._CheckValid()
masked control. NOTE: this must be done in the class derived
from the base wx control.
"""
+## dbg('MaskedComboBox::SetValue(%s)' % value, indent=1)
if not self._mask:
wx.ComboBox.SetValue(value) # revert to base control behavior
+## dbg('no mask; deferring to base class', indent=0)
return
# else...
# empty previous contents, replacing entire value:
+## dbg('MaskedComboBox::SetValue: selection beforehand: %d' % (self.GetSelection()))
self._SetInsertionPoint(0)
self._SetSelection(0, self._masklength)
dateparts = value.split(' ')
dateparts[0] = self._adjustDate(dateparts[0], fixcentury=True)
value = string.join(dateparts, ' ')
-## dbg('adjusted value: "%s"' % value)
value = self._Paste(value, raise_on_invalid=True, just_return_value=True)
else:
raise
+## dbg('adjusted value: "%s"' % value)
- self._SetValue(value)
-#### dbg('queuing insertion after .SetValue', replace_to)
- wx.CallAfter(self._SetInsertionPoint, replace_to)
- wx.CallAfter(self._SetSelection, replace_to, replace_to)
+ # Attempt to compensate for fact that calling .SetInsertionPoint() makes the
+ # selection index -1, even if the resulting set value is in the list.
+ # So, if we are setting a value that's in the list, use index selection instead.
+ if value in self._choices:
+ index = self._choices.index(value)
+ self._prevValue = self._curValue
+ self._curValue = self._choices[index]
+ self._ctrl_constraints._autoCompleteIndex = index
+ self.SetSelection(index)
+ else:
+ self._SetValue(value)
+#### dbg('queuing insertion after .SetValue', replace_to)
+ wx.CallAfter(self._SetInsertionPoint, replace_to)
+ wx.CallAfter(self._SetSelection, replace_to, replace_to)
+## dbg(indent=0)
def _Refresh(self):
Necessary override for bookkeeping on choice selection, to keep current value
current.
"""
-## dbg('MaskedComboBox::SetSelection(%d)' % index)
+## dbg('MaskedComboBox::SetSelection(%d)' % index, indent=1)
if self._mask:
self._prevValue = self._curValue
- self._curValue = self._choices[index]
self._ctrl_constraints._autoCompleteIndex = index
+ if index != -1:
+ self._curValue = self._choices[index]
+ else:
+ self._curValue = None
wx.ComboBox.SetSelection(self, index)
+## dbg('selection now: %d' % self.GetCurrentSelection(), indent=0)
def _OnKeyDownInComboBox(self, event):
"""
- This function is necessary because navigation and control key
- events do not seem to normally be seen by the wxComboBox's
- EVT_CHAR routine. (Tabs don't seem to be visible no matter
- what... {:-( )
+ This function is necessary because navigation and control key events
+ do not seem to normally be seen by the wxComboBox's EVT_CHAR routine.
+ (Tabs don't seem to be visible no matter what, except for CB_READONLY
+ controls, for some bizarre reason... {:-( )
"""
+ key = event.GetKeyCode()
+## dbg('MaskedComboBox::OnKeyDownInComboBox(%d)' % key)
if event.GetKeyCode() in self._nav + self._control:
- self._OnChar(event)
- return
+ if not self._IsEditable():
+ # WANTS_CHARS with CB_READONLY apparently prevents navigation on WXK_TAB;
+ # ensure we can still navigate properly, as maskededit mixin::OnChar assumes
+ # that event.Skip() will just work, but it doesn't:
+ if self._keyhandlers.has_key(key):
+ self._keyhandlers[key](event)
+ # else pass
+ else:
+## dbg('calling OnChar()')
+ self._OnChar(event)
else:
event.Skip() # let mixin default KeyDown behavior occur
+## dbg(indent=0)
+
+
+ def _OnDropdownSelect(self, event):
+ """
+ This function appears to be necessary because dropdown selection seems to
+ manipulate the contents of the control in an inconsistent way, properly
+ changing the selection index, but *not* the value. (!) Calling SetSelection()
+ on a selection event for the same selection would seem like a nop, but it seems to
+ fix the problem.
+ """
+## dbg('MaskedComboBox::OnDropdownSelect(%d)' % event.GetSelection(), indent=1)
+ if self.replace_next_combobox_event:
+## dbg('replacing EVT_COMBOBOX')
+ self.replace_next_combobox_event = False
+ self._OnAutoSelect(self._ctrl_constraints, self.correct_selection)
+ else:
+## dbg('skipping EVT_COMBOBOX')
+ event.Skip()
+## dbg(indent=0)
def _OnSelectChoice(self, event):
Override mixin (empty) autocomplete handler, so that autocompletion causes
combobox to update appropriately.
"""
-## dbg('MaskedComboBox::OnAutoSelect', field._index, indent=1)
+## dbg('MaskedComboBox::OnAutoSelect(%d, %d)' % (field._index, match_index), indent=1)
## field._autoCompleteIndex = match_index
if field == self._ctrl_constraints:
self.SetSelection(match_index)
MaskedComboBoxSelectEvent( self.GetId(), match_index, self ) )
self._CheckValid()
## dbg('field._autoCompleteIndex:', match_index)
-## dbg('self.GetSelection():', self.GetSelection())
+## dbg('self.GetCurrentSelection():', self.GetCurrentSelection())
end = self._goEnd(getPosOnly=True)
## dbg('scheduling set of end position to:', end)
# work around bug in wx 2.5
item in the list. (and then does the usual OnReturn bit.)
"""
## dbg('MaskedComboBox::OnReturn', indent=1)
-## dbg('current value: "%s"' % self.GetValue(), 'current index:', self.GetSelection())
- if self.GetSelection() == -1 and self.GetValue().lower().strip() in self._ctrl_constraints._compareChoices:
- wx.CallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
-
+## dbg('current value: "%s"' % self.GetValue(), 'current selection:', self.GetCurrentSelection())
+ if self.GetCurrentSelection() == -1 and self.GetValue().lower().strip() in self._ctrl_constraints._compareChoices:
+## dbg('attempting to correct the selection to make it %d' % self._ctrl_constraints._autoCompleteIndex)
+## wx.CallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
+ self.replace_next_combobox_event = True
+ self.correct_selection = self._ctrl_constraints._autoCompleteIndex
event.m_keyCode = wx.WXK_TAB
event.Skip()
## dbg(indent=0)
+ def _LostFocus(self):
+## dbg('MaskedComboBox::LostFocus; Selection=%d, value="%s"' % (self.GetSelection(), self.GetValue()))
+ if self.GetCurrentSelection() == -1 and self.GetValue().lower().strip() in self._ctrl_constraints._compareChoices:
+## dbg('attempting to correct the selection to make it %d' % self._ctrl_constraints._autoCompleteIndex)
+ wx.CallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
+
+
class ComboBox( BaseMaskedComboBox, MaskedEditAccessorsMixin ):
"""
The "user-visible" masked combobox control, this class is
__i = 0
## CHANGELOG:
## ====================
+## Version 1.4
+## 1. Added handler for EVT_COMBOBOX to address apparently inconsistent behavior
+## of control when the dropdown control is used to do a selection.
+## NOTE: due to misbehavior of wx.ComboBox re: losing all concept of the
+## current selection index if SetInsertionPoint() is called, which is required
+## to support masked .SetValue(), this control is flaky about retaining selection
+## information. I can't truly fix this without major changes to the base control,
+## but I've tried to compensate as best I can.
+## TODO: investigate replacing base control with ComboCtrl instead...
+## 2. Fixed navigation in readonly masked combobox, which was not working because
+## the base control doesn't do navigation if style=CB_READONLY|WANTS_CHARS.
+##
+##
## Version 1.3
## 1. Made definition of "hack" GetMark conditional on base class not
## implementing it properly, to allow for migration in wx code base
#----------------------------------------------------------------------------
# Name: maskededit.py
-# Authors: Jeff Childers, Will Sadkin
-# Email: jchilders_98@yahoo.com, wsadkin@parlancecorp.com
+# Authors: Will Sadkin, Jeff Childers
+# Email: wsadkin@parlancecorp.com, jchilders_98@yahoo.com
# Created: 02/11/2003
# Copyright: (c) 2003 by Jeff Childers, Will Sadkin, 2003
-# Portions: (c) 2002 by Will Sadkin, 2002-2006
+# Portions: (c) 2002 by Will Sadkin, 2002-2007
# RCS-ID: $Id$
-# License: wxWindows license
+# License: wxWidgets license
#----------------------------------------------------------------------------
# NOTE:
# MaskedEdit controls are based on a suggestion made on [wxPython-Users] by
#
#----------------------------------------------------------------------------
#
-# 03/30/2004 - Will Sadkin (wsadkin@nameconnector.com)
+# 03/30/2004 - Will Sadkin (wsadkin@parlancecorp.com)
#
# o Split out TextCtrl, ComboBox and IpAddrCtrl into their own files,
# o Reorganized code into masked package
raiseOnInvalidPaste False by default; normally a bad paste simply is ignored with a bell;
if True, this will cause a ValueError exception to be thrown,
with the .value attribute of the exception containing the bad value.
- ===================== ==================================================================
+
+ stopFieldChangeIfInvalid
+ False by default; tries to prevent navigation out of a field if its
+ current value is invalid. Can be used to create a hybrid of validation
+ settings, allowing intermediate invalid values in a field without
+ sacrificing ability to limit values as with validRequired.
+ NOTE: It is possible to end up with an invalid value when using
+ this option if focus is switched to some other control via mousing.
+ To avoid this, consider deriving a class that defines _LostFocus()
+ function that returns the control to a valid value when the focus
+ shifts. (AFAICT, The change in focus is unpreventable.)
+ ===================== =================================================================
Coloring Behavior
'emptyInvalid': False, ## Set to True to make EMPTY = INVALID
'description': "", ## primarily for autoformats, but could be useful elsewhere
'raiseOnInvalidPaste': False, ## if True, paste into field will cause ValueError
+ 'stopFieldChangeIfInvalid': False,## if True, disallow field navigation out of invalid field
}
# This list contains all parameters that when set at the control level should
# propagate down to each field:
propagating_params = ('fillChar', 'groupChar', 'decimalChar','useParensForNegatives',
- 'compareNoCase', 'emptyInvalid', 'validRequired', 'raiseOnInvalidPaste')
+ 'compareNoCase', 'emptyInvalid', 'validRequired', 'raiseOnInvalidPaste',
+ 'stopFieldChangeIfInvalid')
def __init__(self, **kwargs):
"""
char = char.decode(self._defaultEncoding)
else:
char = unichr(event.GetUnicodeKey())
- dbg('unicode char:', char)
+## dbg('unicode char:', char)
excludes = u''
if type(field._excludeChars) != types.UnicodeType:
excludes += field._excludeChars.decode(self._defaultEncoding)
## dbg(indent=0)
return False
+ field = self._FindField(sel_to)
+ index = field._index
+ field_start, field_end = field._extent
+ slice = self._GetValue()[field_start:field_end]
+
+## dbg('field._stopFieldChangeIfInvalid?', field._stopFieldChangeIfInvalid)
+## dbg('field.IsValid(slice)?', field.IsValid(slice))
+
+ if field._stopFieldChangeIfInvalid and not field.IsValid(slice):
+## dbg('field invalid; field change disallowed')
+ if not wx.Validator_IsSilent():
+ wx.Bell()
+## dbg(indent=0)
+ return False
+
if event.ShiftDown():
# NOTE: doesn't yet work with SHIFT-tab under wx; the control
# never sees this event! (But I've coded for it should it ever work,
# and it *does* work for '.' in IpAddrCtrl.)
- field = self._FindField(pos)
- index = field._index
- field_start = field._extent[0]
+
if pos < field_start:
## dbg('cursor before 1st field; cannot change to a previous field')
if not wx.Validator_IsSilent():
wx.Bell()
+## dbg(indent=0)
return False
if event.ControlDown():
else:
# "Go forward"
- field = self._FindField(sel_to)
- field_start, field_end = field._extent
if event.ControlDown():
## dbg('queuing select to end of field:', pos, field_end)
wx.CallAfter(self._SetInsertionPoint, pos)
wx.CallAfter(self._SetInsertionPoint, next_pos)
## dbg(indent=0)
return False
+## dbg(indent=0)
def _OnDecimalPoint(self, event):
## dbg('MaskedEditMixin::_OnDecimalPoint', indent=1)
+ field = self._FindField(self._GetInsertionPoint())
+ start, end = field._extent
+ slice = self._GetValue()[start:end]
+
+ if field._stopFieldChangeIfInvalid and not field.IsValid(slice):
+ if not wx.Validator_IsSilent():
+ wx.Bell()
+ return False
pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
def _findNextEntry(self,pos, adjustInsert=True):
""" Find the insertion point for the next valid entry character position."""
-## dbg('MaskedEditMixin::_findNextEntry', indent=1)
+## dbg('MaskedEditMixin::_findNextEntry', indent=1)
if self._isTemplateChar(pos) or pos in self._explicit_field_boundaries: # if changing fields, pay attn to flag
adjustInsert = adjustInsert
else: # else within a field; flag not relevant
#### dbg('field_len?', field_len)
#### dbg('pos==end; len (slice) < field_len?', len(slice) < field_len)
#### dbg('not field._moveOnFieldFull?', not field._moveOnFieldFull)
- if len(slice) == field_len and field._moveOnFieldFull:
+ if( len(slice) == field_len and field._moveOnFieldFull
+ and (not field._stopFieldChangeIfInvalid or
+ field._stopFieldChangeIfInvalid and field.IsValid(slice))):
# move cursor to next field:
pos = self._findNextEntry(pos)
self._SetInsertionPoint(pos)
# else make sure the user is not trying to type over a template character
# If they are, move them to the next valid entry position
elif self._isTemplateChar(pos):
- if( not field._moveOnFieldFull
- and (not self._signOk
- or (self._signOk
- and field._index == 0
- and pos > 0) ) ): # don't move to next field without explicit cursor movement
+ if( (not field._moveOnFieldFull
+ and (not self._signOk
+ or (self._signOk and field._index == 0 and pos > 0) ) )
+
+ or (field._stopFieldChangeIfInvalid
+ and not field.IsValid(self._GetValue()[start:end]) ) ):
+
+ # don't move to next field without explicit cursor movement
pass
else:
# find next valid position
#### dbg('field._moveOnFieldFull?', field._moveOnFieldFull)
#### dbg('len(fstr.lstrip()) == end-start?', len(fstr.lstrip()) == end-start)
if( field._moveOnFieldFull and pos == end
- and len(fstr.lstrip()) == end-start): # if field now full
+ and len(fstr.lstrip()) == end-start # if field now full
+ and (not field._stopFieldChangeIfInvalid # and we either don't care about valid
+ or (field._stopFieldChangeIfInvalid # or we do and the current field value is valid
+ and field.IsValid(fstr)))):
+
newpos = self._findNextEntry(end) # go to next field
else:
newpos = pos # else keep cursor at current position
if( field._insertRight # if insert-right field (but we didn't start at right edge)
and field._moveOnFieldFull # and should move cursor when full
- and len(newtext[start:end].strip()) == end-start): # and field now full
+ and len(newtext[start:end].strip()) == end-start # and field now full
+ and (not field._stopFieldChangeIfInvalid # and we either don't care about valid
+ or (field._stopFieldChangeIfInvalid # or we do and the current field value is valid
+ and field.IsValid(newtext[start:end].strip())))):
+
newpos = self._findNextEntry(end) # go to next field
## dbg('newpos = nextentry =', newpos)
else:
## CHANGELOG:
## ====================
+## Version 1.13
+## 1. Added parameter option stopFieldChangeIfInvalid, which can be used to relax the
+## validation rules for a control, but make best efforts to stop navigation out of
+## that field should its current value be invalid. Note: this does not prevent the
+## value from remaining invalid if focus for the control is lost, via mousing etc.
+##
## Version 1.12
## 1. Added proper support for NUMPAD keypad keycodes for navigation and control.
##
# Name: wxPython.lib.masked.numctrl.py
# Author: Will Sadkin
# Created: 09/06/2003
-# Copyright: (c) 2003 by Will Sadkin
+# Copyright: (c) 2003-2007 by Will Sadkin
# RCS-ID: $Id$
# License: wxWidgets license
#----------------------------------------------------------------------------
min = None,
max = None,
limited = False,
+ limitOnFieldChange = False,
selectOnEntry = True,
foregroundColour = "Black",
signedForegroundColour = "Red",
If False and bounds are set, out-of-bounds values will
result in a background colored with the current invalidBackgroundColour.
+ limitOnFieldChange
+ An alternative to limited, this boolean indicates whether or not a
+ field change should be allowed if the value in the control
+ is out of bounds. If True, and control focus is lost, this will also
+ cause the control to take on the nearest bound value.
+
selectOnEntry
Boolean indicating whether or not the value in each field of the
control should be automatically selected (for replacement) when
Returns <I>True</I> if the control is currently limiting the
value to fall within the current bounds.
+SetLimitOnFieldChange()
+ If called with a value of True, will cause the control to allow
+ out-of-bounds values, but will prevent field change if attempted
+ via navigation, and if the control loses focus, it will change
+ the value to the nearest bound.
+
+GetLimitOnFieldChange()
+
+IsLimitedOnFieldChange()
+ Returns <I>True</I> if the control is currently limiting the
+ value on field change.
+
SetAllowNone(bool)
If called with a value of True, this function will cause the control
from wx.tools.dbg import Logger
from wx.lib.masked import MaskedEditMixin, Field, BaseMaskedTextCtrl
-dbg = Logger()
+##dbg = Logger()
##dbg(enable=1)
#----------------------------------------------------------------------------
'emptyInvalid',
'validFunc',
'validRequired',
+ 'stopFieldChangeIfInvalid',
)
for param in exposed_basectrl_params:
propname = param[0].upper() + param[1:]
'min': None, # by default, no bounds set
'max': None,
'limited': False, # by default, no limiting even if bounds set
+ 'limitOnFieldChange': False, # by default, don't limit if changing fields, even if bounds set
'allowNone': False, # by default, don't allow empty value
'selectOnEntry': True, # by default, select the value of each field on entry
'foregroundColour': "Black",
maskededit_kwargs['validRequired'] = False
self._limited = kwargs['limited']
+ if kwargs.has_key('limitOnFieldChange'):
+ if kwargs['limitOnFieldChange'] and not self._limitOnFieldChange:
+ maskededit_kwargs['stopFieldChangeIfInvalid'] = True
+ elif kwargs['limitOnFieldChange'] and self._limitOnFieldChange:
+ maskededit_kwargs['stopFieldChangeIfInvalid'] = False
+
## dbg('maskededit_kwargs:', maskededit_kwargs)
if maskededit_kwargs.keys():
self.SetCtrlParameters(**maskededit_kwargs)
wx.CallAfter(self.SetInsertionPoint, sel_start) # preserve current selection/position
wx.CallAfter(self.SetSelection, sel_start, sel_to)
+
+ def _OnChangeField(self, event):
+ """
+ This routine enhances the base masked control _OnFieldChange(). It's job
+ is to ensure limits are imposed if limitOnFieldChange is enabled.
+ """
+## dbg('NumCtrl::_OnFieldChange', indent=1)
+ if self._limitOnFieldChange and not (self._min <= self.GetValue() <= self._max):
+ self._disallowValue()
+## dbg('oob - field change disallowed',indent=0)
+ return False
+ else:
+## dbg(indent=0)
+ return MaskedEditMixin._OnChangeField(self, event) # call the baseclass function
+
+
+ def _LostFocus(self):
+ """
+ On loss of focus, if limitOnFieldChange is set, ensure value conforms to limits.
+ """
+## dbg('NumCtrl::_LostFocus', indent=1)
+ if self._limitOnFieldChange:
+## dbg("limiting on loss of focus")
+ value = self.GetValue()
+ if self._min is not None and value < self._min:
+## dbg('Set to min value:', self._min)
+ self._SetValue(self._toGUI(self._min))
+
+ elif self._max is not None and value > self._max:
+## dbg('Setting to max value:', self._max)
+ self._SetValue(self._toGUI(self._max))
+ # (else do nothing.)
+ # (else do nothing.)
+## dbg(indent=0)
+ return True
+
+
def _SetValue(self, value):
"""
This routine supersedes the base masked control _SetValue(). It is
def GetLimited(self):
""" (For regularization of property accessors) """
- return self.IsLimited
+ return self.IsLimited()
+
+ def SetLimitOnFieldChange(self, limit):
+ """
+ If called with a value of True, this function will cause the control
+ to prevent navigation out of the current field if its value is out-of-bounds,
+ and limit the value to fall within the bounds currently specified if the
+ control loses focus.
+
+ If called with a value of False, this function will disable value
+ limiting, but coloring of out-of-bounds values will still take
+ place if bounds have been set for the control.
+ """
+ self.SetParameters(limitOnFieldChange = limit)
+
+
+ def IsLimitedOnFieldChange(self):
+ """
+ Returns True if the control is currently limiting the
+ value to fall within the current bounds.
+ """
+ return self._limitOnFieldChange
+
+ def GetLimitOnFieldChange(self):
+ """ (For regularization of property accessors) """
+ return self.IsLimitedOnFieldChange()
def IsInBounds(self, value=None):
## 1. Add support for printf-style format specification.
## 2. Add option for repositioning on 'illegal' insertion point.
##
+## Version 1.4
+## 1. In response to user request, added limitOnFieldChange feature, so that
+## out-of-bounds values can be temporarily added to the control, but should
+## navigation be attempted out of an invalid field, it will not navigate,
+## and if focus is lost on a control so limited with an invalid value, it
+## will change the value to the nearest bound.
+##
## Version 1.3
## 1. fixed to allow space for a group char.
##
Allow mixin to set the raw value of the control with this function.
REQUIRED by any class derived from MaskedEditMixin.
"""
-## dbg('MaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent=1)
+## dbg('MaskedTextCtrl::_SetValue("%(value)s", use_change_value=%(use_change_value)d)' % locals(), indent=1)
# Record current selection and insertion point, for undo
self._prevSelection = self._GetSelection()
self._prevInsertionPoint = self._GetInsertionPoint()
wx.TextCtrl.SetValue(self, value)
## dbg(indent=0)
+ def _ChangeValue(self, value):
+ """
+ Allow mixin to set the raw value of the control with this function without
+ generating an event as a result. (New for masked.TextCtrl as of 2.8.4)
+ """
+## dbg('MaskedTextCtrl::_ChangeValue("%(value)s", use_change_value=%(use_change_value)d)' % locals(), indent=1)
+ # Record current selection and insertion point, for undo
+ self._prevSelection = self._GetSelection()
+ self._prevInsertionPoint = self._GetInsertionPoint()
+ wx.TextCtrl.ChangeValue(self, value)
+## dbg(indent=0)
+
def SetValue(self, value):
"""
This function redefines the externally accessible .SetValue() to be
masked control. NOTE: this must be done in the class derived
from the base wx control.
"""
-## dbg('MaskedTextCtrl::SetValue = "%s"' % value, indent=1)
+ self.ModifyValue(value, use_change_value=False)
+
+ def ChangeValue(self, value):
+ """
+ Provided to accomodate similar functionality added to base control in wxPython 2.7.1.1.
+ """
+ self.ModifyValue(value, use_change_value=True)
+
+
+ def ModifyValue(self, value, use_change_value=False):
+ """
+ This factored function of common code does the bulk of the work for SetValue
+ and ChangeValue.
+ """
+## dbg('MaskedTextCtrl::ModifyValue("%(value)s", use_change_value=%(use_change_value)d)' % locals(), indent=1)
if not self._mask:
- wx.TextCtrl.SetValue(self, value) # revert to base control behavior
+ if use_change_value:
+ wx.TextCtrl.ChangeValue(self, value) # revert to base control behavior
+ else:
+ wx.TextCtrl.SetValue(self, value) # revert to base control behavior
return
# empty previous contents, replacing entire value:
value = value[1:]
## dbg('padded value = "%s"' % value)
- # make SetValue behave the same as if you had typed the value in:
+ # make Set/ChangeValue behave the same as if you had typed the value in:
try:
value, replace_to = self._Paste(value, raise_on_invalid=True, just_return_value=True)
if self._isFloat:
else:
## dbg('exception thrown', indent=0)
raise
-
- self._SetValue(value) # note: to preserve similar capability, .SetValue()
- # does not change IsModified()
-#### dbg('queuing insertion after .SetValue', replace_to)
+ if use_change_value:
+ self._ChangeValue(value)
+ else:
+ self._SetValue(value) # note: to preserve similar capability, .SetValue()
+ # does not change IsModified()
+#### dbg('queuing insertion after ._Set/ChangeValue', replace_to)
# set selection to last char replaced by paste
wx.CallAfter(self._SetInsertionPoint, replace_to)
wx.CallAfter(self._SetSelection, replace_to, replace_to)
__i=0
## CHANGELOG:
## ====================
+## Version 1.3
+## - Added support for ChangeValue() function, similar to that of the base
+## control, added in wxPython 2.7.1.1.
+##
## Version 1.2
## - Converted docstrings to reST format, added doc for ePyDoc.
## removed debugging override functions.
-'''
+"""
treemixin.py
This module provides three mixin classes that can be used with tree
all items in the tree to restore it later.
All mixin classes work with wx.TreeCtrl, wx.gizmos.TreeListCtrl,
-and wx.lib.customtree.CustomTreeCtrl. They can be used together or
+and wx.lib.customtreectrl.CustomTreeCtrl. They can be used together or
separately.
The VirtualTree and DragAndDrop mixins force the wx.TR_HIDE_ROOT style.
ExpansionState is based on code and ideas from Karsten Hilbert.
Andrea Gavana provided help with the CustomTreeCtrl integration.
-'''
+"""
-import wx, wx.lib.customtreectrl
+import wx
class TreeAPIHarmonizer(object):
- ''' This class attempts to hide the differences in API between the
- different tree controls that are part of wxPython. '''
+ """ This class attempts to hide the differences in API between the
+ different tree controls that are part of wxPython. """
def __callSuper(self, methodName, default, *args, **kwargs):
# If our super class has a method called methodName, call it,
**kwargs)
def HitTest(self, *args, **kwargs):
- ''' HitTest returns a two-tuple (item, flags) for tree controls
+ """ HitTest returns a two-tuple (item, flags) for tree controls
without columns and a three-tuple (item, flags, column) for tree
controls with columns. Our caller can indicate this method to
always return a three-tuple no matter what tree control we're mixed
in with by specifying the optional argument 'alwaysReturnColumn'
- to be True. '''
+ to be True. """
alwaysReturnColumn = kwargs.pop('alwaysReturnColumn', False)
hitTestResult = super(TreeAPIHarmonizer, self).HitTest(*args, **kwargs)
if len(hitTestResult) == 2 and alwaysReturnColumn:
class TreeHelper(object):
- ''' This class provides methods that are not part of the API of any
- tree control, but are convenient to have available. '''
+ """ This class provides methods that are not part of the API of any
+ tree control, but are convenient to have available. """
def GetItemChildren(self, item=None, recursively=False):
- ''' Return the children of item as a list. '''
+ """ Return the children of item as a list. """
if not item:
item = self.GetRootItem()
if not item:
return children
def GetIndexOfItem(self, item):
- ''' Return the index of item. '''
+ """ Return the index of item. """
parent = self.GetItemParent(item)
if parent:
parentIndices = self.GetIndexOfItem(parent)
return ()
def GetItemByIndex(self, index):
- ''' Return the item specified by index. '''
+ """ Return the item specified by index. """
item = self.GetRootItem()
for i in index:
children = self.GetItemChildren(item)
class VirtualTree(TreeAPIHarmonizer, TreeHelper):
- ''' This is a mixin class that can be used to allow for virtual tree
+ """ This is a mixin class that can be used to allow for virtual tree
controls. It can be mixed in with wx.TreeCtrl, wx.gizmos.TreeListCtrl,
wx.lib.customtree.CustomTreeCtrl.
the fourth one. A tuple with two integers, e.g. (3,0), represents a
child of a visible root item, in this case the first child of the
fourth root item.
- '''
+ """
def __init__(self, *args, **kwargs):
kwargs['style'] = kwargs.get('style', wx.TR_DEFAULT_STYLE) | \
self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed)
def OnGetChildrenCount(self, index):
- ''' This function *must* be overloaded in the derived class.
+ """ This function *must* be overloaded in the derived class.
It should return the number of child items of the item with the
provided index. If index == () it should return the number of
- root items. '''
+ root items. """
raise NotImplementedError
def OnGetItemText(self, index, column=0):
- ''' This function *must* be overloaded in the derived class. It
+ """ This function *must* be overloaded in the derived class. It
should return the string containing the text of the specified
- item. '''
+ item. """
raise NotImplementedError
def OnGetItemFont(self, index):
- ''' This function may be overloaded in the derived class. It
- should return the wx.Font to be used for the specified item. '''
+ """ This function may be overloaded in the derived class. It
+ should return the wx.Font to be used for the specified item. """
return wx.NullFont
def OnGetItemTextColour(self, index):
- ''' This function may be overloaded in the derived class. It
+ """ This function may be overloaded in the derived class. It
should return the wx.Colour to be used as text colour for the
- specified item. '''
+ specified item. """
return wx.NullColour
def OnGetItemBackgroundColour(self, index):
- ''' This function may be overloaded in the derived class. It
+ """ This function may be overloaded in the derived class. It
should return the wx.Colour to be used as background colour for
- the specified item. '''
+ the specified item. """
return wx.NullColour
def OnGetItemImage(self, index, which=wx.TreeItemIcon_Normal, column=0):
- ''' This function may be overloaded in the derived class. It
+ """ This function may be overloaded in the derived class. It
should return the index of the image to be used. Don't forget
- to associate an ImageList with the tree control. '''
+ to associate an ImageList with the tree control. """
return -1
def OnGetItemType(self, index):
- ''' This function may be overloaded in the derived class, but
+ """ This function may be overloaded in the derived class, but
that only makes sense when this class is mixed in with a tree
control that supports checkable items, i.e. CustomTreeCtrl.
This method should return whether the item is to be normal (0,
the default), a checkbox (1) or a radiobutton (2).
Note that OnGetItemChecked needs to be implemented as well; it
- should return whether the item is actually checked. '''
+ should return whether the item is actually checked. """
return 0
def OnGetItemChecked(self, index):
- ''' This function may be overloaded in the derived class, but
+ """ This function may be overloaded in the derived class, but
that only makes sense when this class is mixed in with a tree
control that supports checkable items, i.e. CustomTreeCtrl.
This method should return whether the item is to be checked.
Note that OnGetItemType should return 1 (checkbox) or 2
- (radiobutton) for this item. '''
+ (radiobutton) for this item. """
return False
def RefreshItems(self):
- ''' Redraws all visible items. '''
+ """ Redraws all visible items. """
rootItem = self.GetRootItem()
if not rootItem:
rootItem = self.AddRoot('Hidden root')
self.RefreshChildrenRecursively(rootItem)
def RefreshItem(self, index):
- ''' Redraws the item with the specified index. '''
+ """ Redraws the item with the specified index. """
try:
item = self.GetItemByIndex(index)
except IndexError:
self.DoRefreshItem(item, index, hasChildren)
def RefreshChildrenRecursively(self, item, itemIndex=None):
- ''' Refresh the children of item, reusing as much of the
- existing items in the tree as possible. '''
+ """ Refresh the children of item, reusing as much of the
+ existing items in the tree as possible. """
if itemIndex is None:
itemIndex = self.GetIndexOfItem(item)
reusableChildren = self.GetItemChildren(item)
self.Delete(child)
def RefreshItemRecursively(self, item, itemIndex):
- ''' Refresh the item and its children recursively. '''
+ """ Refresh the item and its children recursively. """
hasChildren = bool(self.OnGetChildrenCount(itemIndex))
item = self.DoRefreshItem(item, itemIndex, hasChildren)
# We need to refresh the children when the item is expanded and
self.SetItemHasChildren(item, hasChildren)
def DoRefreshItem(self, item, index, hasChildren):
- ''' Refresh one item. '''
+ """ Refresh one item. """
item = self.RefreshItemType(item, index)
self.RefreshItemText(item, index)
self.RefreshColumns(item, index)
event.Skip()
def __refreshAttribute(self, item, index, attribute, *args):
- ''' Refresh the specified attribute if necessary. '''
+ """ Refresh the specified attribute if necessary. """
value = getattr(self, 'OnGet%s'%attribute)(index, *args)
if getattr(self, 'Get%s'%attribute)(item, *args) != value:
return getattr(self, 'Set%s'%attribute)(item, value, *args)
class DragAndDrop(TreeAPIHarmonizer, TreeHelper):
- ''' This is a mixin class that can be used to easily implement
+ """ This is a mixin class that can be used to easily implement
dragging and dropping of tree items. It can be mixed in with
wx.TreeCtrl, wx.gizmos.TreeListCtrl, or wx.lib.customtree.CustomTreeCtrl.
dropped an item on top of another item. It's up to you to decide how
to handle the drop. If you are using this mixin together with the
VirtualTree mixin, it makes sense to rearrange your underlying data
- and then call RefreshItems to let the virtual tree refresh itself. '''
+ and then call RefreshItems to let the virtual tree refresh itself. """
def __init__(self, *args, **kwargs):
kwargs['style'] = kwargs.get('style', wx.TR_DEFAULT_STYLE) | \
self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag)
def OnDrop(self, dropItem, dragItem):
- ''' This function must be overloaded in the derived class.
+ """ This function must be overloaded in the derived class.
dragItem is the item being dragged by the user. dropItem is the
item dragItem is dropped upon. If the user doesn't drop dragItem
on another item, dropItem equals the (hidden) root item of the
- tree control. '''
+ tree control. """
raise NotImplementedError
def OnBeginDrag(self, event):
class ExpansionState(TreeAPIHarmonizer, TreeHelper):
- ''' This is a mixin class that can be used to save and restore
+ """ This is a mixin class that can be used to save and restore
the expansion state (i.e. which items are expanded and which items
are collapsed) of a tree. It can be mixed in with wx.TreeCtrl,
wx.gizmos.TreeListCtrl, or wx.lib.customtree.CustomTreeCtrl.
expansion doesn't depend on the position of items in the tree, but
rather on some more stable characteristic of the underlying domain
object, e.g. a social security number in case of persons or an isbn
- number in case of books. '''
+ number in case of books. """
def GetItemIdentity(self, item):
- ''' Return a hashable object that represents the identity of the
+ """ Return a hashable object that represents the identity of the
item. By default this returns the position of the item in the
tree. You may want to override this to return the item label
(if you know that labels are unique and don't change), or return
something that represents the underlying domain object, e.g.
- a database key. '''
+ a database key. """
return self.GetIndexOfItem(item)
def GetExpansionState(self):
- ''' GetExpansionState() -> list of expanded items. Expanded items
- are coded as determined by the result of GetItemIdentity(item). '''
+ """ GetExpansionState() -> list of expanded items. Expanded items
+ are coded as determined by the result of GetItemIdentity(item). """
root = self.GetRootItem()
if not root:
return []
return self.GetExpansionStateOfItem(root)
def SetExpansionState(self, listOfExpandedItems):
- ''' SetExpansionState(listOfExpandedItems). Expands all tree items
+ """ SetExpansionState(listOfExpandedItems). Expands all tree items
whose identity, as determined by GetItemIdentity(item), is present
- in the list and collapses all other tree items. '''
+ in the list and collapses all other tree items. """
root = self.GetRootItem()
if not root:
return
pre.thisown = 0
if hasattr(self, '_setOORInfo'):
self._setOORInfo(self)
- if hasattr(self, '_setCallbackInfo'):
- self._setCallbackInfo(self, self.__class__)
# Define variables for the menu items
"""
+ MENUBAR_CLASS_HEADER = """\
+class xrc%(windowName)s(wx.%(windowClass)s):
+ def __init__(self):
+ pre = get_resources().LoadMenuBar("%(windowName)s")
+ self.PostCreate(pre)
+
+ # Define variables for the menu items
+"""
+
CREATE_MENUITEM_VAR = """\
self.%(widgetName)s = self.FindItemById(xrc.XRCID(\"%(widgetName)s\"))
"""
windowClass = re.sub("^wx", "", windowClass)
windowName = topWindow.getAttribute("name")
- if windowClass in ["Menu", "MenuItem"]:
+ if windowClass in ["MenuBar"]:
+ outputList.append(self.templates.MENUBAR_CLASS_HEADER % locals())
+ elif windowClass in ["Menu"]:
outputList.append(self.templates.MENU_CLASS_HEADER % locals())
else:
outputList.append(self.templates.CLASS_HEADER % locals())
self.sizerType = "vertical"
def AddChild(self, child):
- wx.PyPanel.base_AddChild(self, child)
-
+ if wx.VERSION < (2,8):
+ wx.PyPanel.base_AddChild(self, child)
+ else:
+ wx.PyPanel.AddChild(self, child)
+
+ # Note: The wx.LogNull is used here to suppress a log message
+ # on wxMSW that happens because when AddChild is called the
+ # widget's hwnd hasn't been set yet, so the GetWindowRect that
+ # happens as a result of sizer.Add (in wxSizerItem::SetWindow)
+ # fails. A better fix would be to defer this code somehow
+ # until after the child widget is fully constructed.
sizer = self.GetSizer()
+ nolog = wx.LogNull()
item = sizer.Add(child)
+ del nolog
item.SetUserData({"HGrow":0, "VGrow":0})
# Note: One problem is that the child class given to AddChild