1 #!/usr/bin/env python2.2
3 from Numeric
import array
,Float
,cos
,pi
,sum,minimum
,maximum
6 from wxPython
.wx
import *
10 ID_ZOOM_IN_BUTTON
= wxNewId()
11 ID_ZOOM_OUT_BUTTON
= wxNewId()
12 ID_ZOOM_TO_FIT_BUTTON
= wxNewId()
13 ID_MOVE_MODE_BUTTON
= wxNewId()
14 ID_TEST_BUTTON
= wxNewId()
15 ID_ABOUT_MENU
= wxNewId()
16 ID_EXIT_MENU
= wxNewId()
17 ID_ZOOM_TO_FIT_MENU
= wxNewId()
18 ID_DRAWTEST_MENU
= wxNewId()
19 ID_DRAWMAP_MENU
= wxNewId()
20 ID_CLEAR_MENU
= wxNewId()
25 ### These are some functions for bitmaps of icons.
29 return cPickle
.loads(zlib
.decompress(
30 'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
31 \x01\xc8S\xb6t\x06A(\x1f\x0b\xa0\xa9\x8c\x9e\x1e6\x19\xa0\xa8\x1e\x88\xd4C\
32 \x97\xd1\x83\xe8\x80 \x9c2zh\xa6\xc1\x11X\n\xab\x8c\x02\x8a\x0cD!\x92\x12\
33 \x98\x8c\x1e\x8a\x8b\xd1d\x14\xf4\x90%\x90LC\xf6\xbf\x1e\xba\xab\x91%\xd0\
34 \xdc\x86C\x06\xd9m\xe8!\xaa\x87S\x86\x1a1\xa7\x07\x00v\x0f[\x17' ))
37 return wxBitmapFromXPMData(GetHandData())
39 #----------------------------------------------------------------------
41 return cPickle
.loads(zlib
.decompress(
42 'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
43 \x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=l2`\r\
44 \xe82HF\xe9a\xc8\xe8\xe9A\x9c@\x8a\x0c\x0e\xd3p\xbb\x00\x8f\xab\xe1>\xd5\xd3\
45 \xc3\x15:P)l!\n\x91\xc2\x1a\xd6`)\xec\xb1\x00\x92\xc2\x11?\xb8e\x88\x8fSt\
46 \x19=\x00\x82\x16[\xf7' ))
49 return wxBitmapFromXPMData(GetPlusData())
51 #----------------------------------------------------------------------
53 return cPickle
.loads(zlib
.decompress(
54 'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
55 \x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=\xa2e\
56 \x10\x16@\x99\xc82zz\x10\'\x90"\x83\xc34r\xdc\x86\xf0\xa9\x9e\x1e\xae\xd0\
57 \x81Ja\x0bQ\x88\x14\xd6\xb0\x06Ka\x8f\x05\x90\x14\x8e\xf8\xc1-C|\x9c\xa2\xcb\
58 \xe8\x01\x00\xed\x0f[\x87' ))
61 return wxBitmapFromXPMData(GetMinusData())
63 ## This is a bunch of stuff for implimenting interactive use: catching
64 ## when objects are clicked on by the mouse, etc. I've made a start, so if
65 ## you are interesed in making that work, let me know, I may have gotten
68 #### I probably want a full set of events someday:
69 ## #### mouse over, right click, left click mouse up etc, etc.
70 ## ##FLOATCANVAS_EVENT_LEFT_DOWN = wxNewEventType()
71 ## ##FLOATCANVAS_EVENT_LEFT_UP = wxNewEventType()
72 ## ##FLOATCANVAS_EVENT_RIGHT_DOWN = wxNewEventType()
73 ## ##FLOATCANVAS_EVENT_RIGHT_UP = wxNewEventType()
74 ## ##FLOATCANVAS_EVENT_MOUSE_OVER = wxNewEventType()
76 ##WXFLOATCANVASEVENT = wxNewEventType()
78 ##def EVT_WXFLOATCANVASEVENT( window, function ):
80 ## """Your documentation here"""
82 ## window.Connect( -1, -1, WXFLOATCANVASEVENT, function )
84 ##class wxFloatCanvasObjectEvent(wxPyCommandEvent):
85 ## def __init__(self, WindowID,Object):
86 ## wxPyCommandEvent.__init__(self, WXFLOATCANVASEVENT, WindowID)
87 ## self.Object = Object
90 ## self.__class__( self.GetId() )
92 ##class ColorGenerator:
94 ## """ An instance of this class generates a unique color each time
95 ## GetNextColor() is called. Someday I will use a proper Python
96 ## generator for this class.
98 ## The point of this generator is for the hit-test bitmap, each object
99 ## needs to be a unique color. Also, each system can be running a
100 ## different number of colors, and it doesn't appear to be possible to
101 ## have a wxMemDC with a different colordepth as the screen so this
102 ## generates colors far enough apart that they can be distinguished on
103 ## a 16bit screen. Anything less than 16bits won't work.
106 ## def __init__(self,depth = 16):
115 ## raise "ColorGenerator does not work with depth = %s"%depth
117 ## def GetNextColor(self):
119 ## ##r,g,b = self.r,self.g,self.b
128 ## ## fixme: this should be a derived exception
129 ## raise "Too many objects for HitTest"
130 ## return (self.r,self.g,self.b)
135 This is the base class for all the objects that can be drawn.
137 each object has the following properties; (incomplete)
139 BoundingBox : is of the form: array((min_x,min_y),(max_x,max_y))
145 def __init__(self
,Foreground
= 0):
146 self
.Foreground
= Foreground
150 # I pre-define all these as class variables to provide an easier
151 # interface, and perhaps speed things up by caching all the Pens
152 # and Brushes, although that may not help, as I think wx now
153 # does that on it's own. Send me a note if you know!
156 ( None,"Transparent") : wxTRANSPARENT_BRUSH
,
157 ("Blue","Solid") : wxBLUE_BRUSH
,
158 ("Green","Solid") : wxGREEN_BRUSH
,
159 ("White","Solid") : wxWHITE_BRUSH
,
160 ("Black","Solid") : wxBLACK_BRUSH
,
161 ("Grey","Solid") : wxGREY_BRUSH
,
162 ("MediumGrey","Solid") : wxMEDIUM_GREY_BRUSH
,
163 ("LightGrey","Solid") : wxLIGHT_GREY_BRUSH
,
164 ("Cyan","Solid") : wxCYAN_BRUSH
,
165 ("Red","Solid") : wxRED_BRUSH
168 (None,"Transparent",1) : wxTRANSPARENT_PEN
,
169 ("Green","Solid",1) : wxGREEN_PEN
,
170 ("White","Solid",1) : wxWHITE_PEN
,
171 ("Black","Solid",1) : wxBLACK_PEN
,
172 ("Grey","Solid",1) : wxGREY_PEN
,
173 ("MediumGrey","Solid",1) : wxMEDIUM_GREY_PEN
,
174 ("LightGrey","Solid",1) : wxLIGHT_GREY_PEN
,
175 ("Cyan","Solid",1) : wxCYAN_PEN
,
176 ("Red","Solid",1) : wxRED_PEN
180 "Transparent" : wxTRANSPARENT
,
182 "BiDiagonalHatch": wxBDIAGONAL_HATCH
,
183 "CrossDiagHatch" : wxCROSSDIAG_HATCH
,
184 "FDiagonal_Hatch": wxFDIAGONAL_HATCH
,
185 "CrossHatch" : wxCROSS_HATCH
,
186 "HorizontalHatch": wxHORIZONTAL_HATCH
,
187 "VerticalHatch" : wxVERTICAL_HATCH
192 "Transparent": wxTRANSPARENT
,
194 "LongDash" : wxLONG_DASH
,
195 "ShortDash" : wxSHORT_DASH
,
196 "DotDash" : wxDOT_DASH
,
199 def SetBrush(self
,FillColor
,FillStyle
):
200 if FillColor
is None or FillStyle
is None:
201 self
.Brush
= wxTRANSPARENT_BRUSH
202 self
.FillStyle
= "Transparent"
204 if not self
.BrushList
.has_key((FillColor
,FillStyle
)):
205 self
.BrushList
[(FillColor
,FillStyle
)] = wxBrush(FillColor
,self
.FillStyleList
[FillStyle
])
206 self
.Brush
= self
.BrushList
[(FillColor
,FillStyle
)]
208 def SetPen(self
,LineColor
,LineStyle
,LineWidth
):
209 if LineColor
is None or LineStyle
is None:
210 self
.Pen
= wxTRANSPARENT_PEN
211 self
.LineStyle
= 'Transparent'
212 if not self
.PenList
.has_key((LineColor
,LineStyle
,LineWidth
)):
213 self
.PenList
[(LineColor
,LineStyle
,LineWidth
)] = wxPen(LineColor
,LineWidth
,self
.LineStyleList
[LineStyle
])
214 self
.Pen
= self
.PenList
[(LineColor
,LineStyle
,LineWidth
)]
216 def SetPens(self
,LineColors
,LineStyles
,LineWidths
):
218 This method used when an object could have a list of pens, rather than just one
219 It is used for LineSet, and perhaps others in the future.
221 fixme: this is really kludgy, there has got to be a better way!
226 if type(LineColors
) == types
.ListType
:
227 length
= len(LineColors
)
229 LineColors
= [LineColors
]
231 if type(LineStyles
) == types
.ListType
:
232 length
= len(LineStyles
)
234 LineStyles
= [LineStyles
]
236 if type(LineWidths
) == types
.ListType
:
237 length
= len(LineWidths
)
239 LineWidths
= [LineWidths
]
242 if len(LineColors
) == 1:
243 LineColors
= LineColors
*length
244 if len(LineStyles
) == 1:
245 LineStyles
= LineStyles
*length
246 if len(LineWidths
) == 1:
247 LineWidths
= LineWidths
*length
250 for (LineColor
,LineStyle
,LineWidth
) in zip(LineColors
,LineStyles
,LineWidths
):
251 if LineColor
is None or LineStyle
is None:
252 self
.Pens
.append(wxTRANSPARENT_PEN
)
253 # what's this for?> self.LineStyle = 'Transparent'
254 if not self
.PenList
.has_key((LineColor
,LineStyle
,LineWidth
)):
255 Pen
= wxPen(LineColor
,LineWidth
,self
.LineStyleList
[LineStyle
])
256 self
.Pens
.append(Pen
)
258 self
.Pens
.append(self
.PenList
[(LineColor
,LineStyle
,LineWidth
)])
260 self
.Pens
= self
.Pens
[0]
262 def PutInBackground(self
):
263 if self
._Canvas
and self
.Foreground
:
264 self
._Canvas
._TopDrawList
.remove(self
)
265 self
._Canvas
._DrawList
.append(self
)
266 self
._Canvas
._BackgroundDirty
= 1
269 def PutInForeground(self
):
270 if self
._Canvas
and (not self
.Foreground
):
271 self
._Canvas
._TopDrawList
.append(self
)
272 self
._Canvas
._DrawList
.remove(self
)
273 self
._Canvas
._BackgroundDirty
= 1
277 class Polygon(draw_object
):
281 The Polygon class takes a list of 2-tuples, or a NX2 NumPy array of
282 point coordinates. so that Points[N][0] is the x-coordinate of
283 point N and Points[N][1] is the y-coordinate or Points[N,0] is the
284 x-coordinate of point N and Points[N,1] is the y-coordinate for
288 def __init__(self
,Points
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
= 0):
289 draw_object
.__init
__(self
,Foreground
)
290 self
.Points
= array(Points
,Float
)
291 self
.BoundingBox
= array(((min(self
.Points
[:,0]),min(self
.Points
[:,1])),(max(self
.Points
[:,0]),max(self
.Points
[:,1]))),Float
)
293 self
.LineColor
= LineColor
294 self
.LineStyle
= LineStyle
295 self
.LineWidth
= LineWidth
296 self
.FillColor
= FillColor
297 self
.FillStyle
= FillStyle
299 self
.SetPen(LineColor
,LineStyle
,LineWidth
)
300 self
.SetBrush(FillColor
,FillStyle
)
303 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
304 Points
= WorldToPixel(self
.Points
)
306 dc
.SetBrush(self
.Brush
)
307 #dc.DrawPolygon(map(lambda x: (x[0],x[1]), Points.tolist()))
308 dc
.DrawPolygon(Points
)
310 class PolygonSet(draw_object
):
312 The PolygonSet class takes a Geometry.Polygon object.
313 so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
315 it creates a set of line segments, from (x1,y1) to (x2,y2)
319 def __init__(self
,PolySet
,LineColors
,LineStyles
,LineWidths
,FillColors
,FillStyles
,Foreground
= 0):
320 draw_object
.__init
__(self
, Foreground
)
322 ##fixme: there should be some error checking for everything being the right length.
325 self
.Points
= array(Points
,Float
)
326 self
.BoundingBox
= array(((min(self
.Points
[:,0]),min(self
.Points
[:,1])),(max(self
.Points
[:,0]),max(self
.Points
[:,1]))),Float
)
328 self
.LineColors
= LineColors
329 self
.LineStyles
= LineStyles
330 self
.LineWidths
= LineWidths
331 self
.FillColors
= FillColors
332 self
.FillStyles
= FillStyles
334 self
.SetPens(LineColors
,LineStyles
,LineWidths
)
336 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
337 Points
= WorldToPixel(self
.Points
)
338 Points
.shape
= (-1,4)
339 dc
.DrawLineList(Points
,self
.Pens
)
342 class Line(draw_object
):
344 The Line class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
345 so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
346 or Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
348 It will draw a straight line if there are two points, and a polyline if there are more than two.
351 def __init__(self
,Points
,LineColor
,LineStyle
,LineWidth
,Foreground
= 0):
352 draw_object
.__init
__(self
, Foreground
)
354 self
.Points
= array(Points
,Float
)
355 self
.BoundingBox
= array(((min(self
.Points
[:,0]),min(self
.Points
[:,1])),(max(self
.Points
[:,0]),max(self
.Points
[:,1]))),Float
)
357 self
.LineColor
= LineColor
358 self
.LineStyle
= LineStyle
359 self
.LineWidth
= LineWidth
361 self
.SetPen(LineColor
,LineStyle
,LineWidth
)
363 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
364 Points
= WorldToPixel(self
.Points
)
366 #dc.DrawLines(map(lambda x: (x[0],x[1]), Points.tolist()))
370 class LineSet(draw_object
):
372 The LineSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
373 so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
375 it creates a set of line segments, from (x1,y1) to (x2,y2)
379 def __init__(self
,Points
,LineColors
,LineStyles
,LineWidths
,Foreground
= 0):
380 draw_object
.__init
__(self
, Foreground
)
382 NumLines
= len(Points
) / 2
383 ##fixme: there should be some error checking for everything being the right length.
386 self
.Points
= array(Points
,Float
)
387 self
.BoundingBox
= array(((min(self
.Points
[:,0]),min(self
.Points
[:,1])),(max(self
.Points
[:,0]),max(self
.Points
[:,1]))),Float
)
389 self
.LineColors
= LineColors
390 self
.LineStyles
= LineStyles
391 self
.LineWidths
= LineWidths
393 self
.SetPens(LineColors
,LineStyles
,LineWidths
)
395 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
396 Points
= WorldToPixel(self
.Points
)
397 Points
.shape
= (-1,4)
398 dc
.DrawLineList(Points
,self
.Pens
)
401 class PointSet(draw_object
):
403 The PointSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
404 so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
405 or Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
407 Each point will be drawn the same color and Diameter. The Diameter is in screen points,
408 not world coordinates.
411 def __init__(self
,Points
,Color
,Diameter
,Foreground
= 0):
412 draw_object
.__init
__(self
,Foreground
)
414 self
.Points
= array(Points
,Float
)
415 self
.Points
.shape
= (-1,2) # Make sure it is a NX2 array, even if there is only one point
416 self
.BoundingBox
= array(((min(self
.Points
[:,0]),min(self
.Points
[:,1])),(max(self
.Points
[:,0]),max(self
.Points
[:,1]))),Float
)
419 self
.Diameter
= Diameter
421 self
.SetPen(Color
,"Solid",1)
422 self
.SetBrush(Color
,"Solid")
424 def SetPoints(self
,Points
):
426 self
.BoundingBox
= array(((min(self
.Points
[:,0]),min(self
.Points
[:,1])),(max(self
.Points
[:,0]),max(self
.Points
[:,1]))),Float
)
428 # It looks like this shouldn't be private
429 self
._Canvas
.BoundingBoxDirty
= 1
431 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
433 Points
= WorldToPixel(self
.Points
)
434 if self
.Diameter
<= 1:
435 dc
.DrawPointList(Points
)
436 elif self
.Diameter
<= 2:
437 # A Little optimization for a diameter2 - point
438 dc
.DrawPointList(Points
)
439 dc
.DrawPointList(Points
+ (1,0))
440 dc
.DrawPointList(Points
+ (0,1))
441 dc
.DrawPointList(Points
+ (1,1))
443 dc
.SetBrush(self
.Brush
)
444 radius
= int(round(self
.Diameter
/2))
446 dc
.DrawEllipse((x
- radius
), (y
- radius
), self
.Diameter
, self
.Diameter
)
450 class Dot(draw_object
):
452 The Dot class takes an x.y coordinate pair, and the Diameter of the circle.
453 The Diameter is in pixels, so it won't change with zoom.
455 Also Fill and line data
458 def __init__(self
,x
,y
,Diameter
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
= 0):
459 draw_object
.__init
__(self
,Foreground
)
463 self
.Diameter
= Diameter
464 # NOTE: the bounding box does not include the diameter of the dot, as that is in pixel coords.
465 # If this is a problem, perhaps you should use a circle, instead!
466 self
.BoundingBox
= array(((x
,y
),(x
,y
)),Float
)
468 self
.LineColor
= LineColor
469 self
.LineStyle
= LineStyle
470 self
.LineWidth
= LineWidth
471 self
.FillColor
= FillColor
472 self
.FillStyle
= FillStyle
474 self
.SetPen(LineColor
,LineStyle
,LineWidth
)
475 self
.SetBrush(FillColor
,FillStyle
)
477 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
479 dc
.SetBrush(self
.Brush
)
480 radius
= int(round(self
.Diameter
/2))
481 (X
,Y
) = WorldToPixel((self
.X
,self
.Y
))
482 dc
.DrawEllipse((X
- radius
), (Y
- radius
), self
.Diameter
, self
.Diameter
)
485 class Rectangle(draw_object
):
486 def __init__(self
,x
,y
,width
,height
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
= 0):
487 draw_object
.__init
__(self
,Foreground
)
493 self
.BoundingBox
= array(((x
,y
),(x
+width
,y
+height
)),Float
)
494 self
.LineColor
= LineColor
495 self
.LineStyle
= LineStyle
496 self
.LineWidth
= LineWidth
497 self
.FillColor
= FillColor
498 self
.FillStyle
= FillStyle
500 self
.SetPen(LineColor
,LineStyle
,LineWidth
)
501 self
.SetBrush(FillColor
,FillStyle
)
503 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
504 (X
,Y
) = WorldToPixel((self
.X
,self
.Y
))
505 (Width
,Height
) = ScaleFunction((self
.Width
,self
.Height
))
508 dc
.SetBrush(self
.Brush
)
509 dc
.DrawRectangle(X
,Y
,Width
,Height
)
511 class Ellipse(draw_object
):
512 def __init__(self
,x
,y
,width
,height
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
= 0):
513 draw_object
.__init
__(self
,Foreground
)
519 self
.BoundingBox
= array(((x
,y
),(x
+width
,y
+height
)),Float
)
520 self
.LineColor
= LineColor
521 self
.LineStyle
= LineStyle
522 self
.LineWidth
= LineWidth
523 self
.FillColor
= FillColor
524 self
.FillStyle
= FillStyle
526 self
.SetPen(LineColor
,LineStyle
,LineWidth
)
527 self
.SetBrush(FillColor
,FillStyle
)
529 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
530 (X
,Y
) = WorldToPixel((self
.X
,self
.Y
))
531 (Width
,Height
) = ScaleFunction((self
.Width
,self
.Height
))
534 dc
.SetBrush(self
.Brush
)
535 dc
.DrawEllipse(X
,Y
,Width
,Height
)
537 class Circle(draw_object
):
538 def __init__(self
,x
,y
,Diameter
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
= 0):
539 draw_object
.__init
__(self
,Foreground
)
543 self
.Diameter
= Diameter
544 self
.BoundingBox
= array(((x
-Diameter
/2,y
-Diameter
/2),(x
+Diameter
/2,y
+Diameter
/2)),Float
)
545 self
.LineColor
= LineColor
546 self
.LineStyle
= LineStyle
547 self
.LineWidth
= LineWidth
548 self
.FillColor
= FillColor
549 self
.FillStyle
= FillStyle
551 self
.SetPen(LineColor
,LineStyle
,LineWidth
)
552 self
.SetBrush(FillColor
,FillStyle
)
554 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
555 (X
,Y
) = WorldToPixel((self
.X
,self
.Y
))
556 (Diameter
,dummy
) = ScaleFunction((self
.Diameter
,self
.Diameter
))
559 dc
.SetBrush(self
.Brush
)
560 dc
.DrawEllipse(X
-Diameter
/2,Y
-Diameter
/2,Diameter
,Diameter
)
562 class Text(draw_object
):
565 This class creates a text object, placed at the coordinates,
566 x,y. the "Position" argument is a two charactor string, indicating
567 where in relation to the coordinates the string should be oriented.
569 The first letter is: t, c, or b, for top, center and bottom
570 The second letter is: l, c, or r, for left, center and right
572 I've provided arguments for Family, Style, and Weight of font, but
573 have not yet implimented them, so all text is: wxSWISS, wxNORMAL, wxNORMAL.
574 I'd love it if someone would impliment that!
576 The size is fixed, and does not scale with the drawing.
580 def __init__(self
,String
,x
,y
,Size
,ForeGround
,BackGround
,Family
,Style
,Weight
,Underline
,Position
,Foreground
= 0):
581 draw_object
.__init
__(self
,Foreground
)
586 self
.ForeGround
= ForeGround
587 if BackGround
is None:
588 self
.BackGround
= None
590 self
.BackGround
= BackGround
594 self
.Underline
= Underline
595 self
.Position
= Position
596 # fixme: this should use the passed in parameters!
597 self
.Font
= wxFont(Size
, wxMODERN
, wxNORMAL
, wxNORMAL
, Underline
, )
599 self
.BoundingBox
= array(((x
,y
),(x
,y
)),Float
)
606 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
607 (X
,Y
) = WorldToPixel((self
.X
,self
.Y
))
608 dc
.SetFont(self
.Font
)
609 dc
.SetTextForeground(self
.ForeGround
)
611 dc
.SetBackgroundMode(wxSOLID
)
612 dc
.SetTextBackground(self
.BackGround
)
614 dc
.SetBackgroundMode(wxTRANSPARENT
)
616 # compute the shift, and adjust the coordinates, if neccesary
617 # This had to be put in here, becsuse there is no wxDC during __init__
618 if self
.x_shift
is None or self
.y_shift
is None:
619 if self
.Position
== 'tl':
620 x_shift
,y_shift
= 0,0
622 (w
,h
) = dc
.GetTextExtent(self
.String
)
623 if self
.Position
[0] == 't':
625 elif self
.Position
[0] == 'c':
627 elif self
.Position
[0] == 'b':
630 ##fixme: this should be a real derived exception
631 raise "Invalid value for Text Object Position Attribute"
632 if self
.Position
[1] == 'l':
634 elif self
.Position
[1] == 'c':
636 elif self
.Position
[1] == 'r':
639 ##fixme: this should be a real derived exception
640 raise "Invalid value for Text Object Position Attribute"
641 self
.x_shift
= x_shift
642 self
.y_shift
= y_shift
643 dc
.DrawText(self
.String
, X
-self
.x_shift
, Y
-self
.y_shift
)
646 #---------------------------------------------------------------------------
648 class FloatCanvas(wxPanel
):
652 This is a high level window for drawing maps and anything else in an
653 arbitrary coordinate system.
655 The goal is to provide a convenient way to draw stuff on the screen
656 without having to deal with handling OnPaint events, converting to pixel
657 coordinates, knowing about wxWindows brushes, pens, and colors, etc. It
658 also provides virtually unlimited zooming and scrolling
660 I am using it for two things:
661 1) general purpose drawing in floating point coordinates
662 2) displaying map data in Lat-long coordinates
664 If the projection is set to None, it will draw in general purpose
665 floating point coordinates. If the projection is set to 'FlatEarth', it
666 will draw a FlatEarth projection, centered on the part of the map that
667 you are viewing. You can also pass in your own projection function.
669 It is double buffered, so re-draws after the window is uncovered by something
672 It relies on NumPy, which is needed for speed
674 Bugs and Limitations:
675 Lots: patches, fixes welcome
677 For Map drawing: It ignores the fact that the world is, in fact, a
678 sphere, so it will do strange things if you are looking at stuff near
679 the poles or the date line. so far I don't have a need to do that, so I
680 havn't bothered to add any checks for that yet.
683 I have set no zoom limits. What this means is that if you zoom in really
684 far, you can get integer overflows, and get wierd results. It
685 doesn't seem to actually cause any problems other than wierd output, at
686 least when I have run it.
689 I have done a couple of things to improve speed in this app. The one
690 thing I have done is used NumPy Arrays to store the coordinates of the
691 points of the objects. This allowed me to use array oriented functions
692 when doing transformations, and should provide some speed improvement
693 for objects with a lot of points (big polygons, polylines, pointsets).
695 The real slowdown comes when you have to draw a lot of objects, because
696 you have to call the wxDC.DrawSomething call each time. This is plenty
697 fast for tens of objects, OK for hundreds of objects, but pretty darn
698 slow for thousands of objects.
700 The solution is to be able to pass some sort of object set to the DC
701 directly. I've used DC.DrawPointList(Points), and it helped a lot with
702 drawing lots of points. I havn't got a LineSet type object, so I havn't
703 used DC.DrawLineList yet. I'd like to get a full set of DrawStuffList()
704 methods implimented, and then I'd also have a full set of Object sets
705 that could take advantage of them. I hope to get to it some day.
707 Copyright: Christopher Barker
709 License: Same as wxPython
711 Please let me know if you're using this!!!
715 Chris.Barker@noaa.gov
719 def __init__(self
, parent
, id = -1,
720 size
= wxDefaultSize
,
721 ProjectionFun
= None,
722 BackgroundColor
= "WHITE",
724 EnclosingFrame
= None,
729 wxPanel
.__init
__( self
, parent
, id, wxDefaultPosition
, size
)
731 if ProjectionFun
== 'FlatEarth':
732 self
.ProjectionFun
= self
.FlatEarthProjection
733 elif type(ProjectionFun
) == types
.FunctionType
:
734 self
.ProjectionFun
= ProjectionFun
735 elif ProjectionFun
is None:
736 self
.ProjectionFun
= lambda x
=None: array( (1,1), Float
)
738 raise('Projectionfun must be either: "FlatEarth", None, or a function that takes the ViewPortCenter and returns a MapProjectionVector')
740 self
.UseBackground
= UseBackground
741 self
.UseHitTest
= UseHitTest
743 self
.NumBetweenBlits
= 40
745 ## you can have a toolbar with buttons for zoom-in, zoom-out and
746 ## move. If you don't use the toolbar, you should provide your
747 ## own way of navigating the canvas
749 ## Create the vertical sizer for the toolbar and Panel
750 box
= wxBoxSizer(wxVERTICAL
)
751 box
.Add(self
.BuildToolbar(), 0, wxALL | wxALIGN_LEFT | wxGROW
, 4)
753 self
.DrawPanel
= wxWindow(self
,-1,wxDefaultPosition
,wxDefaultSize
,wxSUNKEN_BORDER
)
754 box
.Add(self
.DrawPanel
,1,wxGROW
)
757 self
.SetAutoLayout(True)
760 self
.DrawPanel
= self
762 self
.DrawPanel
.BackgroundBrush
= wxBrush(BackgroundColor
,wxSOLID
)
766 self
.EnclosingFrame
= EnclosingFrame
768 EVT_PAINT(self
.DrawPanel
, self
.OnPaint
)
769 EVT_SIZE(self
.DrawPanel
, self
.OnSize
)
771 EVT_LEFT_DOWN(self
.DrawPanel
, self
.LeftButtonEvent
)
772 EVT_LEFT_UP(self
.DrawPanel
, self
.LeftButtonEvent
)
773 EVT_RIGHT_DOWN(self
.DrawPanel
, self
.RightButtonEvent
)
774 EVT_MOTION(self
.DrawPanel
, self
.LeftButtonEvent
)
778 if self
.UseBackground
:
779 self
._TopDrawList
= []
780 self
.BoundingBox
= None
781 self
.BoundingBoxDirty
= 0
782 self
.ViewPortCenter
= array( (0,0), Float
)
784 self
.MapProjectionVector
= array( (1,1), Float
) # No Projection to start!
785 self
.TransformVector
= array( (1,-1), Float
) # default Transformation
789 # called just to make sure everything is initialized
793 self
.StartRBBox
= None
794 self
.StartMove
= None
796 def BuildToolbar(self
):
797 tb
= wxToolBar(self
,-1)
800 tb
.AddTool(ID_ZOOM_IN_BUTTON
, GetPlusBitmap(), isToggle
=true
,shortHelpString
= "Zoom In")
801 EVT_TOOL(self
, ID_ZOOM_IN_BUTTON
, self
.SetMode
)
803 tb
.AddTool(ID_ZOOM_OUT_BUTTON
, GetMinusBitmap(), isToggle
=true
,shortHelpString
= "Zoom Out")
804 EVT_TOOL(self
, ID_ZOOM_OUT_BUTTON
, self
.SetMode
)
806 tb
.AddTool(ID_MOVE_MODE_BUTTON
, GetHandBitmap(), isToggle
=true
,shortHelpString
= "Move")
807 EVT_TOOL(self
, ID_MOVE_MODE_BUTTON
, self
.SetMode
)
811 tb
.AddControl(wxButton(tb
, ID_ZOOM_TO_FIT_BUTTON
, "Zoom To Fit",wxDefaultPosition
, wxDefaultSize
))
812 EVT_BUTTON(self
, ID_ZOOM_TO_FIT_BUTTON
, self
.ZoomToFit
)
817 def SetMode(self
,event
):
818 for id in [ID_ZOOM_IN_BUTTON
,ID_ZOOM_OUT_BUTTON
,ID_MOVE_MODE_BUTTON
]:
819 self
.ToolBar
.ToggleTool(id,0)
820 self
.ToolBar
.ToggleTool(event
.GetId(),1)
821 if event
.GetId() == ID_ZOOM_IN_BUTTON
:
822 self
.SetGUIMode("ZoomIn")
823 elif event
.GetId() == ID_ZOOM_OUT_BUTTON
:
824 self
.SetGUIMode("ZoomOut")
825 elif event
.GetId() == ID_MOVE_MODE_BUTTON
:
826 self
.SetGUIMode("Move")
829 def SetGUIMode(self
,Mode
):
830 if Mode
in ["ZoomIn","ZoomOut","Move",None]:
833 raise "Not a valid Mode"
835 def FlatEarthProjection(self
,CenterPoint
):
836 return array((cos(pi
*CenterPoint
[1]/180),1),Float
)
838 def LeftButtonEvent(self
,event
):
839 if self
.EnclosingFrame
:
841 position
= self
.PixelToWorld((event
.GetX(),event
.GetY()))
842 self
.EnclosingFrame
.SetStatusText("%8.3f, %8.3f"%tuple(position
))
844 if self
.GUIMode
== "ZoomIn":
846 self
.StartRBBox
= (event
.GetX(),event
.GetY())
847 self
.PrevRBBox
= None
848 elif event
.Dragging() and event
.LeftIsDown() and self
.StartRBBox
:
849 x0
,y0
= self
.StartRBBox
850 x1
,y1
= event
.GetX(),event
.GetY()
851 w
, h
= abs(x1
-x0
),abs(y1
-y0
)
852 w
= max(w
,int(h
*self
.AspectRatio
))
853 h
= int(w
/self
.AspectRatio
)
854 x_c
, y_c
= (x0
+x1
)/2 , (y0
+y1
)/2
855 dc
= wxClientDC(self
.DrawPanel
)
857 dc
.SetPen(wxPen('WHITE', 2,wxSHORT_DASH
))
858 dc
.SetBrush(wxTRANSPARENT_BRUSH
)
859 dc
.SetLogicalFunction(wxXOR
)
861 dc
.DrawRectangle(*self
.PrevRBBox
)
862 dc
.DrawRectangle(x_c
-w
/2,y_c
-h
/2,w
,h
)
863 self
.PrevRBBox
= (x_c
-w
/2,y_c
-h
/2,w
,h
)
866 elif event
.LeftUp() and self
.StartRBBox
:
867 EndRBBox
= (event
.GetX(),event
.GetY())
868 StartRBBox
= self
.StartRBBox
869 # if mouse has moved less that ten pixels, don't use the box.
870 if abs(StartRBBox
[0] - EndRBBox
[0]) > 10 and abs(StartRBBox
[1] - EndRBBox
[1]) > 10:
871 EndRBBox
= self
.PixelToWorld(EndRBBox
)
872 StartRBBox
= self
.PixelToWorld(StartRBBox
)
873 BB
= array(((min(EndRBBox
[0],StartRBBox
[0]), min(EndRBBox
[1],StartRBBox
[1])),
874 (max(EndRBBox
[0],StartRBBox
[0]), max(EndRBBox
[1],StartRBBox
[1]))),Float
)
877 Center
= self
.PixelToWorld(StartRBBox
)
878 self
.Zoom(1.5,Center
)
879 self
.StartRBBox
= None
880 if self
.GUIMode
== "ZoomOut":
882 Center
= self
.PixelToWorld((event
.GetX(),event
.GetY()))
883 self
.Zoom(1/1.5,Center
)
884 elif self
.GUIMode
== "Move":
886 self
.StartMove
= array((event
.GetX(),event
.GetY()))
887 self
.PrevMoveBox
= None
888 elif event
.Dragging() and event
.LeftIsDown() and self
.StartMove
:
889 x_1
,y_1
= event
.GetX(),event
.GetY()
890 w
, h
= self
.PanelSize
891 x_tl
, y_tl
= x_1
- self
.StartMove
[0], y_1
- self
.StartMove
[1]
892 dc
= wxClientDC(self
.DrawPanel
)
894 dc
.SetPen(wxPen('WHITE', 1,))
895 dc
.SetBrush(wxTRANSPARENT_BRUSH
)
896 dc
.SetLogicalFunction(wxXOR
)
898 dc
.DrawRectangle(*self
.PrevMoveBox
)
899 dc
.DrawRectangle(x_tl
,y_tl
,w
,h
)
900 self
.PrevMoveBox
= (x_tl
,y_tl
,w
,h
)
903 elif event
.LeftUp() and self
.StartMove
:
904 StartMove
= self
.StartMove
905 EndMove
= array((event
.GetX(),event
.GetY()))
906 if sum((StartMove
-EndMove
)**2) > 16:
907 self
.Move(StartMove
-EndMove
,'Pixel')
908 self
.StartMove
= None
910 def RightButtonEvent(self
,event
):
912 if self
.GUIMode
== "ZoomIn":
913 Center
= self
.PixelToWorld((event
.GetX(),event
.GetY()))
914 self
.Zoom(1/1.5,Center
)
915 elif self
.GUIMode
== "ZoomOut":
916 Center
= self
.PixelToWorld((event
.GetX(),event
.GetY()))
917 self
.Zoom(1.5,Center
)
921 def MakeNewBuffers(self
):
922 # Make new offscreen bitmap:
923 self
._Buffer
= wxEmptyBitmap(int(self
.PanelSize
[0]), int(self
.PanelSize
[1]))
924 if self
.UseBackground
:
925 self
._BackBuffer
= wxEmptyBitmap((self
.PanelSize
[0]), (self
.PanelSize
[1]))
926 self
._BackgroundDirty
= 1
930 def OnSize(self
,event
):
931 self
.PanelSize
= array(self
.DrawPanel
.GetClientSizeTuple(),Float
)
933 self
.AspectRatio
= self
.PanelSize
[0]/self
.PanelSize
[1]
934 except ZeroDivisionError:
935 self
.AspectRatio
= 1.0
936 self
.MakeNewBuffers()
939 def OnPaint(self
, event
):
940 #dc = wxBufferedPaintDC(self.DrawPanel, self._Buffer)
941 dc
= wxPaintDC(self
.DrawPanel
)
942 dc
.DrawBitmap(self
._Buffer
,0,0)
946 The Draw method gets pretty complicated because of all the buffers
948 There is a main buffer set up to double buffer the screen, so
949 you can get quick re-draws when the window gets uncovered.
951 If self.UseBackground is set, and an object is set up with the
952 "ForeGround" flag, then it gets drawn to the screen after blitting
953 the background. This is done so that you can have a complicated
954 background, but have something changing on the foreground,
955 without having to wait for the background to get re-drawn. This
956 can be used to support simple animation, for instance.
959 if self
.Debug
: start
= clock()
960 ScreenDC
= wxClientDC(self
.DrawPanel
)
961 if self
.UseBackground
:
963 dc
.SelectObject(self
._BackBuffer
)
964 dc
.SetBackground(self
.DrawPanel
.BackgroundBrush
)
966 if self
._BackgroundDirty
:
970 for Object
in self
._DrawList
:
971 if self
.BBCheck(Object
.BoundingBox
,ViewPortBB
):
972 #print "object is in Bounding Box"
974 Object
._Draw
(dc
,self
.WorldToPixel
,self
.ScaleFunction
)
975 if i
% self
.NumBetweenBlits
== 0:
976 ScreenDC
.Blit(0, 0, self
.PanelSize
[0],self
.PanelSize
[1], dc
, 0, 0)
980 self
._BackgroundDirty
= 0
981 dc
.SelectObject(self
._Buffer
)
983 ##Draw Background on Main Buffer:
984 dc
.DrawBitmap(self
._BackBuffer
,0,0)
985 #Draw the OnTop stuff
987 for Object
in self
._TopDrawList
:
989 Object
._Draw
(dc
,self
.WorldToPixel
,self
.ScaleFunction
)
990 if i
% self
.NumBetweenBlits
== 0:
991 ScreenDC
.Blit(0, 0, self
.PanelSize
[0],self
.PanelSize
[1], dc
, 0, 0)
993 else: # not using a Background DC
995 dc
.SelectObject(self
._Buffer
)
996 dc
.SetBackground(self
.DrawPanel
.BackgroundBrush
)
1001 ViewPortWorld
= array((self
.PixelToWorld((0,0)), self
.PixelToWorld(self
.DrawPanel
.GetSizeTuple() ) ),Float
)
1002 ViewPortBB
= array( (minimum
.reduce(ViewPortWorld
), maximum
.reduce(ViewPortWorld
)) )
1003 for Object
in self
._DrawList
:
1004 if self
.BBCheck(Object
.BoundingBox
,ViewPortBB
):
1005 #print "object is in Bounding Box"
1007 Object
._Draw
(dc
,self
.WorldToPixel
,self
.ScaleFunction
)
1008 if i
% self
.NumBetweenBlits
== 0:
1009 ScreenDC
.Blit(0, 0, int(self
.PanelSize
[0]), int(self
.PanelSize
[1]), dc
, 0, 0)
1010 print "there were %i objects drawn"%i
1014 # now refresh the screen
1015 ScreenDC
.DrawBitmap(self
._Buffer
,0,0)
1016 if self
.Debug
: print "Drawing took %f seconds of CPU time"%(clock()-start
)
1018 def BBCheck(self
, BB1
, BB2
):
1021 BBCheck(BB1, BB2) returns True is the Bounding boxes intesect, False otherwise
1024 if ( (BB1
[1,0] > BB2
[0,0]) and (BB1
[0,0] < BB2
[1,0]) and
1025 (BB1
[1,1] > BB2
[0,1]) and (BB1
[0,1] < BB2
[1,1]) ):
1030 def Move(self
,shift
,CoordType
):
1032 move the image in the window.
1034 shift is an (x,y) tuple, specifying the amount to shift in each direction
1036 It can be in any of three coordinates: Panel, Pixel, World,
1037 specified by the CoordType parameter
1039 Panel coordinates means you want to shift the image by some
1040 fraction of the size of the displaed image
1042 Pixel coordinates means you want to shift the image by some number of pixels
1044 World coordinates meand you want to shift the image by an amount
1045 in Floating point world coordinates
1049 shift
= array(shift
,Float
)
1050 if CoordType
== 'Panel':# convert from panel coordinates
1051 shift
= shift
* array((-1,1),Float
) *self
.PanelSize
/self
.TransformVector
1052 elif CoordType
== 'Pixel': # convert from pixel coordinates
1053 shift
= shift
/self
.TransformVector
1054 elif CoordType
== 'World': # No conversion
1057 raise 'CoordType must be either "Panel", "Pixel", or "World"'
1059 self
.ViewPortCenter
= self
.ViewPortCenter
+ shift
1060 self
.MapProjectionVector
= self
.ProjectionFun(self
.ViewPortCenter
)
1061 self
.TransformVector
= array((self
.Scale
,-self
.Scale
),Float
)* self
.MapProjectionVector
1062 self
._BackgroundDirty
= 1
1065 def Zoom(self
,factor
,center
= None):
1068 Zoom(factor, center) changes the amount of zoom of the image by factor.
1069 If factor is greater than one, the image gets larger.
1070 If factor is less than one, the image gets smaller.
1072 Center is a tuple of (x,y) coordinates of the center of the viewport, after zooming.
1073 If center is not given, the center will stay the same.
1076 self
.Scale
= self
.Scale
*factor
1078 self
.ViewPortCenter
= array(center
,Float
)
1079 self
.MapProjectionVector
= self
.ProjectionFun(self
.ViewPortCenter
)
1080 self
.TransformVector
= array((self
.Scale
,-self
.Scale
),Float
)* self
.MapProjectionVector
1081 self
._BackgroundDirty
= 1
1084 def ZoomToFit(self
,event
):
1087 def ZoomToBB(self
,NewBB
= None,DrawFlag
= 1):
1091 Zooms the image to the bounding box given, or to the bounding
1092 box of all the objects on the canvas, if none is given.
1099 if self
.BoundingBoxDirty
:
1100 self
._ResetBoundingBox
()
1101 BoundingBox
= self
.BoundingBox
1103 self
.ViewPortCenter
= array(((BoundingBox
[0,0]+BoundingBox
[1,0])/2,
1104 (BoundingBox
[0,1]+BoundingBox
[1,1])/2 ),Float
)
1105 self
.MapProjectionVector
= self
.ProjectionFun(self
.ViewPortCenter
)
1106 # Compute the new Scale
1107 BoundingBox
= BoundingBox
* self
.MapProjectionVector
1109 self
.Scale
= min((self
.PanelSize
[0] / (BoundingBox
[1,0]-BoundingBox
[0,0])),
1110 (self
.PanelSize
[1] / (BoundingBox
[1,1]-BoundingBox
[0,1])))*0.95
1111 except ZeroDivisionError: # this will happen if the BB has zero width or height
1113 self
.Scale
= (self
.PanelSize
[0] / (BoundingBox
[1,0]-BoundingBox
[0,0]))*0.95
1114 except ZeroDivisionError:
1116 self
.Scale
= (self
.PanelSize
[1] / (BoundingBox
[1,1]-BoundingBox
[0,1]))*0.95
1117 except ZeroDivisionError: #zero size! (must be a single point)
1120 self
.TransformVector
= array((self
.Scale
,-self
.Scale
),Float
)* self
.MapProjectionVector
1122 self
._BackgroundDirty
= 1
1125 def RemoveObjects(self
,Objects
):
1126 for Object
in Objects
:
1127 self
.RemoveObject(Object
,ResetBB
= 0)
1128 self
.BoundingBoxDirty
= 1
1130 def RemoveObject(self
,Object
,ResetBB
= 1):
1131 if Object
.Foreground
:
1132 self
._TopDrawList
.remove(Object
)
1134 self
._DrawList
.remove(Object
)
1135 self
._BackgroundDirty
= 1
1138 self
.BoundingBoxDirty
= 1
1140 def Clear(self
, ResetBB
= True):
1142 if self
.UseBackground
:
1143 self
._TopDrawList
= []
1144 self
._BackgroundDirty
= 1
1146 self
._ResetBoundingBox
()
1148 def _AddBoundingBox(self
,NewBB
):
1149 if self
.BoundingBox
is None:
1150 self
.BoundingBox
= NewBB
1151 self
.ZoomToBB(NewBB
,DrawFlag
= 0)
1153 self
.BoundingBox
= array(((min(self
.BoundingBox
[0,0],NewBB
[0,0]),
1154 min(self
.BoundingBox
[0,1],NewBB
[0,1])),
1155 (max(self
.BoundingBox
[1,0],NewBB
[1,0]),
1156 max(self
.BoundingBox
[1,1],NewBB
[1,1]))),Float
)
1157 def _ResetBoundingBox(self
):
1158 # NOTE: could you remove an item without recomputing the entire bounding box?
1159 self
.BoundingBox
= None
1161 self
.BoundingBox
= self
._DrawList
[0].BoundingBox
1162 for Object
in self
._DrawList
[1:]:
1163 self
._AddBoundingBox
(Object
.BoundingBox
)
1164 if self
.UseBackground
:
1165 for Object
in self
._TopDrawList
:
1166 self
._AddBoundingBox
(Object
.BoundingBox
)
1167 if self
.BoundingBox
is None:
1168 self
.ViewPortCenter
= array( (0,0), Float
)
1169 self
.TransformVector
= array( (1,-1), Float
)
1170 self
.MapProjectionVector
= array( (1,1), Float
)
1172 self
.BoundingBoxDirty
= 0
1174 def PixelToWorld(self
,Points
):
1176 Converts coordinates from Pixel coordinates to world coordinates.
1178 Points is a tuple of (x,y) coordinates, or a list of such tuples, or a NX2 Numpy array of x,y coordinates.
1181 return (((array(Points
,Float
) - (self
.PanelSize
/2))/self
.TransformVector
) + self
.ViewPortCenter
)
1183 def WorldToPixel(self
,Coordinates
):
1185 This function will get passed to the drawing functions of the objects,
1186 to transform from world to pixel coordinates.
1187 Coordinates should be a NX2 array of (x,y) coordinates, or
1188 a 2-tuple, or sequence of 2-tuples.
1190 return (((array(Coordinates
,Float
) - self
.ViewPortCenter
)*self
.TransformVector
)+(self
.PanelSize
/2)).astype('i')
1192 def ScaleFunction(self
,Lengths
):
1194 This function will get passed to the drawing functions of the objects,
1195 to Change a length from world to pixel coordinates.
1197 Lengths should be a NX2 array of (x,y) coordinates, or
1198 a 2-tuple, or sequence of 2-tuples.
1200 return (array(Lengths
,Float
)*self
.TransformVector
).astype('i')
1203 ## This is a set of methods that add objects to the Canvas. It kind
1204 ## of seems like a lot of duplication, but I wanted to be able to
1205 ## instantiate the draw objects separatley form adding them, but
1206 ## also to be able to do add one oin one step. I'm open to better
1208 def AddRectangle(self
,x
,y
,width
,height
,
1209 LineColor
= "Black",
1210 LineStyle
= "Solid",
1213 FillStyle
= "Solid",
1215 Object
= Rectangle(x
,y
,width
,height
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
)
1216 self
.AddObject(Object
)
1219 def AddEllipse(self
,x
,y
,width
,height
,
1220 LineColor
= "Black",
1221 LineStyle
= "Solid",
1224 FillStyle
= "Solid",
1226 Object
= Ellipse(x
,y
,width
,height
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
)
1227 self
.AddObject(Object
)
1230 def AddCircle(self
,x
,y
,Diameter
,
1231 LineColor
= "Black",
1232 LineStyle
= "Solid",
1235 FillStyle
= "Solid",
1237 Object
= Circle(x
,y
,Diameter
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
)
1238 self
.AddObject(Object
)
1241 def AddDot(self
,x
,y
,Diameter
,
1242 LineColor
= "Black",
1243 LineStyle
= "Solid",
1246 FillStyle
= "Solid",
1248 Object
= Dot(x
,y
,Diameter
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
)
1249 self
.AddObject(Object
)
1252 def AddPolygon(self
,Points
,
1253 LineColor
= "Black",
1254 LineStyle
= "Solid",
1257 FillStyle
= "Solid",
1260 Object
= Polygon(Points
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
)
1261 self
.AddObject(Object
)
1264 def AddLine(self
,Points
,
1265 LineColor
= "Black",
1266 LineStyle
= "Solid",
1270 Object
= Line(Points
,LineColor
,LineStyle
,LineWidth
,Foreground
)
1271 self
.AddObject(Object
)
1274 def AddLineSet(self
,Points
,
1275 LineColors
= "Black",
1276 LineStyles
= "Solid",
1280 Object
= LineSet(Points
,LineColors
,LineStyles
,LineWidths
,Foreground
)
1281 self
.AddObject(Object
)
1284 def AddPointSet(self
,Points
,
1289 Object
= PointSet(Points
,Color
,Diameter
,Foreground
)
1290 self
.AddObject(Object
)
1293 def AddText(self
,String
,x
,y
,
1295 ForeGround
= 'Black',
1303 Object
= Text(String
,x
,y
,Size
,ForeGround
,BackGround
,Family
,Style
,Weight
,Underline
,Position
,Foreground
)
1304 self
.AddObject(Object
)
1307 def AddObject(self
,obj
):
1308 # put in a reference to the Canvas, so remove and other stuff can work
1310 if obj
.Foreground
and self
.UseBackground
:
1311 self
._TopDrawList
.append(obj
)
1313 self
._DrawList
.append(obj
)
1314 self
._backgrounddirty
= 1
1315 self
._AddBoundingBox
(obj
.BoundingBox
)