]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/floatcanvas.py
3 from Numeric
import array
,Float
,cos
,pi
,sum,minimum
,maximum
,Int32
4 from time
import clock
, sleep
8 ID_ZOOM_IN_BUTTON
= wx
.NewId()
9 ID_ZOOM_OUT_BUTTON
= wx
.NewId()
10 ID_ZOOM_TO_FIT_BUTTON
= wx
.NewId()
11 ID_MOVE_MODE_BUTTON
= wx
.NewId()
12 ID_TEST_BUTTON
= wx
.NewId()
14 ID_ABOUT_MENU
= wx
.NewId()
15 ID_EXIT_MENU
= wx
.NewId()
16 ID_ZOOM_TO_FIT_MENU
= wx
.NewId()
17 ID_DRAWTEST_MENU
= wx
.NewId()
18 ID_DRAWMAP_MENU
= wx
.NewId()
19 ID_CLEAR_MENU
= wx
.NewId()
24 ### These are some functions for bitmaps of icons.
28 return cPickle
.loads(zlib
.decompress(
29 'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
30 \x01\xc8S\xb6t\x06A(\x1f\x0b\xa0\xa9\x8c\x9e\x1e6\x19\xa0\xa8\x1e\x88\xd4C\
31 \x97\xd1\x83\xe8\x80 \x9c2zh\xa6\xc1\x11X\n\xab\x8c\x02\x8a\x0cD!\x92\x12\
32 \x98\x8c\x1e\x8a\x8b\xd1d\x14\xf4\x90%\x90LC\xf6\xbf\x1e\xba\xab\x91%\xd0\
33 \xdc\x86C\x06\xd9m\xe8!\xaa\x87S\x86\x1a1\xa7\x07\x00v\x0f[\x17' ))
36 return wx
.BitmapFromXPMData(GetHandData())
38 #----------------------------------------------------------------------
40 return cPickle
.loads(zlib
.decompress(
41 'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
42 \x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=l2`\r\
43 \xe82HF\xe9a\xc8\xe8\xe9A\x9c@\x8a\x0c\x0e\xd3p\xbb\x00\x8f\xab\xe1>\xd5\xd3\
44 \xc3\x15:P)l!\n\x91\xc2\x1a\xd6`)\xec\xb1\x00\x92\xc2\x11?\xb8e\x88\x8fSt\
45 \x19=\x00\x82\x16[\xf7' ))
48 return wx
.BitmapFromXPMData(GetPlusData())
50 #----------------------------------------------------------------------
52 return cPickle
.loads(zlib
.decompress(
53 'x\xda\xd3\xc8)0\xe4\nV72T\x00!\x05Cu\xae\xc4`u=\x85d\x05\xa7\x9c\xc4\xe4l0O\
54 \x01\xc8S\xb6t\x06A(\x1f\x0b RF\x0f\x08\xb0\xc9@D\xe1r\x08\x19\xb8j=\xa2e\
55 \x10\x16@\x99\xc82zz\x10\'\x90"\x83\xc34r\xdc\x86\xf0\xa9\x9e\x1e\xae\xd0\
56 \x81Ja\x0bQ\x88\x14\xd6\xb0\x06Ka\x8f\x05\x90\x14\x8e\xf8\xc1-C|\x9c\xa2\xcb\
57 \xe8\x01\x00\xed\x0f[\x87' ))
60 return wx
.BitmapFromXPMData(GetMinusData())
62 ## This is a bunch of stuff for implimenting interactive use: catching
63 ## when objects are clicked on by the mouse, etc. I've made a start, so if
64 ## you are interesed in making that work, let me know, I may have gotten
67 #### I probably want a full set of events someday:
68 ## #### mouse over, right click, left click mouse up etc, etc.
69 ## ##FLOATCANVAS_EVENT_LEFT_DOWN = wx.NewEventType()
70 ## ##FLOATCANVAS_EVENT_LEFT_UP = wx.NewEventType()
71 ## ##FLOATCANVAS_EVENT_RIGHT_DOWN = wx.NewEventType()
72 ## ##FLOATCANVAS_EVENT_RIGHT_UP = wx.NewEventType()
73 ## ##FLOATCANVAS_EVENT_MOUSE_OVER = wx.NewEventType()
75 ##WXFLOATCANVASEVENT = wx.NewEventType()
77 ##def EVT_WXFLOATCANVASEVENT( window, function ):
79 ## """Your documentation here"""
81 ## window.Connect( -1, -1, WXFLOATCANVASEVENT, function )
83 ##class wxFloatCanvasObjectEvent(wx.PyCommandEvent):
84 ## def __init__(self, WindowID,Object):
85 ## wx.PyCommandEvent.__init__(self, WXFLOATCANVASEVENT, WindowID)
86 ## self.Object = Object
89 ## self.__class__( self.GetId() )
91 ##class ColorGenerator:
93 ## """ An instance of this class generates a unique color each time
94 ## GetNextColor() is called. Someday I will use a proper Python
95 ## generator for this class.
97 ## The point of this generator is for the hit-test bitmap, each object
98 ## needs to be a unique color. Also, each system can be running a
99 ## different number of colors, and it doesn't appear to be possible to
100 ## have a wxMemDC with a different colordepth as the screen so this
101 ## generates colors far enough apart that they can be distinguished on
102 ## a 16bit screen. Anything less than 16bits won't work.
105 ## def __init__(self,depth = 16):
114 ## raise "ColorGenerator does not work with depth = %s"%depth
116 ## def GetNextColor(self):
118 ## ##r,g,b = self.r,self.g,self.b
127 ## ## fixme: this should be a derived exception
128 ## raise "Too many objects for HitTest"
129 ## return (self.r,self.g,self.b)
134 This is the base class for all the objects that can be drawn.
136 each object has the following properties; (incomplete)
138 BoundingBox : is of the form: array((min_x,min_y),(max_x,max_y))
144 def __init__(self
,Foreground
= 0):
145 self
.Foreground
= Foreground
149 # I pre-define all these as class variables to provide an easier
150 # interface, and perhaps speed things up by caching all the Pens
151 # and Brushes, although that may not help, as I think wx now
152 # does that on it's own. Send me a note if you know!
155 ( None,"Transparent") : wx
.TRANSPARENT_BRUSH
,
156 ("Blue","Solid") : wx
.BLUE_BRUSH
,
157 ("Green","Solid") : wx
.GREEN_BRUSH
,
158 ("White","Solid") : wx
.WHITE_BRUSH
,
159 ("Black","Solid") : wx
.BLACK_BRUSH
,
160 ("Grey","Solid") : wx
.GREY_BRUSH
,
161 ("MediumGrey","Solid") : wx
.MEDIUM_GREY_BRUSH
,
162 ("LightGrey","Solid") : wx
.LIGHT_GREY_BRUSH
,
163 ("Cyan","Solid") : wx
.CYAN_BRUSH
,
164 ("Red","Solid") : wx
.RED_BRUSH
167 (None,"Transparent",1) : wx
.TRANSPARENT_PEN
,
168 ("Green","Solid",1) : wx
.GREEN_PEN
,
169 ("White","Solid",1) : wx
.WHITE_PEN
,
170 ("Black","Solid",1) : wx
.BLACK_PEN
,
171 ("Grey","Solid",1) : wx
.GREY_PEN
,
172 ("MediumGrey","Solid",1) : wx
.MEDIUM_GREY_PEN
,
173 ("LightGrey","Solid",1) : wx
.LIGHT_GREY_PEN
,
174 ("Cyan","Solid",1) : wx
.CYAN_PEN
,
175 ("Red","Solid",1) : wx
.RED_PEN
179 "Transparent" : wx
.TRANSPARENT
,
181 "BiDiagonalHatch": wx
.BDIAGONAL_HATCH
,
182 "CrossDiagHatch" : wx
.CROSSDIAG_HATCH
,
183 "FDiagonal_Hatch": wx
.FDIAGONAL_HATCH
,
184 "CrossHatch" : wx
.CROSS_HATCH
,
185 "HorizontalHatch": wx
.HORIZONTAL_HATCH
,
186 "VerticalHatch" : wx
.VERTICAL_HATCH
191 "Transparent": wx
.TRANSPARENT
,
193 "LongDash" : wx
.LONG_DASH
,
194 "ShortDash" : wx
.SHORT_DASH
,
195 "DotDash" : wx
.DOT_DASH
,
198 def SetBrush(self
,FillColor
,FillStyle
):
199 if FillColor
is None or FillStyle
is None:
200 self
.Brush
= wx
.TRANSPARENT_BRUSH
201 self
.FillStyle
= "Transparent"
203 if not self
.BrushList
.has_key((FillColor
,FillStyle
)):
204 self
.BrushList
[(FillColor
,FillStyle
)] = wx
.Brush(FillColor
,self
.FillStyleList
[FillStyle
])
205 self
.Brush
= self
.BrushList
[(FillColor
,FillStyle
)]
207 def SetPen(self
,LineColor
,LineStyle
,LineWidth
):
208 if (LineColor
is None) or (LineStyle
is None):
209 self
.Pen
= wx
.TRANSPARENT_PEN
210 self
.LineStyle
= 'Transparent'
212 if not self
.PenList
.has_key((LineColor
,LineStyle
,LineWidth
)):
213 self
.PenList
[(LineColor
,LineStyle
,LineWidth
)] = wx
.Pen(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(wx
.TRANSPARENT_PEN
)
253 # what's this for?> self.LineStyle = 'Transparent'
254 if not self
.PenList
.has_key((LineColor
,LineStyle
,LineWidth
)):
255 Pen
= wx
.Pen(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 SetPoints(self
,Points
):
365 self
.BoundingBox
= array(((min(self
.Points
[:,0]),min(self
.Points
[:,1])),(max(self
.Points
[:,0]),max(self
.Points
[:,1]))),Float
)
367 # It looks like this shouldn't be private
368 self
._Canvas
.BoundingBoxDirty
= 1
370 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
371 Points
= WorldToPixel(self
.Points
)
373 #dc.DrawLines(map(lambda x: (x[0],x[1]), Points.tolist()))
377 class LineSet(draw_object
):
379 The LineSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
380 so that Points[N] = (x1,y1) and Points[N+1] = (x2,y2). N must be an even number!
382 it creates a set of line segments, from (x1,y1) to (x2,y2)
386 def __init__(self
,Points
,LineColors
,LineStyles
,LineWidths
,Foreground
= 0):
387 draw_object
.__init
__(self
, Foreground
)
389 NumLines
= len(Points
) / 2
390 ##fixme: there should be some error checking for everything being the right length.
393 self
.Points
= array(Points
,Float
)
394 self
.BoundingBox
= array(((min(self
.Points
[:,0]),min(self
.Points
[:,1])),(max(self
.Points
[:,0]),max(self
.Points
[:,1]))),Float
)
396 self
.LineColors
= LineColors
397 self
.LineStyles
= LineStyles
398 self
.LineWidths
= LineWidths
400 self
.SetPens(LineColors
,LineStyles
,LineWidths
)
402 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
403 Points
= WorldToPixel(self
.Points
)
404 Points
.shape
= (-1,4)
405 dc
.DrawLineList(Points
,self
.Pens
)
408 class PointSet(draw_object
):
410 The PointSet class takes a list of 2-tuples, or a NX2 NumPy array of point coordinates.
411 so that Points[N][0] is the x-coordinate of point N and Points[N][1] is the y-coordinate
412 or Points[N,0] is the x-coordinate of point N and Points[N,1] is the y-coordinate for arrays.
414 Each point will be drawn the same color and Diameter. The Diameter is in screen points,
415 not world coordinates.
418 def __init__(self
,Points
,Color
,Diameter
,Foreground
= 0):
419 draw_object
.__init
__(self
,Foreground
)
421 self
.Points
= array(Points
,Float
)
422 self
.Points
.shape
= (-1,2) # Make sure it is a NX2 array, even if there is only one point
423 self
.BoundingBox
= array(((min(self
.Points
[:,0]),min(self
.Points
[:,1])),(max(self
.Points
[:,0]),max(self
.Points
[:,1]))),Float
)
426 self
.Diameter
= Diameter
428 self
.SetPen(Color
,"Solid",1)
429 self
.SetBrush(Color
,"Solid")
431 def SetPoints(self
,Points
):
433 self
.BoundingBox
= array(((min(self
.Points
[:,0]),min(self
.Points
[:,1])),(max(self
.Points
[:,0]),max(self
.Points
[:,1]))),Float
)
435 # It looks like this shouldn't be private
436 self
._Canvas
.BoundingBoxDirty
= 1
438 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
440 Points
= WorldToPixel(self
.Points
)
441 if self
.Diameter
<= 1:
442 dc
.DrawPointList(Points
)
443 elif self
.Diameter
<= 2:
444 # A Little optimization for a diameter2 - point
445 dc
.DrawPointList(Points
)
446 dc
.DrawPointList(Points
+ (1,0))
447 dc
.DrawPointList(Points
+ (0,1))
448 dc
.DrawPointList(Points
+ (1,1))
450 dc
.SetBrush(self
.Brush
)
451 radius
= int(round(self
.Diameter
/2))
453 dc
.DrawEllipse((x
- radius
), (y
- radius
), self
.Diameter
, self
.Diameter
)
457 class Dot(draw_object
):
459 The Dot class takes an x.y coordinate pair, and the Diameter of the circle.
460 The Diameter is in pixels, so it won't change with zoom.
462 Also Fill and line data
465 def __init__(self
,x
,y
,Diameter
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
= 0):
466 draw_object
.__init
__(self
,Foreground
)
470 self
.Diameter
= Diameter
471 # NOTE: the bounding box does not include the diameter of the dot, as that is in pixel coords.
472 # If this is a problem, perhaps you should use a circle, instead!
473 self
.BoundingBox
= array(((x
,y
),(x
,y
)),Float
)
475 self
.LineColor
= LineColor
476 self
.LineStyle
= LineStyle
477 self
.LineWidth
= LineWidth
478 self
.FillColor
= FillColor
479 self
.FillStyle
= FillStyle
481 self
.SetPen(LineColor
,LineStyle
,LineWidth
)
482 self
.SetBrush(FillColor
,FillStyle
)
484 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
486 dc
.SetBrush(self
.Brush
)
487 radius
= int(round(self
.Diameter
/2))
488 (X
,Y
) = WorldToPixel((self
.X
,self
.Y
))
489 dc
.DrawEllipse((X
- radius
), (Y
- radius
), self
.Diameter
, self
.Diameter
)
492 class Rectangle(draw_object
):
493 def __init__(self
,x
,y
,width
,height
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
= 0):
494 draw_object
.__init
__(self
,Foreground
)
500 self
.BoundingBox
= array(((x
,y
),(x
+width
,y
+height
)),Float
)
501 self
.LineColor
= LineColor
502 self
.LineStyle
= LineStyle
503 self
.LineWidth
= LineWidth
504 self
.FillColor
= FillColor
505 self
.FillStyle
= FillStyle
507 self
.SetPen(LineColor
,LineStyle
,LineWidth
)
508 self
.SetBrush(FillColor
,FillStyle
)
510 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
511 (X
,Y
) = WorldToPixel((self
.X
,self
.Y
))
512 (Width
,Height
) = ScaleFunction((self
.Width
,self
.Height
))
515 dc
.SetBrush(self
.Brush
)
516 dc
.DrawRectangle(X
,Y
, Width
,Height
)
518 class Ellipse(draw_object
):
519 def __init__(self
,x
,y
,width
,height
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
= 0):
520 draw_object
.__init
__(self
,Foreground
)
526 self
.BoundingBox
= array(((x
,y
),(x
+width
,y
+height
)),Float
)
527 self
.LineColor
= LineColor
528 self
.LineStyle
= LineStyle
529 self
.LineWidth
= LineWidth
530 self
.FillColor
= FillColor
531 self
.FillStyle
= FillStyle
533 self
.SetPen(LineColor
,LineStyle
,LineWidth
)
534 self
.SetBrush(FillColor
,FillStyle
)
536 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
537 (X
,Y
) = WorldToPixel((self
.X
,self
.Y
))
538 (Width
,Height
) = ScaleFunction((self
.Width
,self
.Height
))
541 dc
.SetBrush(self
.Brush
)
542 dc
.DrawEllipse(X
,Y
, Width
,Height
)
544 class Circle(draw_object
):
545 def __init__(self
,x
,y
,Diameter
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
= 0):
546 draw_object
.__init
__(self
,Foreground
)
550 self
.Diameter
= Diameter
551 self
.BoundingBox
= array(((x
-Diameter
/2,y
-Diameter
/2),(x
+Diameter
/2,y
+Diameter
/2)),Float
)
552 self
.LineColor
= LineColor
553 self
.LineStyle
= LineStyle
554 self
.LineWidth
= LineWidth
555 self
.FillColor
= FillColor
556 self
.FillStyle
= FillStyle
558 self
.SetPen(LineColor
,LineStyle
,LineWidth
)
559 self
.SetBrush(FillColor
,FillStyle
)
561 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
562 (X
,Y
) = WorldToPixel((self
.X
,self
.Y
))
563 (Diameter
,dummy
) = ScaleFunction((self
.Diameter
,self
.Diameter
))
566 dc
.SetBrush(self
.Brush
)
567 dc
.DrawEllipse(X
-Diameter
/2,Y
-Diameter
/2, Diameter
,Diameter
)
569 class Text(draw_object
):
572 This class creates a text object, placed at the coordinates,
573 x,y. the "Position" argument is a two charactor string, indicating
574 where in relation to the coordinates the string should be oriented.
576 The first letter is: t, c, or b, for top, center and bottom
577 The second letter is: l, c, or r, for left, center and right
579 I've provided arguments for Family, Style, and Weight of font, but
580 have not yet implimented them, so all text is: wx.SWISS, wx.NORMAL, wx.NORMAL.
581 I'd love it if someone would impliment that!
583 The size is fixed, and does not scale with the drawing.
587 def __init__(self
,String
,x
,y
,Size
,ForeGround
,BackGround
,Family
,Style
,Weight
,Underline
,Position
,Foreground
= 0):
588 draw_object
.__init
__(self
,Foreground
)
593 self
.ForeGround
= ForeGround
594 if BackGround
is None:
595 self
.BackGround
= None
597 self
.BackGround
= BackGround
601 self
.Underline
= Underline
602 self
.Position
= Position
603 # fixme: this should use the passed in parameters!
604 self
.Font
= wx
.Font(Size
, wx
.MODERN
, wx
.NORMAL
, wx
.NORMAL
, Underline
, )
606 self
.BoundingBox
= array(((x
,y
),(x
,y
)),Float
)
613 def _Draw(self
,dc
,WorldToPixel
,ScaleFunction
):
614 (X
,Y
) = WorldToPixel((self
.X
,self
.Y
))
615 dc
.SetFont(self
.Font
)
616 dc
.SetTextForeground(self
.ForeGround
)
618 dc
.SetBackgroundMode(wx
.SOLID
)
619 dc
.SetTextBackground(self
.BackGround
)
621 dc
.SetBackgroundMode(wx
.TRANSPARENT
)
623 # compute the shift, and adjust the coordinates, if neccesary
624 # This had to be put in here, becsuse there is no wx.DC during __init__
625 if self
.x_shift
is None or self
.y_shift
is None:
626 if self
.Position
== 'tl':
627 x_shift
,y_shift
= 0,0
629 (w
,h
) = dc
.GetTextExtent(self
.String
)
630 if self
.Position
[0] == 't':
632 elif self
.Position
[0] == 'c':
634 elif self
.Position
[0] == 'b':
637 ##fixme: this should be a real derived exception
638 raise "Invalid value for Text Object Position Attribute"
639 if self
.Position
[1] == 'l':
641 elif self
.Position
[1] == 'c':
643 elif self
.Position
[1] == 'r':
646 ##fixme: this should be a real derived exception
647 raise "Invalid value for Text Object Position Attribute"
648 self
.x_shift
= x_shift
649 self
.y_shift
= y_shift
650 dc
.DrawText(self
.String
, X
-self
.x_shift
, Y
-self
.y_shift
)
653 #---------------------------------------------------------------------------
655 class FloatCanvas(wx
.Panel
):
659 This is a high level window for drawing maps and anything else in an
660 arbitrary coordinate system.
662 The goal is to provide a convenient way to draw stuff on the screen
663 without having to deal with handling OnPaint events, converting to pixel
664 coordinates, knowing about wxWindows brushes, pens, and colors, etc. It
665 also provides virtually unlimited zooming and scrolling
667 I am using it for two things:
668 1) general purpose drawing in floating point coordinates
669 2) displaying map data in Lat-long coordinates
671 If the projection is set to None, it will draw in general purpose
672 floating point coordinates. If the projection is set to 'FlatEarth', it
673 will draw a FlatEarth projection, centered on the part of the map that
674 you are viewing. You can also pass in your own projection function.
676 It is double buffered, so re-draws after the window is uncovered by something
679 It relies on NumPy, which is needed for speed
681 Bugs and Limitations:
682 Lots: patches, fixes welcome
684 For Map drawing: It ignores the fact that the world is, in fact, a
685 sphere, so it will do strange things if you are looking at stuff near
686 the poles or the date line. so far I don't have a need to do that, so I
687 havn't bothered to add any checks for that yet.
690 I have set no zoom limits. What this means is that if you zoom in really
691 far, you can get integer overflows, and get wierd results. It
692 doesn't seem to actually cause any problems other than wierd output, at
693 least when I have run it.
696 I have done a couple of things to improve speed in this app. The one
697 thing I have done is used NumPy Arrays to store the coordinates of the
698 points of the objects. This allowed me to use array oriented functions
699 when doing transformations, and should provide some speed improvement
700 for objects with a lot of points (big polygons, polylines, pointsets).
702 The real slowdown comes when you have to draw a lot of objects, because
703 you have to call the wx.DC.DrawSomething call each time. This is plenty
704 fast for tens of objects, OK for hundreds of objects, but pretty darn
705 slow for thousands of objects.
707 The solution is to be able to pass some sort of object set to the DC
708 directly. I've used DC.DrawPointList(Points), and it helped a lot with
709 drawing lots of points. I havn't got a LineSet type object, so I havn't
710 used DC.DrawLineList yet. I'd like to get a full set of DrawStuffList()
711 methods implimented, and then I'd also have a full set of Object sets
712 that could take advantage of them. I hope to get to it some day.
714 Copyright: Christopher Barker
716 License: Same as wxPython
718 Please let me know if you're using this!!!
722 Chris.Barker@noaa.gov
726 def __init__(self
, parent
, id = -1,
727 size
= wx
.DefaultSize
,
728 ProjectionFun
= None,
729 BackgroundColor
= "WHITE",
731 EnclosingFrame
= None,
736 wx
.Panel
.__init
__( self
, parent
, id, wx
.DefaultPosition
, size
)
738 if ProjectionFun
== 'FlatEarth':
739 self
.ProjectionFun
= self
.FlatEarthProjection
740 elif type(ProjectionFun
) == types
.FunctionType
:
741 self
.ProjectionFun
= ProjectionFun
742 elif ProjectionFun
is None:
743 self
.ProjectionFun
= lambda x
=None: array( (1,1), Float
)
745 raise('Projectionfun must be either: "FlatEarth", None, or a function that takes the ViewPortCenter and returns a MapProjectionVector')
747 self
.UseBackground
= UseBackground
748 self
.UseHitTest
= UseHitTest
750 self
.NumBetweenBlits
= 40
752 ## you can have a toolbar with buttons for zoom-in, zoom-out and
753 ## move. If you don't use the toolbar, you should provide your
754 ## own way of navigating the canvas
756 ## Create the vertical sizer for the toolbar and Panel
757 box
= wx
.BoxSizer(wx
.VERTICAL
)
758 box
.Add(self
.BuildToolbar(), 0, wx
.ALL | wx
.ALIGN_LEFT | wx
.GROW
, 4)
760 self
.DrawPanel
= wx
.Window(self
,-1,wx
.DefaultPosition
,wx
.DefaultSize
,wx
.SUNKEN_BORDER
)
761 box
.Add(self
.DrawPanel
,1,wx
.GROW
)
764 self
.SetAutoLayout(True)
767 self
.DrawPanel
= self
769 self
.DrawPanel
.BackgroundBrush
= wx
.Brush(BackgroundColor
,wx
.SOLID
)
773 self
.EnclosingFrame
= EnclosingFrame
775 wx
.EVT_PAINT(self
.DrawPanel
, self
.OnPaint
)
776 wx
.EVT_SIZE(self
.DrawPanel
, self
.OnSize
)
778 wx
.EVT_LEFT_DOWN(self
.DrawPanel
, self
.LeftButtonEvent
)
779 wx
.EVT_LEFT_UP(self
.DrawPanel
, self
.LeftButtonEvent
)
780 wx
.EVT_RIGHT_DOWN(self
.DrawPanel
, self
.RightButtonEvent
)
781 wx
.EVT_MOTION(self
.DrawPanel
, self
.LeftButtonEvent
)
785 if self
.UseBackground
:
786 self
._TopDrawList
= []
787 self
.BoundingBox
= None
788 self
.BoundingBoxDirty
= 0
789 self
.ViewPortCenter
= array( (0,0), Float
)
791 self
.MapProjectionVector
= array( (1,1), Float
) # No Projection to start!
792 self
.TransformVector
= array( (1,-1), Float
) # default Transformation
797 self
.StartRBBox
= None
798 self
.PrevRBBox
= None
799 self
.StartMove
= None
800 self
.PrevMoveBox
= None
801 # called just to make sure everything is initialized
802 if wx
.Platform
!= "__WXMAC__":
806 def BuildToolbar(self
):
807 tb
= wx
.ToolBar(self
,-1)
810 tb
.SetToolBitmapSize((23,23))
812 tb
.AddTool(ID_ZOOM_IN_BUTTON
, GetPlusBitmap(), isToggle
=True,shortHelpString
= "Zoom In")
813 wx
.EVT_TOOL(self
, ID_ZOOM_IN_BUTTON
, self
.SetMode
)
815 tb
.AddTool(ID_ZOOM_OUT_BUTTON
, GetMinusBitmap(), isToggle
=True,shortHelpString
= "Zoom Out")
816 wx
.EVT_TOOL(self
, ID_ZOOM_OUT_BUTTON
, self
.SetMode
)
818 tb
.AddTool(ID_MOVE_MODE_BUTTON
, GetHandBitmap(), isToggle
=True,shortHelpString
= "Move")
819 wx
.EVT_TOOL(self
, ID_MOVE_MODE_BUTTON
, self
.SetMode
)
823 tb
.AddControl(wx
.Button(tb
, ID_ZOOM_TO_FIT_BUTTON
, "Zoom To Fit",wx
.DefaultPosition
, wx
.DefaultSize
))
824 wx
.EVT_BUTTON(self
, ID_ZOOM_TO_FIT_BUTTON
, self
.ZoomToFit
)
829 def SetMode(self
,event
):
830 for id in [ID_ZOOM_IN_BUTTON
,ID_ZOOM_OUT_BUTTON
,ID_MOVE_MODE_BUTTON
]:
831 self
.ToolBar
.ToggleTool(id,0)
832 self
.ToolBar
.ToggleTool(event
.GetId(),1)
833 if event
.GetId() == ID_ZOOM_IN_BUTTON
:
834 self
.SetGUIMode("ZoomIn")
835 elif event
.GetId() == ID_ZOOM_OUT_BUTTON
:
836 self
.SetGUIMode("ZoomOut")
837 elif event
.GetId() == ID_MOVE_MODE_BUTTON
:
838 self
.SetGUIMode("Move")
841 def SetGUIMode(self
,Mode
):
842 if Mode
in ["ZoomIn","ZoomOut","Move",None]:
845 raise "Not a valid Mode"
847 def FlatEarthProjection(self
,CenterPoint
):
848 return array((cos(pi
*CenterPoint
[1]/180),1),Float
)
850 def LeftButtonEvent(self
,event
):
851 if self
.EnclosingFrame
:
853 position
= self
.PixelToWorld((event
.GetX(),event
.GetY()))
854 self
.EnclosingFrame
.SetStatusText("%8.3f, %8.3f"%tuple(position
))
856 if self
.GUIMode
== "ZoomIn":
858 self
.StartRBBox
= (event
.GetX(),event
.GetY())
859 self
.PrevRBBox
= None
860 elif event
.Dragging() and event
.LeftIsDown() and self
.StartRBBox
:
861 x0
,y0
= self
.StartRBBox
862 x1
,y1
= event
.GetX(),event
.GetY()
863 w
, h
= abs(x1
-x0
),abs(y1
-y0
)
864 w
= max(w
,int(h
*self
.AspectRatio
))
865 h
= int(w
/self
.AspectRatio
)
866 x_c
, y_c
= (x0
+x1
)/2 , (y0
+y1
)/2
867 dc
= wx
.ClientDC(self
.DrawPanel
)
869 dc
.SetPen(wx
.Pen('WHITE', 2,wx
.SHORT_DASH
))
870 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
871 dc
.SetLogicalFunction(wx
.XOR
)
873 dc
.DrawRectangle(*self
.PrevRBBox
)
874 dc
.DrawRectangle(x_c
-w
/2,y_c
-h
/2,w
,h
)
875 self
.PrevRBBox
= (x_c
-w
/2,y_c
-h
/2,w
,h
)
878 elif event
.LeftUp() and self
.StartRBBox
:
879 self
.PrevRBBox
= None
880 EndRBBox
= (event
.GetX(),event
.GetY())
881 StartRBBox
= self
.StartRBBox
882 # if mouse has moved less that ten pixels, don't use the box.
883 if abs(StartRBBox
[0] - EndRBBox
[0]) > 10 and abs(StartRBBox
[1] - EndRBBox
[1]) > 10:
884 EndRBBox
= self
.PixelToWorld(EndRBBox
)
885 StartRBBox
= self
.PixelToWorld(StartRBBox
)
886 BB
= array(((min(EndRBBox
[0],StartRBBox
[0]), min(EndRBBox
[1],StartRBBox
[1])),
887 (max(EndRBBox
[0],StartRBBox
[0]), max(EndRBBox
[1],StartRBBox
[1]))),Float
)
890 Center
= self
.PixelToWorld(StartRBBox
)
891 self
.Zoom(1.5,Center
)
892 self
.StartRBBox
= None
894 if self
.GUIMode
== "ZoomOut":
896 Center
= self
.PixelToWorld((event
.GetX(),event
.GetY()))
897 self
.Zoom(1/1.5,Center
)
898 elif self
.GUIMode
== "Move":
900 self
.StartMove
= array((event
.GetX(),event
.GetY()))
901 self
.PrevMoveBox
= None
902 elif event
.Dragging() and event
.LeftIsDown() and self
.StartMove
:
903 x_1
,y_1
= event
.GetX(),event
.GetY()
904 w
, h
= self
.PanelSize
905 x_tl
, y_tl
= x_1
- self
.StartMove
[0], y_1
- self
.StartMove
[1]
906 dc
= wx
.ClientDC(self
.DrawPanel
)
908 dc
.SetPen(wx
.Pen('WHITE', 1,))
909 dc
.SetBrush(wx
.TRANSPARENT_BRUSH
)
910 dc
.SetLogicalFunction(wx
.XOR
)
912 dc
.DrawRectangle(*self
.PrevMoveBox
)
913 dc
.DrawRectangle(x_tl
,y_tl
,w
,h
)
914 self
.PrevMoveBox
= (x_tl
,y_tl
,w
,h
)
917 elif event
.LeftUp() and self
.StartMove
:
918 self
.PrevMoveBox
= None
919 StartMove
= self
.StartMove
920 EndMove
= array((event
.GetX(),event
.GetY()))
921 if sum((StartMove
-EndMove
)**2) > 16:
922 self
.Move(StartMove
-EndMove
,'Pixel')
923 self
.StartMove
= None
925 def RightButtonEvent(self
,event
):
927 if self
.GUIMode
== "ZoomIn":
928 Center
= self
.PixelToWorld((event
.GetX(),event
.GetY()))
929 self
.Zoom(1/1.5,Center
)
930 elif self
.GUIMode
== "ZoomOut":
931 Center
= self
.PixelToWorld((event
.GetX(),event
.GetY()))
932 self
.Zoom(1.5,Center
)
937 def MakeNewBuffers(self
):
938 # Make new offscreen bitmap:
939 self
._Buffer
= wx
.EmptyBitmap(self
.PanelSize
[0],self
.PanelSize
[1])
940 if self
.UseBackground
:
941 self
._BackBuffer
= wx
.EmptyBitmap(self
.PanelSize
[0],self
.PanelSize
[1])
942 self
._BackgroundDirty
= 1
946 def OnSize(self
,event
):
947 self
.PanelSize
= array(self
.DrawPanel
.GetClientSizeTuple(),Int32
)
949 self
.AspectRatio
= self
.PanelSize
[0]/self
.PanelSize
[1]
950 except ZeroDivisionError:
951 self
.AspectRatio
= 1.0
952 self
.MakeNewBuffers()
955 def OnPaint(self
, event
):
956 #dc = wx.BufferedPaintDC(self.DrawPanel, self._Buffer)
957 dc
= wx
.PaintDC(self
.DrawPanel
)
958 dc
.DrawBitmap(self
._Buffer
, 0,0)
962 The Draw method gets pretty complicated because of all the buffers
964 There is a main buffer set up to double buffer the screen, so
965 you can get quick re-draws when the window gets uncovered.
967 If self.UseBackground is set, and an object is set up with the
968 "ForeGround" flag, then it gets drawn to the screen after blitting
969 the background. This is done so that you can have a complicated
970 background, but have something changing on the foreground,
971 without having to wait for the background to get re-drawn. This
972 can be used to support simple animation, for instance.
975 if self
.Debug
: start
= clock()
976 ScreenDC
= wx
.ClientDC(self
.DrawPanel
)
977 ViewPortWorld
= ( self
.PixelToWorld((0,0)), self
.PixelToWorld(self
.PanelSize
) )
978 ViewPortBB
= array( ( minimum
.reduce(ViewPortWorld
), maximum
.reduce(ViewPortWorld
) ) )
979 if self
.UseBackground
:
981 dc
.SelectObject(self
._BackBuffer
)
982 dc
.SetBackground(self
.DrawPanel
.BackgroundBrush
)
984 if self
._BackgroundDirty
:
988 for Object
in self
._DrawList
:
989 if self
.BBCheck(Object
.BoundingBox
,ViewPortBB
):
990 #print "object is in Bounding Box"
992 Object
._Draw
(dc
,self
.WorldToPixel
,self
.ScaleFunction
)
993 if i
% self
.NumBetweenBlits
== 0:
995 ScreenDC
.Blit(0, 0, w
,h
, dc
, 0, 0)
999 self
._BackgroundDirty
= 0
1000 dc
.SelectObject(self
._Buffer
)
1002 ##Draw Background on Main Buffer:
1003 dc
.DrawBitmap(self
._BackBuffer
,0,0)
1004 #Draw the OnTop stuff
1006 for Object
in self
._TopDrawList
:
1008 Object
._Draw
(dc
,self
.WorldToPixel
,self
.ScaleFunction
)
1009 if i
% self
.NumBetweenBlits
== 0:
1010 w
, h
= self
.PanelSize
1011 ScreenDC
.Blit(0, 0, w
,h
, dc
, 0, 0)
1013 else: # not using a Background DC
1015 dc
.SelectObject(self
._Buffer
)
1016 dc
.SetBackground(self
.DrawPanel
.BackgroundBrush
)
1021 for Object
in self
._DrawList
:
1022 if self
.BBCheck(Object
.BoundingBox
,ViewPortBB
):
1023 #print "object is in Bounding Box"
1025 Object
._Draw
(dc
,self
.WorldToPixel
,self
.ScaleFunction
)
1026 if i
% self
.NumBetweenBlits
== 0:
1027 w
, h
= self
.PanelSize
1028 ScreenDC
.Blit(0, 0, w
, h
, dc
, 0, 0)
1032 # now refresh the screen
1033 #ScreenDC.DrawBitmap(self._Buffer,0,0) #NOTE: uisng DrawBitmap didn't work right on MSW
1034 w
, h
= self
.PanelSize
1035 ScreenDC
.Blit(0, 0, w
, h
, dc
, 0, 0)
1037 # If the canvas is in the middle of a zoom or move, the Rubber Band box needs to be re-drawn
1039 ScreenDC
.SetPen(wx
.Pen('WHITE', 2,wx
.SHORT_DASH
))
1040 ScreenDC
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1041 ScreenDC
.SetLogicalFunction(wx
.XOR
)
1042 ScreenDC
.DrawRectangle(*self
.PrevRBBox
)
1043 elif self
.PrevMoveBox
:
1044 ScreenDC
.SetPen(wx
.Pen('WHITE', 1,))
1045 ScreenDC
.SetBrush(wx
.TRANSPARENT_BRUSH
)
1046 ScreenDC
.SetLogicalFunction(wx
.XOR
)
1047 ScreenDC
.DrawRectangle(*self
.PrevMoveBox
)
1048 if self
.Debug
: print "Drawing took %f seconds of CPU time"%(clock()-start
)
1050 def BBCheck(self
, BB1
, BB2
):
1053 BBCheck(BB1, BB2) returns True is the Bounding boxes intesect, False otherwise
1056 if ( (BB1
[1,0] > BB2
[0,0]) and (BB1
[0,0] < BB2
[1,0]) and
1057 (BB1
[1,1] > BB2
[0,1]) and (BB1
[0,1] < BB2
[1,1]) ):
1062 def Move(self
,shift
,CoordType
):
1064 move the image in the window.
1066 shift is an (x,y) tuple, specifying the amount to shift in each direction
1068 It can be in any of three coordinates: Panel, Pixel, World,
1069 specified by the CoordType parameter
1071 Panel coordinates means you want to shift the image by some
1072 fraction of the size of the displaed image
1074 Pixel coordinates means you want to shift the image by some number of pixels
1076 World coordinates meand you want to shift the image by an amount
1077 in Floating point world coordinates
1081 shift
= array(shift
,Float
)
1082 if CoordType
== 'Panel':# convert from panel coordinates
1083 shift
= shift
* array((-1,1),Float
) *self
.PanelSize
/self
.TransformVector
1084 elif CoordType
== 'Pixel': # convert from pixel coordinates
1085 shift
= shift
/self
.TransformVector
1086 elif CoordType
== 'World': # No conversion
1089 raise 'CoordType must be either "Panel", "Pixel", or "World"'
1091 self
.ViewPortCenter
= self
.ViewPortCenter
+ shift
1092 self
.MapProjectionVector
= self
.ProjectionFun(self
.ViewPortCenter
)
1093 self
.TransformVector
= array((self
.Scale
,-self
.Scale
),Float
)* self
.MapProjectionVector
1094 self
._BackgroundDirty
= 1
1097 def Zoom(self
,factor
,center
= None):
1100 Zoom(factor, center) changes the amount of zoom of the image by factor.
1101 If factor is greater than one, the image gets larger.
1102 If factor is less than one, the image gets smaller.
1104 Center is a tuple of (x,y) coordinates of the center of the viewport, after zooming.
1105 If center is not given, the center will stay the same.
1108 self
.Scale
= self
.Scale
*factor
1110 self
.ViewPortCenter
= array(center
,Float
)
1111 self
.MapProjectionVector
= self
.ProjectionFun(self
.ViewPortCenter
)
1112 self
.TransformVector
= array((self
.Scale
,-self
.Scale
),Float
)* self
.MapProjectionVector
1113 self
._BackgroundDirty
= 1
1116 def ZoomToFit(self
,event
):
1119 def ZoomToBB(self
,NewBB
= None,DrawFlag
= 1):
1123 Zooms the image to the bounding box given, or to the bounding
1124 box of all the objects on the canvas, if none is given.
1131 if self
.BoundingBoxDirty
:
1132 self
._ResetBoundingBox
()
1133 BoundingBox
= self
.BoundingBox
1135 self
.ViewPortCenter
= array(((BoundingBox
[0,0]+BoundingBox
[1,0])/2,
1136 (BoundingBox
[0,1]+BoundingBox
[1,1])/2 ),Float
)
1137 self
.MapProjectionVector
= self
.ProjectionFun(self
.ViewPortCenter
)
1138 # Compute the new Scale
1139 BoundingBox
= BoundingBox
* self
.MapProjectionVector
1141 self
.Scale
= min((self
.PanelSize
[0] / (BoundingBox
[1,0]-BoundingBox
[0,0])),
1142 (self
.PanelSize
[1] / (BoundingBox
[1,1]-BoundingBox
[0,1])))*0.95
1143 except ZeroDivisionError: # this will happen if the BB has zero width or height
1145 self
.Scale
= (self
.PanelSize
[0] / (BoundingBox
[1,0]-BoundingBox
[0,0]))*0.95
1146 except ZeroDivisionError:
1148 self
.Scale
= (self
.PanelSize
[1] / (BoundingBox
[1,1]-BoundingBox
[0,1]))*0.95
1149 except ZeroDivisionError: #zero size! (must be a single point)
1152 self
.TransformVector
= array((self
.Scale
,-self
.Scale
),Float
)* self
.MapProjectionVector
1154 self
._BackgroundDirty
= 1
1157 def RemoveObjects(self
,Objects
):
1158 for Object
in Objects
:
1159 self
.RemoveObject(Object
,ResetBB
= 0)
1160 self
.BoundingBoxDirty
= 1
1162 def RemoveObject(self
,Object
,ResetBB
= 1):
1163 if Object
.Foreground
:
1164 self
._TopDrawList
.remove(Object
)
1166 self
._DrawList
.remove(Object
)
1167 self
._BackgroundDirty
= 1
1170 self
.BoundingBoxDirty
= 1
1172 def Clear(self
, ResetBB
= True):
1174 if self
.UseBackground
:
1175 self
._TopDrawList
= []
1176 self
._BackgroundDirty
= 1
1178 self
._ResetBoundingBox
()
1180 def _AddBoundingBox(self
,NewBB
):
1181 if self
.BoundingBox
is None:
1182 self
.BoundingBox
= NewBB
1183 self
.ZoomToBB(NewBB
,DrawFlag
= 0)
1185 self
.BoundingBox
= array(((min(self
.BoundingBox
[0,0],NewBB
[0,0]),
1186 min(self
.BoundingBox
[0,1],NewBB
[0,1])),
1187 (max(self
.BoundingBox
[1,0],NewBB
[1,0]),
1188 max(self
.BoundingBox
[1,1],NewBB
[1,1]))),Float
)
1189 def _ResetBoundingBox(self
):
1190 # NOTE: could you remove an item without recomputing the entire bounding box?
1191 self
.BoundingBox
= None
1193 self
.BoundingBox
= self
._DrawList
[0].BoundingBox
1194 for Object
in self
._DrawList
[1:]:
1195 self
._AddBoundingBox
(Object
.BoundingBox
)
1196 if self
.UseBackground
:
1197 for Object
in self
._TopDrawList
:
1198 self
._AddBoundingBox
(Object
.BoundingBox
)
1199 if self
.BoundingBox
is None:
1200 self
.ViewPortCenter
= array( (0,0), Float
)
1201 self
.TransformVector
= array( (1,-1), Float
)
1202 self
.MapProjectionVector
= array( (1,1), Float
)
1204 self
.BoundingBoxDirty
= 0
1206 def PixelToWorld(self
,Points
):
1208 Converts coordinates from Pixel coordinates to world coordinates.
1210 Points is a tuple of (x,y) coordinates, or a list of such tuples, or a NX2 Numpy array of x,y coordinates.
1213 return (((array(Points
,Float
) - (self
.PanelSize
/2))/self
.TransformVector
) + self
.ViewPortCenter
)
1215 def WorldToPixel(self
,Coordinates
):
1217 This function will get passed to the drawing functions of the objects,
1218 to transform from world to pixel coordinates.
1219 Coordinates should be a NX2 array of (x,y) coordinates, or
1220 a 2-tuple, or sequence of 2-tuples.
1222 return (((array(Coordinates
,Float
) - self
.ViewPortCenter
)*self
.TransformVector
)+(self
.PanelSize
/2)).astype('i')
1224 def ScaleFunction(self
,Lengths
):
1226 This function will get passed to the drawing functions of the objects,
1227 to Change a length from world to pixel coordinates.
1229 Lengths should be a NX2 array of (x,y) coordinates, or
1230 a 2-tuple, or sequence of 2-tuples.
1232 return (array(Lengths
,Float
)*self
.TransformVector
).astype('i')
1235 ## This is a set of methods that add objects to the Canvas. It kind
1236 ## of seems like a lot of duplication, but I wanted to be able to
1237 ## instantiate the draw objects separatley form adding them, but
1238 ## also to be able to do add one oin one step. I'm open to better
1240 def AddRectangle(self
,x
,y
,width
,height
,
1241 LineColor
= "Black",
1242 LineStyle
= "Solid",
1245 FillStyle
= "Solid",
1247 Object
= Rectangle(x
,y
,width
,height
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
)
1248 self
.AddObject(Object
)
1251 def AddEllipse(self
,x
,y
,width
,height
,
1252 LineColor
= "Black",
1253 LineStyle
= "Solid",
1256 FillStyle
= "Solid",
1258 Object
= Ellipse(x
,y
,width
,height
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
)
1259 self
.AddObject(Object
)
1262 def AddCircle(self
,x
,y
,Diameter
,
1263 LineColor
= "Black",
1264 LineStyle
= "Solid",
1267 FillStyle
= "Solid",
1269 Object
= Circle(x
,y
,Diameter
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
)
1270 self
.AddObject(Object
)
1273 def AddDot(self
,x
,y
,Diameter
,
1274 LineColor
= "Black",
1275 LineStyle
= "Solid",
1278 FillStyle
= "Solid",
1280 Object
= Dot(x
,y
,Diameter
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
)
1281 self
.AddObject(Object
)
1284 def AddPolygon(self
,Points
,
1285 LineColor
= "Black",
1286 LineStyle
= "Solid",
1289 FillStyle
= "Solid",
1292 Object
= Polygon(Points
,LineColor
,LineStyle
,LineWidth
,FillColor
,FillStyle
,Foreground
)
1293 self
.AddObject(Object
)
1296 def AddLine(self
,Points
,
1297 LineColor
= "Black",
1298 LineStyle
= "Solid",
1302 Object
= Line(Points
,LineColor
,LineStyle
,LineWidth
,Foreground
)
1303 self
.AddObject(Object
)
1306 def AddLineSet(self
,Points
,
1307 LineColors
= "Black",
1308 LineStyles
= "Solid",
1312 Object
= LineSet(Points
,LineColors
,LineStyles
,LineWidths
,Foreground
)
1313 self
.AddObject(Object
)
1316 def AddPointSet(self
,Points
,
1321 Object
= PointSet(Points
,Color
,Diameter
,Foreground
)
1322 self
.AddObject(Object
)
1325 def AddText(self
,String
,x
,y
,
1327 ForeGround
= 'Black',
1335 Object
= Text(String
,x
,y
,Size
,ForeGround
,BackGround
,Family
,Style
,Weight
,Underline
,Position
,Foreground
)
1336 self
.AddObject(Object
)
1339 def AddObject(self
,obj
):
1340 # put in a reference to the Canvas, so remove and other stuff can work
1342 if obj
.Foreground
and self
.UseBackground
:
1343 self
._TopDrawList
.append(obj
)
1345 self
._DrawList
.append(obj
)
1346 self
._backgrounddirty
= 1
1347 self
._AddBoundingBox
(obj
.BoundingBox
)