1#!/usr/bin/env python 2 3import wx 4import wx.adv 5import images 6import random 7 8#--------------------------------------------------------------------------- 9 10W = 2000 11H = 2000 12SW = 150 13SH = 150 14SHAPE_COUNT = 750 15hitradius = 5 16 17#--------------------------------------------------------------------------- 18 19colours = [ 20 "BLACK", 21 "BLUE", 22 "BLUE VIOLET", 23 "BROWN", 24 "CYAN", 25 "DARK GREY", 26 "DARK GREEN", 27 "GOLD", 28 "GREY", 29 "GREEN", 30 "MAGENTA", 31 "NAVY", 32 "PINK", 33 "RED", 34 "SKY BLUE", 35 "VIOLET", 36 "YELLOW", 37 ] 38 39 40 41class MyCanvas(wx.ScrolledWindow): 42 def __init__(self, parent, id, log, size = wx.DefaultSize): 43 wx.ScrolledWindow.__init__(self, parent, id, (0, 0), size=size, style=wx.SUNKEN_BORDER) 44 45 self.lines = [] 46 self.maxWidth = W 47 self.maxHeight = H 48 self.x = self.y = 0 49 self.curLine = [] 50 self.drawing = False 51 52 self.SetBackgroundColour("WHITE") 53 bmp = images.Test2.GetBitmap() 54 mask = wx.Mask(bmp, wx.BLUE) 55 bmp.SetMask(mask) 56 self.bmp = bmp 57 58 self.SetVirtualSize((self.maxWidth, self.maxHeight)) 59 self.SetScrollRate(20,20) 60 61 # create a PseudoDC to record our drawing 62 self.pdc = wx.adv.PseudoDC() 63 self.pen_cache = {} 64 self.brush_cache = {} 65 self.DoDrawing(self.pdc) 66 log.write('Created PseudoDC draw list with %d operations!'%self.pdc.GetLen()) 67 68 self.Bind(wx.EVT_PAINT, self.OnPaint) 69 self.Bind(wx.EVT_ERASE_BACKGROUND, lambda x:None) 70 self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouse) 71 72 # vars for handling mouse clicks 73 self.dragid = -1 74 self.lastpos = (0,0) 75 76 def ConvertEventCoords(self, event): 77 xView, yView = self.GetViewStart() 78 xDelta, yDelta = self.GetScrollPixelsPerUnit() 79 return (event.GetX() + (xView * xDelta), 80 event.GetY() + (yView * yDelta)) 81 82 def OffsetRect(self, r): 83 xView, yView = self.GetViewStart() 84 xDelta, yDelta = self.GetScrollPixelsPerUnit() 85 r.Offset(-(xView*xDelta),-(yView*yDelta)) 86 87 def OnMouse(self, event): 88 global hitradius 89 if event.LeftDown(): 90 x,y = self.ConvertEventCoords(event) 91 #l = self.pdc.FindObjectsByBBox(x, y) 92 l = self.pdc.FindObjects(x, y, hitradius) 93 for id in l: 94 if not self.pdc.GetIdGreyedOut(id): 95 self.dragid = id 96 self.lastpos = (event.GetX(),event.GetY()) 97 break 98 elif event.RightDown(): 99 x,y = self.ConvertEventCoords(event) 100 #l = self.pdc.FindObjectsByBBox(x, y) 101 l = self.pdc.FindObjects(x, y, hitradius) 102 if l: 103 self.pdc.SetIdGreyedOut(l[0], not self.pdc.GetIdGreyedOut(l[0])) 104 r = self.pdc.GetIdBounds(l[0]) 105 r.Inflate(4,4) 106 self.OffsetRect(r) 107 self.RefreshRect(r, False) 108 elif event.Dragging() or event.LeftUp(): 109 if self.dragid != -1: 110 x,y = self.lastpos 111 dx = event.GetX() - x 112 dy = event.GetY() - y 113 r = self.pdc.GetIdBounds(self.dragid) 114 self.pdc.TranslateId(self.dragid, dx, dy) 115 r2 = self.pdc.GetIdBounds(self.dragid) 116 r = r.Union(r2) 117 r.Inflate(4,4) 118 self.OffsetRect(r) 119 self.RefreshRect(r, False) 120 self.lastpos = (event.GetX(),event.GetY()) 121 if event.LeftUp(): 122 self.dragid = -1 123 124 def RandomPen(self): 125 c = random.choice(colours) 126 t = random.randint(1, 4) 127 if (c, t) not in self.pen_cache: 128 self.pen_cache[(c, t)] = wx.Pen(c, t) 129 return self.pen_cache[(c, t)] 130 131 132 def RandomBrush(self): 133 c = random.choice(colours) 134 if c not in self.brush_cache: 135 self.brush_cache[c] = wx.Brush(c) 136 137 return self.brush_cache[c] 138 139 def RandomColor(self): 140 return random.choice(colours) 141 142 143 def OnPaint(self, event): 144 # Create a buffered paint DC. It will create the real 145 # wx.PaintDC and then blit the bitmap to it when dc is 146 # deleted. 147 dc = wx.BufferedPaintDC(self) 148 149 # we need to clear the dc BEFORE calling PrepareDC 150 bg = wx.Brush(self.GetBackgroundColour()) 151 dc.SetBackground(bg) 152 dc.Clear() 153 154 # use PrepareDC to set position correctly 155 self.PrepareDC(dc) 156 157 # create a clipping rect from our position and size 158 # and the Update Region 159 xv, yv = self.GetViewStart() 160 dx, dy = self.GetScrollPixelsPerUnit() 161 x, y = (xv * dx, yv * dy) 162 rgn = self.GetUpdateRegion() 163 rgn.Offset(x,y) 164 r = rgn.GetBox() 165 166 # Draw the saved drawing operations to the dc using the calculated 167 # clipping rect 168 self.pdc.DrawToDCClipped(dc,r) 169 170 171 def DoDrawing(self, dc): 172 random.seed() 173 self.objids = [] 174 self.boundsdict = {} 175 for i in range(SHAPE_COUNT): 176 id = wx.NewIdRef() 177 dc.SetId(id) 178 choice = random.randint(0,8) 179 if choice in (0,1): 180 x = random.randint(0, W) 181 y = random.randint(0, H) 182 pen = self.RandomPen() 183 dc.SetPen(pen) 184 dc.DrawPoint(x,y) 185 r = wx.Rect(x,y,1,1) 186 r.Inflate(pen.GetWidth(),pen.GetWidth()) 187 dc.SetIdBounds(id,r) 188 elif choice in (2,3): 189 x1 = random.randint(0, W-SW) 190 y1 = random.randint(0, H-SH) 191 x2 = random.randint(x1, x1+SW) 192 y2 = random.randint(y1, y1+SH) 193 pen = self.RandomPen() 194 dc.SetPen(pen) 195 dc.DrawLine(x1,y1,x2,y2) 196 r = wx.Rect(x1,y1,x2-x1,y2-y1) 197 r.Inflate(pen.GetWidth(),pen.GetWidth()) 198 dc.SetIdBounds(id,r) 199 elif choice in (4,5): 200 w = random.randint(10, SW) 201 h = random.randint(10, SH) 202 x = random.randint(0, W - w) 203 y = random.randint(0, H - h) 204 pen = self.RandomPen() 205 dc.SetPen(pen) 206 dc.SetBrush(self.RandomBrush()) 207 dc.DrawRectangle(x,y,w,h) 208 r = wx.Rect(x,y,w,h) 209 r.Inflate(pen.GetWidth(),pen.GetWidth()) 210 dc.SetIdBounds(id,r) 211 self.objids.append(id) 212 elif choice == 6: 213 Np = 8 # number of characters in text 214 word = [] 215 for i in range(Np): 216 c = chr( random.randint(48, 122) ) 217 word.append( c ) 218 word = "".join(word) 219 w,h = self.GetFullTextExtent(word)[0:2] 220 x = random.randint(0, W-w) 221 y = random.randint(0, H-h) 222 dc.SetFont(self.GetFont()) 223 dc.SetTextForeground(self.RandomColor()) 224 dc.SetTextBackground(self.RandomColor()) 225 dc.DrawText(word, x, y) 226 r = wx.Rect(x,y,w,h) 227 r.Inflate(2,2) 228 dc.SetIdBounds(id, r) 229 self.objids.append(id) 230 elif choice == 7: 231 Np = 8 # number of points per polygon 232 poly = [] 233 minx = SW 234 miny = SH 235 maxx = 0 236 maxy = 0 237 for i in range(Np): 238 x = random.randint(0, SW) 239 y = random.randint(0, SH) 240 if x < minx: minx = x 241 if x > maxx: maxx = x 242 if y < miny: miny = y 243 if y > maxy: maxy = y 244 poly.append(wx.Point(x,y)) 245 x = random.randint(0, W-SW) 246 y = random.randint(0, H-SH) 247 pen = self.RandomPen() 248 dc.SetPen(pen) 249 dc.SetBrush(self.RandomBrush()) 250 dc.DrawPolygon(poly, x,y) 251 r = wx.Rect(minx+x,miny+y,maxx-minx,maxy-miny) 252 r.Inflate(pen.GetWidth(),pen.GetWidth()) 253 dc.SetIdBounds(id,r) 254 self.objids.append(id) 255 elif choice == 8: 256 w,h = self.bmp.GetSize() 257 x = random.randint(0, W-w) 258 y = random.randint(0, H-h) 259 dc.DrawBitmap(self.bmp,x,y,True) 260 dc.SetIdBounds(id,wx.Rect(x,y,w,h)) 261 self.objids.append(id) 262 263 264class ControlPanel(wx.Panel): 265 def __init__(self, parent, id, pos=wx.DefaultPosition, 266 size=wx.DefaultSize, style = wx.TAB_TRAVERSAL): 267 wx.Panel.__init__(self,parent,id,pos,size,style) 268 lbl = wx.StaticText(self, wx.ID_ANY, "Hit Test Radius: ") 269 lbl2 = wx.StaticText(self, wx.ID_ANY, "Left Click to drag, Right Click to enable/disable") 270 sc = wx.SpinCtrl(self, wx.ID_ANY, "5") 271 sc.SetRange(0,100) 272 global hitradius 273 sc.SetValue(hitradius) 274 self.sc = sc 275 sz = wx.BoxSizer(wx.HORIZONTAL) 276 sz.Add(lbl,0,wx.EXPAND) 277 sz.Add(sc,0) 278 sz.Add(lbl2,0,wx.LEFT,5) 279 sz.Add((10,10),1,wx.EXPAND) 280 self.SetSizerAndFit(sz) 281 sc.Bind(wx.EVT_SPINCTRL,self.OnChange) 282 sc.Bind(wx.EVT_TEXT,self.OnChange) 283 284 def OnChange(self, event): 285 global hitradius 286 hitradius = self.sc.GetValue() 287 288 289#--------------------------------------------------------------------------- 290 291def runTest(frame, nb, log): 292 pnl = wx.Panel(nb, wx.ID_ANY,size=(200,30)) 293 pnl2 = ControlPanel(pnl,wx.ID_ANY) 294 win = MyCanvas(pnl, wx.ID_ANY, log) 295 sz = wx.BoxSizer(wx.VERTICAL) 296 sz.Add(pnl2,0,wx.EXPAND|wx.ALL,5) 297 sz.Add(win,1,wx.EXPAND) 298 pnl.SetSizerAndFit(sz) 299 return pnl 300 301#--------------------------------------------------------------------------- 302 303 304 305overview = """ 306<html> 307<body> 308<h2>wx.adv.PseudoDC</h2> 309 310The wx.adv.PseudoDC class provides a way to record operations on a DC and then 311play them back later. The PseudoDC can be passed to a drawing routine as 312if it were a real DC. All Drawing methods are supported except Blit but 313GetXXX methods are not supported and none of the drawing methods return 314a value. The PseudoDC records the drawing to an operation 315list. The operations can be played back to a real DC using:<pre> 316DrawToDC(dc) 317</pre> 318The operations can be tagged with an id in order to associate them with a 319specific object. To do this use:<pre> 320 SetId(id) 321</pre> 322Every operation after this will be associated with that tag id until SetId is called 323again. The PseudoDC also supports object level clipping. To enable this use:<pre> 324 SetIdBounds(id,rect) 325</pre> 326for each object that should be clipped. Then use:<pre> 327 DrawToDCClipped(dc, clippingRect) 328</pre> 329To draw the PseudoDC to a real dc. This is useful for large scrolled windows 330where many objects are off screen. 331 332Objects can be moved around without re-drawing using:<pre> 333 TranslateId(id, dx, dy) 334</pre> 335 336To re-draw an object use:<pre> 337 ClearId(id) 338 SetId(id) 339</pre> 340and then re-draw the object. 341</body> 342</html> 343""" 344 345 346if __name__ == '__main__': 347 import sys,os 348 import run 349 run.main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) 350 351