Package joy ::
Module scope
|
|
1 import pygame as pg
2 import numpy as np
3
5 """
6 Concrete class DataWindow represents a sliding window of data through time
7 """
9 self.clear()
10 self.duration = duration
11
13 """
14 Empty the window.
15 """
16 self.push = self._firstPush
17 self.t = []
18 self.data = []
19
21 """
22 Change the duration of data allowed in the window.
23 This may discard existing data if duration was made smaller
24 """
25 assert duration>=0
26 self.duration = duration
27 if self.t:
28 self._trim()
29
31 """
32 (private) trim any entries outside the most recent window of
33 self.duration time units
34 """
35 t = self.t[-1]
36 while t-self.t[0]>self.duration:
37 self.t.pop(0)
38 self.data.pop(0)
39
41 """
42 Push data at the end of the window
43 INPUTS:
44 t -- timestamp
45 val -- data record to append / replace
46
47 If t equals last timestamp, last entry in window is replaced. Otherwise,
48 entry is appended and window is trimmed to duration limit.
49
50 (private) first push operation on an empty window
51 """
52 self.t = [t]
53 self.data = [val]
54 self.push = self._push
55
56 - def _push( self, t, val ):
57 """
58 Push data at the end of the window
59 INPUTS:
60 t -- timestamp
61 val -- data record to append / replace
62
63 If t equals last timestamp, last entry in window is replaced. Otherwise,
64 entry is appended and window is trimmed to duration limit.
65 """
66 if t>self.t[-1]:
67 self.t.append(t)
68 self.data.append(val)
69 self._trim()
70 elif t==self.t[-1]:
71 self.data[-1] = val
72 else:
73 raise IndexError('Time %g < last entry %g' % (t,self.t[-1]))
74
75 - def push( self, t, val ):
76 """Fake placeholder -- modified by __init__, _firstPush"""
77 pass
78
80 """
81 Convenience function for creating a figure layout
82 INPUT:
83 grid -- ASCII art layout (see example below)
84 bw -- border width in grids; either scalar or (horizontal,vertical)
85 OUTPUT:
86 Boxes for regions, sorted by region id chars.
87 Regions marked with space, '-' or '|' are ignored
88 Example:
89 p='''
90 xxxxx yyy
91 xxxxx yyy
92 xxxxx yyy
93
94 zzzzzzzzz
95 zzzzzzzzz
96 '''
97 box_x,box_y,box_z = planAxes(p,bw=0.5)
98 """
99
100 bw = np.asarray(bw)
101 if bw.size==1:
102 bw = np.array([bw,bw])
103 else:
104 assert bw.size==2
105
106 g0 = grid.strip().split("\n")
107 l = max([len(r) for r in g0])
108
109 pad = " "*l
110
111 g = np.array([ [y for y in (x+pad)[:l]] for x in g0][::-1])
112 xs = 1.0/g.shape[1]
113 ys = 1.0/g.shape[0]
114 lst = np.unique(g.flatten())
115 res = []
116 bx,by = bw
117 for nm in lst:
118 if nm in ' -|':
119 continue
120 ind = (g==nm)
121 xi = np.any(ind,axis=0).ravel().nonzero()[0]
122 yi = np.any(ind,axis=1).ravel().nonzero()[0]
123 box = np.array((
124 (xi[0]+bx)*xs,(yi[0]+by)*ys,
125 xs*(xi[-1]-xi[0]+1-2*bx),ys*(yi[-1]-yi[0]+1-2*by)
126 ))
127 res.append(box)
128 return res
129
131 """
132 Concrete class representing a bitmap that is placed at multiple
133 locations on an image. Each Glyph has an image and a basepoint
134 offset (which defaults to its center). The image is RGBA, so
135 pixels may be made transparent.
136
137 By default, a Glyph contains a blue cross of the specified size,
138 with lines 3 pixels wide. With size 5, this makes a good circular
139 dot.
140 """
141 - def __init__(self, size, color=(0,0,128) ):
142 """
143 Create a glyph of the specified size and color.
144
145 INPUTS:
146 size -- number or pair -- size of glyph image
147
148 ATTRIBUTES:
149 .ofs -- len 2 -- offset of basepoint into image
150 .img -- pygame image of size size, mode RGBA -- the Glyph itself
151 """
152 try:
153 len(size)
154 except:
155 size = (size,size)
156 self.img = pg.image.fromstring(
157 ' ' * (size[0]*size[1]*4), size,"RGBA" )
158 self.ofs = (size[0]/2, size[1]/2)
159 self.img.fill((0,0,0,0))
160 pg.draw.line(self.img, color, (0,self.ofs[1]),(size[0],self.ofs[1]),3)
161 pg.draw.line(self.img, color, (self.ofs[0],0),(self.ofs[1],size[1]),3)
162
163 - def put( self, surf, x, y ):
164 """
165 Put copies of the image at the specified points on the surface
166
167 INPUTS:
168 surf -- pygame.Surface to draw on
169 x,y -- sequences of equal length of x and y coordinates
170 """
171 sz = self.img.get_size()
172 for xi,yi in zip(x,y):
173 surf.blit( self.img, (xi-self.ofs[0],yi-self.ofs[1],sz[0],sz[1]))
174
176 """
177 Concrete class LinePlotter implements simple line plotting functionality
178 (including coordinate scaling and line markers) on a box within a
179 pygame.Surface
180 """
181 color = np.array([0,0,255])
182
183 - def __init__(self, surf, box = (0,0,1,1) ):
184 """
185 INPUTS:
186 surf -- the pygame.Surface on which to plot
187 box -- bounding box, in range (0,0) to (1,1)
188
189 ATTRIBUTES (public):
190 .c -- color -- line color to use; autogenerated
191 .mrk -- Glyph -- line symbols; default is dot with the color .c
192 .bg -- color -- background color used for clearing plot area
193 .axes -- 4-list -- (xmin,ymin,width,height)
194 """
195 w,h = surf.get_size()
196 x0,y0,x1,y1 = box
197 self._surf = surf.subsurface( (w*x0, h*y0, w*x1, h*y1) )
198 self.c = LinePlotter.color
199 self.mrk = Glyph((5,5),color=LinePlotter.color)
200 LinePlotter.color = (LinePlotter.color + np.array([0x85,0x05,0x50])) % 255
201 self.bg = (255,255,255)
202 self.axes = [0,0,1,1]
203
204 - def plot( self, x, y=None ):
205 """
206 Plot a line with the specified points
207
208 INPUTS:
209 x -- numbers -- x coordinates, in plot(x,y) form, or y coordinates
210 in plot(y) form.
211 y -- numbers or None -- y coordinates of points
212 """
213 W,H = self._surf.get_size()
214 x0,y0,xs,ys = tuple(( float(x) for x in self.axes ))
215 if y is None:
216 py = (np.asarray(x)-x0)/xs * H
217 px = np.arange(0,W,float(W)/len(py))
218 else:
219 px = (np.asarray(x)-x0)/xs * W
220 py = (np.asarray(y)-y0)/ys * H
221 if self.mrk:
222 self.mrk.put(self._surf,px,py)
223 if len(px)>1:
224 pg.draw.aalines( self._surf, self.c, False, zip(px,py))
225
227 """
228 Clear the plot area to background color
229 """
230 self._surf.fill(self.bg)
231
232 if __name__=="__main__":
233 from pygame.locals import *
234 pg.init()
235 screen = pg.display.set_mode((640,480))
236 clock = pg.time.Clock()
237 bx = planBoxes('abc\ndef')
238 plt = [
239 [LinePlotter( screen, b ),DataWindow( 2000 )]
240 for b in bx
241 ]
242 while True:
243 clock.tick(60)
244 pg.event.pump()
245 event = pg.event.poll()
246 if event.type == QUIT:
247 break
248 if event.type == KEYDOWN and event.key == K_ESCAPE:
249 break
250 t = pg.time.get_ticks()
251 screen.fill(0)
252 for lp,dw in plt:
253 lp.cla()
254 dw.push(t,np.random.rand())
255 lp.axes[2] = float(dw.duration)
256 lp.plot( dw.data )
257 pg.display.flip()
258