comparison visualclient/visclient.py @ 27:c97feb7ef9e9

Strasbourg
author gyorgyf
date Thu, 20 Sep 2012 23:55:48 +0100
parents 858fc44a17c1
children 6022a370c7dd
comparison
equal deleted inserted replaced
26:2ec521bbd543 27:c97feb7ef9e9
12 from pygame.locals import * 12 from pygame.locals import *
13 import httplib as ht 13 import httplib as ht
14 14
15 import gradients 15 import gradients
16 from gradients import genericFxyGradient 16 from gradients import genericFxyGradient
17
18 from threading import Thread
19 from random import random
17 20
18 # from pytagcloud import create_tag_image, make_tags 21 # from pytagcloud import create_tag_image, make_tags
19 # from pytagcloud.lang.counter import get_tag_counts 22 # from pytagcloud.lang.counter import get_tag_counts
20 23
21 # YOUR_TEXT = "A tag cloud is a visual representation for text data, typically\ 24 # YOUR_TEXT = "A tag cloud is a visual representation for text data, typically\
27 30
28 scol = (0,255,0,255) 31 scol = (0,255,0,255)
29 ecol = (0,0,0,255) 32 ecol = (0,0,0,255)
30 33
31 # X,Y=1140,900 34 # X,Y=1140,900
32 X,Y = 600,400 35 # X,Y = 600,400
33 NBLOBS = 15 36 X,Y = 800,600
37
38 NBLOBS = 18
34 BLOBSIZE = 25 39 BLOBSIZE = 25
35 G=110 40 G=110
36 FADE = 15 41 FADE = 15
42 DIST = 0.1 # blob equivalence tolerance
43 FRAMERATE = 60
44
45 # Connection:
46 # IP = "192.168.2.184:8030"
47 IP = "138.37.95.215"
48 HTTP_TIMEOUT = 3
49 SERVER_UPDATE_INTERVAL = 0.8
50
51 class Indicator(object):
52
53 off_color = pg.Color(110,0,0)
54 on_color = pg.Color(0,120,0)
55
56 def __init__(self,bg,pos):
57 self.visible = True
58 self.ison = True
59 self.x,self.y = pos
60 self.xs = int(self.x * X)
61 self.ys = int(Y - (self.y * Y))
62 self.c = self.off_color
63 self.size = 6
64 self.bg = bg
65
66 def reinit(self,bg):
67 self.bg = bg
68 self.xs = int(self.x * X)
69 self.ys = int(Y - (self.y * Y))
70
71 def draw(self):
72 if self.visible :
73 pg.draw.circle(self.bg, self.c, (self.xs,self.ys),self.size,0)
74
75 def toggle(self):
76 if self.ison == True :
77 self.off()
78 else :
79 self.on()
80 return self
81
82 def on(self):
83 self.c = self.on_color
84 self.ison = True
85 return self
86
87 def off(self):
88 self.c = self.off_color
89 self.ison = False
90 return self
91
37 92
38 class Blob(object): 93 class Blob(object):
39 94
40 def __init__(self,bg,x,y,color=(255,255,255),mood=None,fade=FADE): 95 def __init__(self,bg,x,y,color=(255,255,255),mood=None,fade=FADE):
41 self.x = x 96 self.x = x
53 if mood and mood.color : 108 if mood and mood.color :
54 self.c = mood.color 109 self.c = mood.color
55 110
56 def __cmp__(self,other): 111 def __cmp__(self,other):
57 d = math.sqrt( math.pow((self.x-other.x),2) + math.pow((self.y-other.y),2) ) 112 d = math.sqrt( math.pow((self.x-other.x),2) + math.pow((self.y-other.y),2) )
58 if d < 0.03 : 113 if d < DIST :
59 return 0 114 return 0
60 else : 115 else :
61 return -1 116 return -1
62 117
63 def draw(self): 118 def draw(self):
119 if not self.visible : return
64 d=int(self.size) 120 d=int(self.size)
65 self.bg.blit(gradients.radial(self.size, (self.c[0],self.c[1],self.c[2],self.alpha), (0,0,0,self.alpha)), (self.xs-d,self.ys-d)) 121 self.bg.blit(gradients.radial(self.size, (self.c[0],self.c[1],self.c[2],self.alpha), (0,0,0,self.alpha)), (self.xs-d,self.ys-d))
66 self.alpha = 255 - int(self.age()*self.FADE) 122 self.alpha = 255 - int(self.age()*self.FADE)
67 if self.alpha < 5 : 123 if self.alpha < 5 :
68 self.alpha = 1 124 self.alpha = 1
100 return math.sqrt( math.pow((self.x-x),2) + math.pow((self.y-y),2) ) 156 return math.sqrt( math.pow((self.x-x),2) + math.pow((self.y-y),2) )
101 157
102 158
103 159
104 class VisualClient(object): 160 class VisualClient(object):
161 '''Main visualisation client.'''
105 162
106 def __init__(self): 163 def __init__(self):
107 # self.conn = ht.HTTPConnection("192.168.2.184:8030") 164 # self.conn = ht.HTTPConnection("192.168.2.184:8030")
108 self.conn = ht.HTTPConnection("138.37.95.215") 165 # self.conn = ht.HTTPConnection("138.37.95.215")
109 166
110 pg.init() 167 pg.init()
111 168
112 # fontObj = pg.font.Font("freesansbold.ttf",18) 169 # fontObj = pg.font.Font("freesansbold.ttf",18)
113 white = ( 255, 255, 255) 170 white = ( 255, 255, 255)
117 self.screen = pg.display.set_mode((X, Y)) 174 self.screen = pg.display.set_mode((X, Y))
118 pg.display.set_caption('Mood Conductor') 175 pg.display.set_caption('Mood Conductor')
119 self.bg = pg.Surface(self.screen.get_size()) 176 self.bg = pg.Surface(self.screen.get_size())
120 self.bg = self.bg.convert() 177 self.bg = self.bg.convert()
121 self.bg.fill((0,0,0)) 178 self.bg.fill((0,0,0))
179 pg.display.set_gamma(100.0)
122 180
123 181
124 self.scol = (0,255,0,255) 182 self.scol = (0,255,0,255)
125 self.ecol = (0,0,0,255) 183 self.ecol = (0,0,0,255)
126 coordstxt = "test" 184 coordstxt = "test"
127 185
128 self.blobs = [] 186 self.blobs = []
129 self.moods = [] 187 self.moods = []
130 self.read_mood_data() 188 self.read_mood_data()
131 # pg.quit()
132 # sys.exit(-1)
133 189
134 self.FADE = FADE 190 self.FADE = FADE
135 191
192 self.indicators = {
193 "conn":Indicator(self.bg,(0.98,0.02)),
194 "update":Indicator(self.bg,(0.96,0.02)),
195 "data":Indicator(self.bg,(0.94,0.02)),
196 "receive":Indicator(self.bg,(0.92,0.02))}
197
198 self.thread = None
199 self.running = False
200 self.fullscreen = False
201 self.init_reconnect = False
136 pass 202 pass
137 203
204
138 def read_mood_data(self): 205 def read_mood_data(self):
139 206 '''Read the mood position and color information form csv file.'''
140 with open('moods.csv') as mf: 207 with open('moods.csv') as mf:
141 data = mf.readlines()[1:] 208 data = mf.readlines()[1:]
142 for line in data : 209 for line in data :
143 l = line.split(',') 210 l = line.split(',')
144 mood = Mood(l[0],l[1],l[2]) 211 mood = Mood(l[0],l[1],l[2])
145 self.moods.append(mood) 212 self.moods.append(mood)
146 # with open('feelings.txt') as ff:
147 # data = ff.readlines()
148 # data = map(lambda x: x.split('\t'),data)
149 # for mood in self.moods :
150 # for colors in data :
151 # if mood.word == colors[0] :
152 # mood.color = colors[2]
153 with open('colors.txt') as ff: 213 with open('colors.txt') as ff:
154 data = ff.readlines()[1:] 214 data = ff.readlines()[1:]
155 data = map(lambda x: x.split(','),data) 215 data = map(lambda x: x.split(','),data)
156 for mood in self.moods : 216 for mood in self.moods :
157 d = cd = sys.float_info.max 217 d = cd = sys.float_info.max
158 for colors in data : 218 for colors in data :
159 d = mood.get_distance(float(colors[0]),float(colors[1])) 219 d = mood.get_distance(float(colors[0]),float(colors[1]))
160 if d < cd : 220 if d < cd :
161 cd = d 221 cd = d
162 mood.color = tuple(map(lambda x: int(pow(math.atan((float(x)/7.0)),12.5)),(colors[2],colors[3],colors[4]))) 222 mood.color = tuple(map(lambda x: int(pow(math.atan((float(x)/7.0)),12.5)),(colors[2],colors[3],colors[4])))
163 223 # mood.color = tuple(map(lambda x: int(x),(colors[2],colors[3],colors[4])))
164 # for mood in self.moods: 224 return True
165 # if mood.color : 225
166 # # mood.color = map(lambda x: '0x'+str(x).strip(),[mood.color[0:2],mood.color[2:4],mood.color[4:]]) 226
167 # mood.color = tuple(map(lambda x: int(x),mood.color)) 227 def start_update_thread(self):
168 # print mood.color 228 '''Start the thread that reads data from the server.'''
169 229 self.running = True
230 self.thread = Thread(target = self.update_thread)
231 self.thread.daemon = True
232 self.thread.start()
233
234 def stop_update_thread(self):
235 '''Stop the thread and allow some time fot the connections to close.'''
236 self.running = False
237 try :
238 self.thread.join(2)
239 except :
240 print "No update thread to join."
241
242 def update_thread(self):
243 '''The server update thread'''
244 while self.running :
245 try :
246 self.update()
247 # self.indicators["update"].visible = True
248 except Exception, e:
249 if str(e).strip() : print "Exception: ", str(e), type(e), len(str(e).strip())
250 self.indicators["conn"].off()
251 # self.indicators["update"].visible = False
252 time.sleep(SERVER_UPDATE_INTERVAL)
253
170 254
171 def update(self): 255 def update(self):
256 '''Update the blob list from the server. This should be in a thread.'''
257
258 # indicate connection health by toggling an indictor
259 self.indicators["update"].toggle()
172 260
173 # delete invisibles 261 # delete invisibles
174 for blob in self.blobs : 262 for blob in self.blobs :
175 if not blob.visible : 263 if not blob.visible :
176 self.blobs.remove(blob) 264 self.blobs.remove(blob)
179 self.conn.putrequest("GET","/moodconductor/result", skip_host=True) 267 self.conn.putrequest("GET","/moodconductor/result", skip_host=True)
180 self.conn.putheader("Host", "www.isophonics.net") 268 self.conn.putheader("Host", "www.isophonics.net")
181 self.conn.endheaders() 269 self.conn.endheaders()
182 res = self.conn.getresponse() 270 res = self.conn.getresponse()
183 data = res.read() 271 data = res.read()
184 data = eval(data) 272 data = eval(data)
185 if not data : 273 if not data :
186 self.conn.close() 274 self.conn.close()
275 self.indicators["data"].toggle()
187 return False 276 return False
188 for d in data : 277 for d in data :
189 # coordstxt = "x:%s y:%s c:%s" %d 278 # coordstxt = "x:%s y:%s c:%s" %d
190 x,y,c = d 279 x,y,count = d
191 280 self.add_blob(x,y,count)
192 cmood = None 281 self.indicators["receive"].toggle()
193 d = cd = sys.float_info.max
194 for mood in self.moods :
195 d = mood.get_distance(x,y)
196 if d < cd :
197 cd = d
198 cmood = mood
199
200 new = Blob(self.bg,x,y,mood=cmood,fade=self.FADE)
201 if not new in self.blobs :
202 self.blobs.insert(0,new)
203 elif c > self.blobs[self.blobs.index(new)].count:
204 self.blobs[self.blobs.index(new)].increment(c)
205
206 self.conn.close() 282 self.conn.close()
207 self.blobs = self.blobs[:NBLOBS] 283 self.blobs = self.blobs[:NBLOBS]
208 return True 284 return True
285
286
287 def add_blob(self,x,y,count=1):
288 '''Insert a blob to the list of blobs'''
289 # find mood correxponding to x,y
290 cmood = None
291 d = cd = sys.float_info.max
292 for mood in self.moods :
293 d = mood.get_distance(x,y)
294 if d < cd :
295 cd = d
296 cmood = mood
297 # create new blob or increase click count on existing one
298 new = Blob(self.bg,x,y,mood=cmood,fade=self.FADE)
299 if not new in self.blobs :
300 self.blobs.insert(0,new)
301 elif count > self.blobs[self.blobs.index(new)].count:
302 self.blobs[self.blobs.index(new)].increment(count)
303 pass
209 304
210 305
211 def draw(self): 306 def draw(self):
212 self.bg.fill((0,0,0)) 307 self.bg.fill((0,0,0))
213 # self.bg.blit(gradients.radial(19, self.scol, self.ecol), (rect_x,rect_y)) 308 # self.bg.blit(gradients.radial(19, self.scol, self.ecol), (rect_x,rect_y))
214 l = copy.copy(self.blobs) 309 l = copy.copy(self.blobs)
215 l.reverse() 310 l.reverse()
216 for blob in l : 311 for blob in l :
217 blob.draw() 312 blob.draw()
218 313
219 # axis 314 # axis
220 pg.draw.line(self.bg, (G,G,G), (int(X/2.0),0),(int(X/2.0),Y), 1) 315 pg.draw.line(self.bg, (G,G,G), (int(X/2.0),0),(int(X/2.0),Y), 1)
221 pg.draw.line(self.bg, (G,G,G), (0,int(Y/2.0)),(X,int(Y/2.0)), 1) 316 pg.draw.line(self.bg, (G,G,G), (0,int(Y/2.0)),(X,int(Y/2.0)), 1)
222 317
318 # indicators
319 for i in self.indicators.itervalues() :
320 i.draw()
321
322 def read_keys(self):
323 '''Read keys'''
324 for event in pg.event.get() :
325 # Quit (event)
326 if event.type == QUIT:
327 self.quit()
328 elif event.type == KEYDOWN :
329 # Post Quit: Esc, q
330 if event.key == K_ESCAPE or event.key == K_q :
331 pg.event.post(pg.event.Event(QUIT))
332 # Reset: Space
333 elif event.key == K_SPACE :
334 self.blobs = []
335 # Random : r
336 elif event.key == K_r :
337 self.add_blob(random(),random(),count=5)
338 # Connection : c
339 elif event.key == K_c :
340 self.init_reconnect = True
341 self.indicators["conn"].off()
342 # Fullscreen: f
343 elif event.key == K_f :
344 # pg.display.toggle_fullscreen()
345 self.toggle_screen_mode()
346 # Toggle fade: s
347 elif event.key == K_s :
348 if self.FADE :
349 print "fade off"
350 self.indicators["conn"].off()
351 self.FADE = 0
352 for blob in self.blobs :
353 blob.fade(0)
354 else:
355 print "fade on"
356 self.indicators["conn"].on()
357 self.FADE = 15
358 for blob in self.blobs :
359 blob.fade(15)
360 blob.reset_time()
361 pass
362
363 def toggle_screen_mode(self):
364 '''Go back and forth between full screen mode.'''
365 if self.fullscreen == False:
366 globals()['_X'] = globals()['X']
367 globals()['_Y'] = globals()['Y']
368 globals()['X'] = 1440
369 globals()['Y'] = 900
370 self.screen = pg.display.set_mode((X, Y))
371 self.fullscreen = True
372 self.bg = pg.Surface(self.screen.get_size())
373 self.bg = self.bg.convert()
374 self.bg.fill((0,0,0))
375 for i in self.indicators.itervalues() :
376 i.reinit(self.bg)
377 i.draw()
378 else :
379 globals()['X'] = globals()['_X']
380 globals()['Y'] = globals()['_Y']
381 self.screen = pg.display.set_mode((X, Y))
382 self.fullscreen = False
383 self.bg = pg.Surface(self.screen.get_size())
384 self.bg = self.bg.convert()
385 self.bg.fill((0,0,0))
386 for i in self.indicators.itervalues() :
387 i.reinit(self.bg)
388 i.draw()
389 pg.display.toggle_fullscreen()
390
223 391
224 392
225 def run(self): 393 def run(self):
226 # conn = ht.HTTPConnection("192.168.2.184:8030") 394
227 self.conn = ht.HTTPConnection("138.37.95.215") 395 # setup connection
228 396 self.connect()
229 rect_x,rect_y = 0,0 397
230 counter = 0
231
232 # main loop 398 # main loop
233 while True : 399 while True :
234 # pg.draw.circle(screen, pg.Color(255,0,0), (300,50),20,0) 400 # pg.draw.circle(screen, pg.Color(255,0,0), (300,50),20,0)
235 # screen.blit(gradients.radial(99, scol, ecol), (401, 1)) 401 # screen.blit(gradients.radial(99, scol, ecol), (401, 1))
236 402
237 for event in pg.event.get() : 403 self.read_keys()
238 if event.type == QUIT:
239 self.conn.close()
240 pg.quit()
241 sys.exit()
242 elif event.type == KEYDOWN :
243 if event.key == K_ESCAPE :
244 pg.event.post(pg.event.Event(QUIT))
245 elif event.key == K_SPACE :
246 self.blobs = []
247 elif event.key == K_s :
248 if self.FADE :
249 print "fade off"
250 self.FADE = 0
251 for blob in self.blobs :
252 blob.fade(0)
253 else:
254 print "fade on"
255 self.FADE = 15
256 for blob in self.blobs :
257 blob.fade(15)
258 blob.reset_time()
259
260
261
262 404
263 # Draw 405 # Draw
264 self.draw() 406 self.draw()
265 407
266
267 # update from the server
268 counter += 1
269 if counter % 12 :
270 counter = 0
271 try :
272 self.update()
273 except:
274 pass
275 # update display 408 # update display
276 self.screen.blit(self.bg, (0, 0)) 409 self.screen.blit(self.bg, (0, 0))
277 pg.display.flip() 410 pg.display.flip()
278 self.fpsClock.tick(50) 411 self.fpsClock.tick(FRAMERATE)
279 pass 412
413 if self.init_reconnect:
414 self.reconnect()
415
416 return True
417
418
419 def connect(self,retry = 5):
420 '''Connect to the server and test connection.'''
421 if not retry :
422 print "Server unreachable"
423 pg.quit()
424 raise SystemExit
425 if retry < 5 :
426 time.sleep(3)
427 try :
428 self.conn = ht.HTTPConnection(IP,timeout=HTTP_TIMEOUT)
429 # self.start_update_thread()
430 self.indicators["conn"].on()
431 except :
432 self.indicators["conn"].off()
433 self.connect(retry = retry-1)
434
435 try:
436 self.conn.putrequest("GET","/moodconductor/index.html", skip_host=True)
437 self.conn.putheader("Host", "www.isophonics.net")
438 self.conn.endheaders()
439 res = self.conn.getresponse()
440 if res.status == 200 :
441 self.indicators["conn"].on()
442 else :
443 print "Server response:", res.status, res.reason
444 self.indicators["conn"].off()
445 if not hasattr(self,"noretry") and raw_input("Go offline? ") in ['y',''] :
446 return False
447 else :
448 self.noretry = None
449 self.connect(retry = retry-1)
450 except :
451 print "Exception while testing connection."
452 self.indicators["conn"].off()
453 # comment out in offline mode
454 if not hasattr(self,"noretry") and raw_input("Go offline? ") in ['y',''] :
455 return False
456 else :
457 self.noretry = None
458 self.connect(retry = retry-1)
459 self.start_update_thread()
460 return True
461
462
463
464 def reconnect(self):
465 '''Called when c is pressed.'''
466 self.init_reconnect = False
467 # self.indicators["conn"].off().draw()
468 # self.screen.blit(self.bg, (0, 0))
469 # pg.display.flip()
470
471 self.stop_update_thread()
472 time.sleep(1)
473 try :
474 self.conn.close()
475 except :
476 self.indicators["conn"].off()
477 try :
478 self.conn = ht.HTTPConnection(IP,timeout=HTTP_TIMEOUT)
479 self.conn.connect()
480 self.indicators["conn"].on()
481 print "Reconnected."
482 except :
483 self.indicators["conn"].off()
484 print "Error while reconnecting."
485 self.start_update_thread()
486
487
488 def quit(self):
489 print "Quitting.."
490 self.indicators["conn"].off()
491 self.stop_update_thread()
492 self.conn.close()
493 pg.quit()
494 sys.exit()
495
496
497
280 498
281 499
282 def main(): 500 def main():
283 501
284 v = VisualClient() 502 v = VisualClient()