1 # -*- coding: iso-8859-1 -*- 
   2 # 11/20/2003 - Jeff Grimmett (grimmtooth@softhome.net) 
   4 # o Updated for wx namespace 
   6 # 20040508 - Pierre Hjälm 
   8 # o Changed to use the python version of OGL 
   9 # o Added TextShape, CompositeShape and CompositeShape with divisions 
  11 # 20040830 - Pierre Hjälm 
  17 import wx
.lib
.ogl 
as ogl
 
  21 #---------------------------------------------------------------------- 
  23 class DrawnShape(ogl
.DrawnShape
): 
  25         ogl
.DrawnShape
.__init
__(self
) 
  27         self
.SetDrawnBrush(wx
.WHITE_BRUSH
) 
  28         self
.SetDrawnPen(wx
.BLACK_PEN
) 
  29         self
.DrawArc((0, -10), (30, 0), (-30, 0)) 
  31         self
.SetDrawnPen(wx
.Pen("#ff8030")) 
  32         self
.DrawLine((-30, 5), (30, 5)) 
  34         self
.SetDrawnPen(wx
.Pen("#00ee10")) 
  35         self
.DrawRoundedRectangle((-20, 10, 40, 10), 5) 
  37         self
.SetDrawnPen(wx
.Pen("#9090f0")) 
  38         self
.DrawEllipse((-30, 25, 60, 20)) 
  40         self
.SetDrawnTextColour(wx
.BLACK
) 
  41         self
.SetDrawnFont(wx
.Font(8, wx
.SWISS
, wx
.NORMAL
, wx
.NORMAL
)) 
  42         self
.DrawText("DrawText", (-26, 28)) 
  44         self
.SetDrawnBrush(wx
.GREEN_BRUSH
) 
  45         self
.DrawPolygon([(-100, 5), (-45, 30), (-35, 20), (-30, 5)]) 
  47         self
.SetDrawnPen(wx
.BLACK_PEN
) 
  48         self
.DrawLines([(30, -45), (40, -45), (40 ,45), (30, 45)]) 
  50         # Make sure to call CalculateSize when all drawing is done 
  53 #---------------------------------------------------------------------- 
  55 class DiamondShape(ogl
.PolygonShape
): 
  56     def __init__(self
, w
=0.0, h
=0.0): 
  57         ogl
.PolygonShape
.__init
__(self
) 
  63         points 
= [ (0.0,    -h
/2.0), 
  72 #---------------------------------------------------------------------- 
  74 class RoundedRectangleShape(ogl
.RectangleShape
): 
  75     def __init__(self
, w
=0.0, h
=0.0): 
  76         ogl
.RectangleShape
.__init
__(self
, w
, h
) 
  77         self
.SetCornerRadius(-0.3) 
  80 #---------------------------------------------------------------------- 
  82 class CompositeDivisionShape(ogl
.CompositeShape
): 
  83     def __init__(self
, canvas
): 
  84         ogl
.CompositeShape
.__init
__(self
) 
  86         self
.SetCanvas(canvas
) 
  88         # create a division in the composite 
  91         # add a shape to the original division 
  92         shape2 
= ogl
.RectangleShape(40, 60) 
  93         self
.GetDivisions()[0].AddChild(shape2
) 
  95         # now divide the division so we get 2 
  96         self
.GetDivisions()[0].Divide(wx
.HORIZONTAL
) 
  98         # and add a shape to the second division (and move it to the 
  99         # centre of the division) 
 100         shape3 
= ogl
.CircleShape(40) 
 101         shape3
.SetBrush(wx
.CYAN_BRUSH
) 
 102         self
.GetDivisions()[1].AddChild(shape3
) 
 103         shape3
.SetX(self
.GetDivisions()[1].GetX()) 
 105         for division 
in self
.GetDivisions(): 
 106             division
.SetSensitivityFilter(0) 
 108 #---------------------------------------------------------------------- 
 110 class CompositeShape(ogl
.CompositeShape
): 
 111     def __init__(self
, canvas
): 
 112         ogl
.CompositeShape
.__init
__(self
) 
 114         self
.SetCanvas(canvas
) 
 116         constraining_shape 
= ogl
.RectangleShape(120, 100) 
 117         constrained_shape1 
= ogl
.CircleShape(50) 
 118         constrained_shape2 
= ogl
.RectangleShape(80, 20) 
 120         constraining_shape
.SetBrush(wx
.BLUE_BRUSH
) 
 121         constrained_shape2
.SetBrush(wx
.RED_BRUSH
) 
 123         self
.AddChild(constraining_shape
) 
 124         self
.AddChild(constrained_shape1
) 
 125         self
.AddChild(constrained_shape2
) 
 127         constraint 
= ogl
.Constraint(ogl
.CONSTRAINT_MIDALIGNED_BOTTOM
, constraining_shape
, [constrained_shape1
, constrained_shape2
]) 
 128         self
.AddConstraint(constraint
) 
 131         # If we don't do this, the shapes will be able to move on their 
 132         # own, instead of moving the composite 
 133         constraining_shape
.SetDraggable(False) 
 134         constrained_shape1
.SetDraggable(False) 
 135         constrained_shape2
.SetDraggable(False) 
 137         # If we don't do this the shape will take all left-clicks for itself 
 138         constraining_shape
.SetSensitivityFilter(0) 
 141 #---------------------------------------------------------------------- 
 143 class DividedShape(ogl
.DividedShape
): 
 144     def __init__(self
, width
, height
, canvas
): 
 145         ogl
.DividedShape
.__init
__(self
, width
, height
) 
 147         region1 
= ogl
.ShapeRegion() 
 148         region1
.SetText('DividedShape') 
 149         region1
.SetProportions(0.0, 0.2) 
 150         region1
.SetFormatMode(ogl
.FORMAT_CENTRE_HORIZ
) 
 151         self
.AddRegion(region1
) 
 153         region2 
= ogl
.ShapeRegion() 
 154         region2
.SetText('This is Region number two.') 
 155         region2
.SetProportions(0.0, 0.3) 
 156         region2
.SetFormatMode(ogl
.FORMAT_CENTRE_HORIZ|ogl
.FORMAT_CENTRE_VERT
) 
 157         self
.AddRegion(region2
) 
 159         region3 
= ogl
.ShapeRegion() 
 160         region3
.SetText('Region 3\nwith embedded\nline breaks') 
 161         region3
.SetProportions(0.0, 0.5) 
 162         region3
.SetFormatMode(ogl
.FORMAT_NONE
) 
 163         self
.AddRegion(region3
) 
 165         self
.SetRegionSizes() 
 166         self
.ReformatRegions(canvas
) 
 169     def ReformatRegions(self
, canvas
=None): 
 173             canvas 
= self
.GetCanvas() 
 175         dc 
= wx
.ClientDC(canvas
)  # used for measuring 
 177         for region 
in self
.GetRegions(): 
 178             text 
= region
.GetText() 
 179             self
.FormatText(dc
, text
, rnum
) 
 183     def OnSizingEndDragLeft(self
, pt
, x
, y
, keys
, attch
): 
 185         ogl
.DividedShape
.OnSizingEndDragLeft(self
, pt
, x
, y
, keys
, attch
) 
 186         self
.SetRegionSizes() 
 187         self
.ReformatRegions() 
 188         self
.GetCanvas().Refresh() 
 191 #---------------------------------------------------------------------- 
 193 class MyEvtHandler(ogl
.ShapeEvtHandler
): 
 194     def __init__(self
, log
, frame
): 
 195         ogl
.ShapeEvtHandler
.__init
__(self
) 
 197         self
.statbarFrame 
= frame
 
 199     def UpdateStatusBar(self
, shape
): 
 200         x
, y 
= shape
.GetX(), shape
.GetY() 
 201         width
, height 
= shape
.GetBoundingBoxMax() 
 202         self
.statbarFrame
.SetStatusText("Pos: (%d, %d)  Size: (%d, %d)" % 
 203                                         (x
, y
, width
, height
)) 
 206     def OnLeftClick(self
, x
, y
, keys
=0, attachment
=0): 
 207         shape 
= self
.GetShape() 
 208         canvas 
= shape
.GetCanvas() 
 209         dc 
= wx
.ClientDC(canvas
) 
 213             shape
.Select(False, dc
) 
 215             canvas
.Refresh(False) 
 218             shapeList 
= canvas
.GetDiagram().GetShapeList() 
 223                     # If we unselect it now then some of the objects in 
 224                     # shapeList will become invalid (the control points are 
 225                     # shapes too!) and bad things will happen... 
 228             shape
.Select(True, dc
) 
 235                 canvas
.Refresh(False) 
 237         self
.UpdateStatusBar(shape
) 
 240     def OnEndDragLeft(self
, x
, y
, keys
=0, attachment
=0): 
 241         shape 
= self
.GetShape() 
 242         ogl
.ShapeEvtHandler
.OnEndDragLeft(self
, x
, y
, keys
, attachment
) 
 244         if not shape
.Selected(): 
 245             self
.OnLeftClick(x
, y
, keys
, attachment
) 
 247         self
.UpdateStatusBar(shape
) 
 250     def OnSizingEndDragLeft(self
, pt
, x
, y
, keys
, attch
): 
 251         ogl
.ShapeEvtHandler
.OnSizingEndDragLeft(self
, pt
, x
, y
, keys
, attch
) 
 252         self
.UpdateStatusBar(self
.GetShape()) 
 255     def OnMovePost(self
, dc
, x
, y
, oldX
, oldY
, display
): 
 256         shape 
= self
.GetShape() 
 257         ogl
.ShapeEvtHandler
.OnMovePost(self
, dc
, x
, y
, oldX
, oldY
, display
) 
 258         self
.UpdateStatusBar(shape
) 
 259         if "wxMac" in wx
.PlatformInfo
: 
 260             shape
.GetCanvas().Refresh(False) 
 262     def OnRightClick(self
, *dontcare
): 
 263         self
.log
.WriteText("%s\n" % self
.GetShape()) 
 266 #---------------------------------------------------------------------- 
 268 class TestWindow(ogl
.ShapeCanvas
): 
 269     def __init__(self
, parent
, log
, frame
): 
 270         ogl
.ShapeCanvas
.__init
__(self
, parent
) 
 274         self
.SetScrollbars(20, 20, maxWidth
/20, maxHeight
/20) 
 278         self
.SetBackgroundColour("LIGHT BLUE") #wx.WHITE) 
 279         self
.diagram 
= ogl
.Diagram() 
 280         self
.SetDiagram(self
.diagram
) 
 281         self
.diagram
.SetCanvas(self
) 
 285         rRectBrush 
= wx
.Brush("MEDIUM TURQUOISE", wx
.SOLID
) 
 286         dsBrush 
= wx
.Brush("WHEAT", wx
.SOLID
) 
 289             CompositeDivisionShape(self
),  
 290             270, 310, wx
.BLACK_PEN
, wx
.BLUE_BRUSH
, "Division" 
 294             CompositeShape(self
),  
 295             100, 260, wx
.BLACK_PEN
, wx
.RED_BRUSH
, "Composite" 
 300             75, 110, wx
.Pen(wx
.BLUE
, 3), wx
.GREEN_BRUSH
, "Circle" 
 304             ogl
.TextShape(120, 45),  
 305             160, 35, wx
.GREEN_PEN
, wx
.LIGHT_GREY_BRUSH
, "OGL is now a\npure Python lib!" 
 309             ogl
.RectangleShape(85, 50),  
 310             305, 60, wx
.BLACK_PEN
, wx
.LIGHT_GREY_BRUSH
, "Rectangle" 
 315             500, 80, wx
.BLACK_PEN
, wx
.BLACK_BRUSH
, "DrawnShape" 
 318         ds 
= self
.MyAddShape( 
 319             DividedShape(140, 150, self
),  
 320             520, 265, wx
.BLACK_PEN
, dsBrush
, '' 
 324             DiamondShape(90, 90),  
 325             355, 260, wx
.Pen(wx
.BLUE
, 3, wx
.DOT
), wx
.RED_BRUSH
, "Polygon" 
 329             RoundedRectangleShape(95, 70),  
 330             345, 145, wx
.Pen(wx
.RED
, 2), rRectBrush
, "Rounded Rect" 
 333         bmp 
= images
.getTest2Bitmap() 
 334         mask 
= wx
.Mask(bmp
, wx
.BLUE
) 
 337         s 
= ogl
.BitmapShape() 
 339         self
.MyAddShape(s
, 225, 130, None, None, "Bitmap") 
 341         #dc = wx.ClientDC(self) 
 344         for x 
in range(len(self
.shapes
)): 
 345             fromShape 
= self
.shapes
[x
] 
 346             if x
+1 == len(self
.shapes
): 
 347                 toShape 
= self
.shapes
[0] 
 349                 toShape 
= self
.shapes
[x
+1] 
 351             line 
= ogl
.LineShape() 
 353             line
.SetPen(wx
.BLACK_PEN
) 
 354             line
.SetBrush(wx
.BLACK_BRUSH
) 
 355             line
.AddArrow(ogl
.ARROW_ARROW
) 
 356             line
.MakeLineControlPoints(2) 
 357             fromShape
.AddLine(line
, toShape
) 
 358             self
.diagram
.AddShape(line
) 
 362     def MyAddShape(self
, shape
, x
, y
, pen
, brush
, text
): 
 363         # Composites have to be moved for all children to get in place 
 364         if isinstance(shape
, ogl
.CompositeShape
): 
 365             dc 
= wx
.ClientDC(self
) 
 369             shape
.SetDraggable(True, True) 
 370         shape
.SetCanvas(self
) 
 373         if pen
:    shape
.SetPen(pen
) 
 374         if brush
:  shape
.SetBrush(brush
) 
 376             for line 
in text
.split('\n'): 
 378         #shape.SetShadowMode(ogl.SHADOW_RIGHT) 
 379         self
.diagram
.AddShape(shape
) 
 382         evthandler 
= MyEvtHandler(self
.log
, self
.frame
) 
 383         evthandler
.SetShape(shape
) 
 384         evthandler
.SetPreviousHandler(shape
.GetEventHandler()) 
 385         shape
.SetEventHandler(evthandler
) 
 387         self
.shapes
.append(shape
) 
 391     def OnBeginDragLeft(self
, x
, y
, keys
): 
 392         self
.log
.write("OnBeginDragLeft: %s, %s, %s\n" % (x
, y
, keys
)) 
 394     def OnEndDragLeft(self
, x
, y
, keys
): 
 395         self
.log
.write("OnEndDragLeft: %s, %s, %s\n" % (x
, y
, keys
)) 
 398 #---------------------------------------------------------------------- 
 400 def runTest(frame
, nb
, log
): 
 401     # This creates some pens and brushes that the OGL library uses. 
 402     # It should be called after the app object has been created, but 
 403     # before OGL is used. 
 406     win 
= TestWindow(nb
, log
, frame
) 
 409 #---------------------------------------------------------------------- 
 412 overview 
= """<html><body> 
 413 <h2>Object Graphics Library</h2> 
 415 The Object Graphics Library is a library supporting the creation and 
 416 manipulation of simple and complex graphic images on a canvas. 
 418 <p>The OGL library was originally written in C++ and provided to 
 419 wxPython via an extension module wrapper as is most of the rest of 
 420 wxPython.  The code has now been ported to Python (with many thanks to 
 421 Pierre Hjälm!) in order to make it be more easily maintainable and 
 422 less likely to get rusty because nobody cares about the C++ lib any 
 427 if __name__ 
== '__main__': 
 430     run
.main(['', os
.path
.basename(sys
.argv
[0])] + sys
.argv
[1:])