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
)
217 shapeList
= canvas
.GetDiagram().GetShapeList()
222 # If we unselect it now then some of the objects in
223 # shapeList will become invalid (the control points are
224 # shapes too!) and bad things will happen...
227 shape
.Select(True, dc
)
235 self
.UpdateStatusBar(shape
)
238 def OnEndDragLeft(self
, x
, y
, keys
=0, attachment
=0):
239 shape
= self
.GetShape()
240 ogl
.ShapeEvtHandler
.OnEndDragLeft(self
, x
, y
, keys
, attachment
)
242 if not shape
.Selected():
243 self
.OnLeftClick(x
, y
, keys
, attachment
)
245 self
.UpdateStatusBar(shape
)
248 def OnSizingEndDragLeft(self
, pt
, x
, y
, keys
, attch
):
249 ogl
.ShapeEvtHandler
.OnSizingEndDragLeft(self
, pt
, x
, y
, keys
, attch
)
250 self
.UpdateStatusBar(self
.GetShape())
253 def OnMovePost(self
, dc
, x
, y
, oldX
, oldY
, display
):
254 ogl
.ShapeEvtHandler
.OnMovePost(self
, dc
, x
, y
, oldX
, oldY
, display
)
255 self
.UpdateStatusBar(self
.GetShape())
258 def OnRightClick(self
, *dontcare
):
259 self
.log
.WriteText("%s\n" % self
.GetShape())
262 #----------------------------------------------------------------------
264 class TestWindow(ogl
.ShapeCanvas
):
265 def __init__(self
, parent
, log
, frame
):
266 ogl
.ShapeCanvas
.__init
__(self
, parent
)
270 self
.SetScrollbars(20, 20, maxWidth
/20, maxHeight
/20)
274 self
.SetBackgroundColour("LIGHT BLUE") #wx.WHITE)
275 self
.diagram
= ogl
.Diagram()
276 self
.SetDiagram(self
.diagram
)
277 self
.diagram
.SetCanvas(self
)
281 rRectBrush
= wx
.Brush("MEDIUM TURQUOISE", wx
.SOLID
)
282 dsBrush
= wx
.Brush("WHEAT", wx
.SOLID
)
285 CompositeDivisionShape(self
),
286 270, 310, wx
.BLACK_PEN
, wx
.BLUE_BRUSH
, "Division"
290 CompositeShape(self
),
291 100, 260, wx
.BLACK_PEN
, wx
.RED_BRUSH
, "Composite"
296 75, 110, wx
.Pen(wx
.BLUE
, 3), wx
.GREEN_BRUSH
, "Circle"
300 ogl
.TextShape(120, 45),
301 160, 35, wx
.GREEN_PEN
, wx
.LIGHT_GREY_BRUSH
, "OGL is now a\npure Python lib!"
305 ogl
.RectangleShape(85, 50),
306 305, 60, wx
.BLACK_PEN
, wx
.LIGHT_GREY_BRUSH
, "Rectangle"
311 500, 80, wx
.BLACK_PEN
, wx
.BLACK_BRUSH
, "DrawnShape"
314 ds
= self
.MyAddShape(
315 DividedShape(140, 150, self
),
316 520, 265, wx
.BLACK_PEN
, dsBrush
, ''
320 DiamondShape(90, 90),
321 355, 260, wx
.Pen(wx
.BLUE
, 3, wx
.DOT
), wx
.RED_BRUSH
, "Polygon"
325 RoundedRectangleShape(95, 70),
326 345, 145, wx
.Pen(wx
.RED
, 2), rRectBrush
, "Rounded Rect"
329 bmp
= images
.getTest2Bitmap()
330 mask
= wx
.Mask(bmp
, wx
.BLUE
)
333 s
= ogl
.BitmapShape()
335 self
.MyAddShape(s
, 225, 130, None, None, "Bitmap")
337 dc
= wx
.ClientDC(self
)
340 for x
in range(len(self
.shapes
)):
341 fromShape
= self
.shapes
[x
]
342 if x
+1 == len(self
.shapes
):
343 toShape
= self
.shapes
[0]
345 toShape
= self
.shapes
[x
+1]
347 line
= ogl
.LineShape()
349 line
.SetPen(wx
.BLACK_PEN
)
350 line
.SetBrush(wx
.BLACK_BRUSH
)
351 line
.AddArrow(ogl
.ARROW_ARROW
)
352 line
.MakeLineControlPoints(2)
353 fromShape
.AddLine(line
, toShape
)
354 self
.diagram
.AddShape(line
)
358 def MyAddShape(self
, shape
, x
, y
, pen
, brush
, text
):
359 # Composites have to be moved for all children to get in place
360 if isinstance(shape
, ogl
.CompositeShape
):
361 dc
= wx
.ClientDC(self
)
365 shape
.SetDraggable(True, True)
366 shape
.SetCanvas(self
)
369 if pen
: shape
.SetPen(pen
)
370 if brush
: shape
.SetBrush(brush
)
372 for line
in text
.split('\n'):
374 #shape.SetShadowMode(ogl.SHADOW_RIGHT)
375 self
.diagram
.AddShape(shape
)
378 evthandler
= MyEvtHandler(self
.log
, self
.frame
)
379 evthandler
.SetShape(shape
)
380 evthandler
.SetPreviousHandler(shape
.GetEventHandler())
381 shape
.SetEventHandler(evthandler
)
383 self
.shapes
.append(shape
)
387 def OnBeginDragLeft(self
, x
, y
, keys
):
388 self
.log
.write("OnBeginDragLeft: %s, %s, %s\n" % (x
, y
, keys
))
390 def OnEndDragLeft(self
, x
, y
, keys
):
391 self
.log
.write("OnEndDragLeft: %s, %s, %s\n" % (x
, y
, keys
))
394 #----------------------------------------------------------------------
396 def runTest(frame
, nb
, log
):
397 # This creates some pens and brushes that the OGL library uses.
398 # It should be called after the app object has been created, but
399 # before OGL is used.
402 win
= TestWindow(nb
, log
, frame
)
405 #----------------------------------------------------------------------
408 overview
= """<html><body>
409 <h2>Object Graphics Library</h2>
411 The Object Graphics Library is a library supporting the creation and
412 manipulation of simple and complex graphic images on a canvas.
414 <p>The OGL library was originally written in C++ and provided to
415 wxPython via an extension module wrapper as is most of the rest of
416 wxPython. The code has now been ported to Python (with many thanks to
417 Pierre Hjälm!) in order to make it be more easily maintainable and
418 less likely to get rusty because nobody cares about the C++ lib any
421 <p>The Python version should be mostly drop-in compatible with the
422 wrapped C++ version, except for the location of the package
423 (wx.lib.ogl instead of wx.ogl) and that the base class methods are
424 called the normal Python way (superclass.Method(self, ...)) instead of the
425 hacky way that had to be done to support overloaded methods with the
426 old SWIG (self.base_Method(...))
431 if __name__
== '__main__':
434 run
.main(['', os
.path
.basename(sys
.argv
[0])] + sys
.argv
[1:])