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