Mercurial > hg > mood-conductor
view visualclient2/visclient.py @ 54:c0b34039917a tip
Server: added an exposed function to log the start time of a performance (for log-to-audio sync)
author | Mathieu Barthet <mathieu.barthet@eecs.qmul.ac.uk> |
---|---|
date | Wed, 14 Oct 2015 19:20:08 +0100 |
parents | 2db17c224664 |
children | 874ac833c8e3 |
line wrap: on
line source
#!/usr/bin/env python # encoding: utf-8 """ visclient.py Created by George Fazekas on 2012-06-17. Copyright (c) 2012 . All rights reserved. """ import sys,os,math,time,copy import pygame as pg from pygame.locals import * from pygame import gfxdraw as gd import httplib as ht import gradients from gradients import genericFxyGradient from threading import Thread from random import random import colorsys as cs scol = (0,255,0,255) ecol = (0,0,0,255) xavg=0 yavg=0 countavg=0 moods = [] # X,Y=1140,900 # X,Y = 600,400 X,Y = 800,600 # X,Y = 1024,768 # Fullscreen resolution: # XF,YF = 1280,900 # XF,YF = 1440,900 XF,YF = 1344,900 # display calibrated # detect display resolution import subprocess screenres = subprocess.Popen('xrandr | grep "\*" | cut -d" " -f4',shell=True, stdout=subprocess.PIPE).communicate()[0] screenres = map(lambda x: int(x.strip()), screenres.split('x')) XF,YF = screenres print "Screen resolution: ",XF,YF # NBLOBS = 18 # BLOBSIZE = 25 # G=110 # FADE = 15 # DIST = 0.15 # blob equivalence tolerance # FRAMERATE = 60 # new parameters NBLOBS = 18 BLOBSIZE = 25 G=110 FADE = 25 DIST = 0.10 # blob equivalence tolerance FRAMERATE = 120 # Connection: # IP = "127.0.0.1:8030" # IP = "192.168.2.158:8030" IP = "138.37.95.215" # this is the IP of kakapo<=>golden HTTP_TIMEOUT = 3 SERVER_UPDATE_INTERVAL = 0.8 class Indicator(object): def __init__(self,bg,pos,invisible=False): self.off_color = pg.Color(110,0,0) self.on_color = pg.Color(0,120,0) if invisible : self.off_color = pg.Color(0,0,0) self.on_color = pg.Color(0,0,120) self.visible = True self.ison = True self.x,self.y = pos self.xs = int(self.x * X) self.ys = int(Y - (self.y * Y)) self.c = self.off_color self.size = 6 self.bg = bg def reinit(self,bg): self.bg = bg self.xs = int(self.x * X) self.ys = int(Y - (self.y * Y)) def draw(self): if self.visible : # pg.draw.circle(self.bg, self.c, (self.xs,self.ys),self.size,0) # gd.aacircle(self.bg, self.xs, self.ys, self.size+1, self.c) gd.filled_circle(self.bg, self.xs, self.ys, self.size, self.c) gd.aacircle(self.bg, self.xs, self.ys, self.size, self.c) def toggle(self): if self.ison == True : self.off() else : self.on() return self def on(self): self.c = self.on_color self.ison = True return self def off(self): self.c = self.off_color self.ison = False return self def now(self,screen): # for i in self.indicators.itervalues() : # i.draw() self.draw() screen.blit(self.bg, (0, 0)) pg.display.flip() return self class MovingBlob(object): black_color = pg.Color(0,0,0) def __init__(self,bg,x,y,color=(255,255,255),mood=None,fade=FADE): self.x = x self.y = y self.xs = x * X self.ys = Y - (y * Y) self.bg = bg self.size = BLOBSIZE self.time = time.time() self.alpha = 255 self.c = color self.count = 1 self.visible = True self.FADE = fade self.mood = mood if mood and mood.color : self.c = mood.color self.title = mood.word self.speed_factor = 80.0 self.target_x = 0.0 self.target_y = 0.0 def set_target(self,x,y): self.target_x = x self.target_y = y def draw(self): if not self.visible : return global xavg,yavg,moods # xspeed = (xavg - self.x) / self.speed_factor # yspeed = (yavg - self.y) / self.speed_factor # self.x = self.x + xspeed # self.y = self.y + yspeed self.x = self.x + (self.target_x - self.x) / self.speed_factor self.y = self.y + (self.target_y - self.y) / self.speed_factor #self.size = countavg//TODO self.xs = self.x * X self.ys = Y - (self.y * Y) #d=int(self.size) d = cd = sys.float_info.max for mood in moods : d = mood.get_distance(self.x,self.y) if d < cd : cd = d self.mood = mood self.c = mood.color self.title = mood.word # 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-BLOBSIZE,self.ys-BLOBSIZE)) d=int(self.size) # print d 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)) gd.aacircle(self.bg, int(self.xs), int(self.ys), int(self.size), self.black_color) gd.aacircle(self.bg, int(self.xs), int(self.ys), int(self.size-1), self.black_color) font = pg.font.Font(None, 36) text = font.render(self.title, 1, self.c) textpos = text.get_rect() #print textpos.width if self.xs > X- textpos.width: if self.ys > Y- textpos.height: self.bg.blit(text, (self.xs - textpos.width,self.ys - textpos.height)) else: self.bg.blit(text, (self.xs - textpos.width,self.ys)) else : if self.ys > Y- textpos.height: self.bg.blit(text, (self.xs,self.ys - textpos.height)) else: self.bg.blit(text, (self.xs,self.ys)) #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)) #self.alpha = 255 - int(self.age()*self.FADE) #if self.alpha < 5 : # self.alpha = 1 # self.visible = False def age(self): return time.time() - self.time def resize(self,count): if self.count == count : return None self.time = time.time() self.count = count # self.size = int(BLOBSIZE * int(self.count/1.5)) self.to = int(BLOBSIZE * int(self.count/1.5)) if self.to < BLOBSIZE : self.to = BLOBSIZE if self.to and self.size != self.to: self.start_animate() # print "resize to",count,self.to def get_distance(self,x,y): return math.sqrt( math.pow((self.x-x),2) + math.pow((self.y-y),2) ) def fade(self,fade): if not fade : self.alpha = 255 self.FADE = fade def reset_time(self): self.time = time.time() def reinit(self,bg): self.bg = bg self.xs = int(self.x * X) self.ys = int(Y - (self.y * Y)) def start_animate(self): self.thread = Thread(target = self.animate) self.thread.daemon = True self.thread.start() def animate(self): '''Animate the way bubbles are grown.''' tolerance = 5 # while self.size > self.to-tolerance and self.size < self.to+tolerance : while self.size != self.to : self.size = int(self.size + int(self.to-self.size) * 0.1) time_inc = 20.0 / (pow(1.2, abs(self.to-self.size)) * 200.0) time.sleep(0.02+time_inc) # print "sizing to ", self.to class Blob(object): def __init__(self,bg,x,y,color=(255,255,255),mood=None,fade=FADE): self.x = x self.y = y self.xs = x * X self.ys = Y - (y * Y) self.bg = bg self.size = BLOBSIZE #pixels self.time = time.time() #s self.const_time = time.time() #s self.alpha = 255 #8-bit alpha channel value self.c = color #RGB colour 3-tuple self.count = 1 self.visible = True self.FADE = fade if mood and mood.color : self.c = mood.color self.title = mood.word self.inactivity_delay = 75.0 #s self.proximity_delay = 35.0 #s self.proximity_tolerance = 0.13 self.target_in_proximity = None def __cmp__(self,other): if other is None : return -1 d = math.sqrt( math.pow((self.x-other.x),2) + math.pow((self.y-other.y),2) ) if d < DIST : return 0 else : return -1 def object_in_proximity(self,other): if other is None : return False d = math.sqrt( math.pow((self.x-other.x),2) + math.pow((self.y-other.y),2) ) if d < self.proximity_tolerance : return True else : return False def check_target_proximity(self,target): '''Check if the moving bubble is in proximity of this object. As soon as it is there, mark the time. We do not want to reset this time just wait until this bubble dies due to inactivity in its region.''' prox = self.object_in_proximity(target) if self.target_in_proximity is None and prox is True : self.target_in_proximity = time.time() # if prox is False : # self.target_in_proximity = None def draw(self): if not self.visible : return d=int(self.size) 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)) self.alpha = 255 - int(self.age()*self.FADE) if self.alpha < 5 : self.alpha = 1 self.visible = False def age(self): return time.time() - self.time def real_age(self): return time.time() - self.const_time def age_weight(self): age_s = time.time() - self.const_time if age_s < self.inactivity_delay : return 1.0 return 1.0 / ((age_s-self.inactivity_delay+1)*10.0) def proximity_weight(self): if self.target_in_proximity == None : return 1.0 age_s = time.time() - self.target_in_proximity if age_s < self.proximity_delay : return 1.0 return 1.0 / ((age_s-self.proximity_delay+1)*10.0) def increment(self,count): self.time = time.time() self.count = count # self.size = int(BLOBSIZE * int(self.count/1.5)) self.to = int(BLOBSIZE * int(self.count/1.5)) if self.to < 250 : self.start_animate() def get_distance(self,x,y): return math.sqrt( math.pow((self.x-x),2) + math.pow((self.y-y),2) ) def fade(self,fade): if not fade : self.alpha = 255 self.FADE = fade def reset_time(self): self.time = time.time() def start_animate(self): self.thread = Thread(target = self.animate) self.thread.daemon = True self.thread.start() def animate(self): '''Animate the way bubbles are grown.''' while self.size < self.to : self.size += 1 time_inc = 20.0 / (pow(1.2, self.to-self.size) * 200.0) time.sleep(0.02+time_inc) def force(self,other): '''Calculate the force between this object and another''' if other is None : return 0.0,0.0,False captured = False ds = math.pow((self.x-other.x),2) + math.pow((self.y-other.y),2) if ds < 0.005 : return 0.0,0.0,False d = math.sqrt(ds) if d < 0.07 : captured = True # return 0.0,0.0,False m1,m2 = self.size,other.size G = 6.674 * 0.000001 f = - G * (m1*m2) / ds x = f * (other.x-self.x) / d y = f * (other.y-self.y) / d return x,y,captured class BlobTrail(object): def __init__(self,bg,x,y,color=(255,255,255),mood=None,fade=FADE): self.x = x self.y = y self.xs = x * X self.ys = Y - (y * Y) self.bg = bg self.size = BLOBSIZE self.time = time.time() self.alpha = 255 self.c = color self.count = 1 self.visible = True self.FADE = fade if mood and mood.color : self.c = mood.color self.title = mood.word def __cmp__(self,other): d = math.sqrt( math.pow((self.x-other.x),2) + math.pow((self.y-other.y),2) ) if d < DIST : return 0 else : return -1 def draw(self): if not self.visible : return d=int(self.size) self.bg.blit(gradients.radial(self.size, (self.c[0],self.c[1],self.c[2],self.alpha), (0,0,0,0)), (self.xs-d,self.ys-d)) self.alpha = 255 - int(self.age()*self.FADE) if self.alpha < 5 : self.alpha = 1 self.visible = False def age(self): return time.time() - self.time def increment(self,count): self.time = time.time() self.count = count # self.size = int(BLOBSIZE * int(self.count/1.5)) self.to = int(BLOBSIZE * int(self.count/1.5)) self.start_animate() def get_distance(self,x,y): return math.sqrt( math.pow((self.x-x),2) + math.pow((self.y-y),2) ) def fade(self,fade): if not fade : self.alpha = 255 self.FADE = fade def reset_time(self): self.time = time.time() def start_animate(self): self.thread = Thread(target = self.animate) self.thread.daemon = True self.thread.start() def animate(self): '''Animate the way bubbles are grown.''' while self.size < self.to : self.size += 1 time_inc = 20.0 / (pow(1.2, self.to-self.size) * 200.0) time.sleep(0.02+time_inc) class Mood(): def __init__(self,word,x,y): self.word = word self.x = (float(x)/ 2.0) + 0.5 self.y = (float(y)/ 2.0) + 0.5 self.color = [] def get_distance(self,x,y): return math.sqrt( math.pow((self.x-x),2) + math.pow((self.y-y),2) ) def __repr__(self): return "Mood(%s,%3.2f,%3.2f)" %(self.word,self.x,self.y) class VisualClient(object): '''Main visualisation client.''' global moods,blobTrail def __init__(self): # self.conn = ht.HTTPConnection("192.168.2.184:8030") # self.conn = ht.HTTPConnection("138.37.95.215") self.s_age = 10 self.s_dist = DIST self.s_ninp = 18 pg.init() pg.font.init() # fontObj = pg.font.Font("freesansbold.ttf",18) white = ( 255, 255, 255) black = ( 0,0,0) self.fpsClock = pg.time.Clock() self.screen = pg.display.set_mode((X, Y)) pg.display.set_caption('Mood Conductor') self.bg = pg.Surface(self.screen.get_size()) self.bg = self.bg.convert() self.bg.fill((0,0,0)) pg.display.set_gamma(100.0) self.scol = (0,255,0,255) self.ecol = (0,0,0,255) coordstxt = "test" self.blobs = [] #self.moods = [] self.bt=[] self.movingBlob = None self.read_mood_data() self.FADE = FADE self.indicators = { "conn":Indicator(self.bg,(0.98,0.02)), # connection active "update":Indicator(self.bg,(0.96,0.02)), # update thread executing "data":Indicator(self.bg,(0.94,0.02)), # data status changed "receive":Indicator(self.bg,(0.92,0.02)), # data received "grow":Indicator(self.bg,(0.90,0.02)), # blob growth active "ignore":Indicator(self.bg,(0.88,0.02),True), # little AI: ignore some clusters in certain condition "suspend":Indicator(self.bg,(0.86,0.02),True), # prevent adding new blobs (key: d) "config":Indicator(self.bg,(0.84,0.02),True), # sending config data "fade":Indicator(self.bg,(0.82,0.02),True)} # fade on/off (key: s) self.thread = None self.running = False self.fullscreen = False self.init_reconnect = False self.suspend = False pass def read_mood_data(self): '''Read the mood position and color information form csv file.''' # file = 'moods.csv' file = '../tags/mc_moodtags_lfm_curated2.csv' with open(file) as mf: data = mf.readlines()[1:] for line in data : l = line.split(',') l = map(lambda x:x.replace("'","").strip(),l) mood = Mood(l[0],l[1],l[2]) moods.append(mood) print moods for mood in moods: print"['%s',%2.2f,%2.2f,0,0]," %(mood.word,mood.x,mood.y) with open('colors.txt') as ff: data = ff.readlines()[1:] data = map(lambda x: x.split(','),data) for mood in moods : d = cd = sys.float_info.max for colors in data : d = mood.get_distance(float(colors[0]),float(colors[1])) if d < cd : cd = d # mood.color = tuple(map(lambda x: int(pow(math.atan((float(x)/7.0)),12.5)),(colors[2],colors[3],colors[4]))) mood.color = self.set_color(tuple(map(lambda x: int(x),(colors[2],colors[3],colors[4])))) return True def set_color(self,color): '''Move to HLS colour space and manipulate saturation there.''' # TODO: ideally, we need a non-linear compressor of the lightness and saturation values r,g,b = map(lambda x: (1.0*x/255.0), color) h,l,s = cs.rgb_to_hls(r,g,b) s = 1.0 #1.0 - (1.0 / pow(50.0,s)) l = 1.0 - (1.0 / pow(20.0,l)) #0.6 r,g,b = map(lambda x: int(x*255), cs.hls_to_rgb(h,l,s)) return r,g,b def start_update_thread(self): '''Start the thread that reads data from the server.''' time.sleep(SERVER_UPDATE_INTERVAL) self.thread = Thread(target = self.update_thread) self.thread.daemon = True self.running = True self.thread.start() print "OK. Update thread started." def stop_update_thread(self): '''Stop the thread and allow some time fot the connections to close.''' self.running = False try : self.thread.join(2) self.indicators["conn"].off() self.indicators["update"].off() except : print "No update thread to join." def update_thread(self): '''The server update thread''' print "Thread reporting..." while self.running : # self.update() try : self.update() # self.indicators["update"].visible = True except Exception, e: if str(e).strip() : print "Exception: ", str(e), type(e), len(str(e).strip()) self.indicators["conn"].off() # self.indicators["update"].visible = False try : time.sleep(SERVER_UPDATE_INTERVAL) except : if str(e).strip() : print "Exception: ", str(e), type(e), len(str(e).strip()) self.indicators["conn"].off() def update(self): '''Update the blob list from the server. This should be in a thread.''' # global xavg, yavg, countavg # indicate connection health by toggling an indictor self.indicators["update"].toggle() # delete invisibles for blob in self.blobs : if not blob.visible : self.blobs.remove(blob) # get new coordinates from the server self.conn.putrequest("GET","/moodconductor/result", skip_host=True) self.conn.putheader("Host", "www.isophonics.net") self.conn.endheaders() res = self.conn.getresponse() data = res.read() data = eval(data) if not data : self.conn.close() self.indicators["data"].toggle() return False tempx = 0 tempy = 0 tempcount = 0 for d in data : # coordstxt = "x:%s y:%s c:%s" %d x,y,count = d tempx = tempx + x*count tempy = tempy + y*count tempcount = tempcount + count self.add_blob(x,y,count) self.indicators["receive"].toggle() xavg = tempx/tempcount yavg = tempy/tempcount countavg = tempcount/len(data) # print xavg, yavg, countavg # if not self.blobs : # self.add_blob(xavg,yavg,countavg) # self.indicators["receive"].toggle() self.conn.close() self.blobs = self.blobs[:NBLOBS] return True def add_blob(self,x,y,count=1): '''Insert a blob to the list of blobs''' # find mood correxponding to x,y if self.suspend : return None cmood = None d = cd = sys.float_info.max for mood in moods : d = mood.get_distance(x,y) if d < cd : cd = d cmood = mood # create new blob or increase click count on existing one new = Blob(self.bg,x,y,mood=cmood,fade=self.FADE) if self.movingBlob == None : self.movingBlob = MovingBlob(self.bg,x,y,mood=cmood,fade=self.FADE) if not new in self.blobs : self.blobs.insert(0,new) elif count > self.blobs[self.blobs.index(new)].count: self.blobs[self.blobs.index(new)].increment(count) self.indicators["grow"].toggle() pass def draw(self): '''Draw all objects''' global xavg, yavg, countavg self.bg.fill((0,0,0,1)) # self.bg.blit(gradients.radial(19, self.scol, self.ecol), (rect_x,rect_y)) forces = [] l = copy.copy(self.blobs) l.reverse() xt,yt = 0.0,0.0 bs = 1 c = 1 # captured_by = None # calculate exponential weighted average of the visible blobs ignore = False for blob in l : blob.draw() c = c + blob.count # aw = blob.age_weight() aw = blob.proximity_weight() if aw < 1.0 : ignore = True w = math.pow(blob.size+(blob.alpha/2.0),7) * aw xt = xt + blob.x * w yt = yt + blob.y * w bs = bs + w if self.movingBlob != None : blob.check_target_proximity(self.movingBlob) xavg = xt / bs yavg = yt / bs # countavg = bs/(len(l)+1) countavg = int(c/(len(l)+1)) if ignore : self.indicators["ignore"].on() else : self.indicators["ignore"].off() # compute gravity force # if self.movingBlob != None : # x,y,c = blob.force(self.movingBlob) # forces.append((x,y)) # if c : captured_by = blob # tx,ty = reduce(lambda a,b:(a[0]+b[0],a[1]+b[1]), forces, (0.5,0.5)) # print tx,ty # if tx>1.0 : tx = 1.0 # if tx<0.0 : tx = 0.0 # if ty>1.0 : ty = 1.0 # if ty<0.0 : ty = 0.0 # xavg,yavg = tx,ty # if tx <= 1.0 and tx >= 0.0 and ty <= 1.0 and ty >= 0.0 : # xavg,yavg = tx,ty # countavg = 15 # print tx,ty # else : # print "out of bounds:",tx,ty # if captured_by != None : # xavg,yavg = captured_by.x,captured_by.y l = copy.copy(self.bt) l.reverse() for trail in l: trail.draw() if self.movingBlob != None : # self.movingBlob.resize(countavg) self.movingBlob.set_target(xavg,yavg) self.movingBlob.draw() new = BlobTrail(self.bg,self.movingBlob.x,self.movingBlob.y,mood=self.movingBlob.mood,fade=18) self.bt.insert(0,new) # axis pg.draw.line(self.bg, (G,G,G), (int(X/2.0),0),(int(X/2.0),Y), 1) pg.draw.line(self.bg, (G,G,G), (0,int(Y/2.0)),(X,int(Y/2.0)), 1) # indicators for i in self.indicators.itervalues() : i.draw() def read_keys(self): '''Read keys''' for event in pg.event.get() : # Quit (event) if event.type == QUIT: self.quit() elif event.type == KEYDOWN : # Post Quit: Esc, q if event.key == K_ESCAPE or event.key == K_q : pg.event.post(pg.event.Event(QUIT)) # Reset: Space elif event.key == K_SPACE : if not self.blobs : self.bt = [] self.blobs = [] # Random : r elif event.key == K_r : self.add_blob(random(),random(),count=5) # Connection : c elif event.key == K_c : self.init_reconnect = True self.indicators["conn"].off() # Fullscreen: f elif event.key == K_f : # pg.display.toggle_fullscreen() self.toggle_screen_mode() # Toggle suspend: d elif event.key == K_d : if self.suspend : print "suspend off" self.indicators["suspend"].off() self.suspend = False else: print "suspend on" self.indicators["suspend"].on() self.suspend = True # Toggle fade: s elif event.key == K_s : if self.FADE > 0: print "fade off" self.indicators["fade"].off() self.FADE = 0 for blob in self.blobs : blob.fade(0) else: print "fade on" self.indicators["fade"].on() self.FADE = FADE for blob in self.blobs : blob.fade(FADE) blob.reset_time() # inc age elif event.key == K_1 : self.s_age += 1 self.update_server_config(self.s_age,self.s_dist,self.s_ninp) # dec age elif event.key == K_2 : self.s_age -= 1 self.update_server_config(self.s_age,self.s_dist,self.s_ninp) # inc dist elif event.key == K_3 : self.s_dist += 0.025 self.update_server_config(self.s_age,self.s_dist,self.s_ninp) # dec dist elif event.key == K_4 : self.s_dist -= 0.025 if self.s_dist < 0.025 : self.s_dist = 0.025 self.update_server_config(self.s_age,self.s_dist,self.s_ninp) # inc ninp elif event.key == K_5 : self.s_ninp += 1 self.update_server_config(self.s_age,self.s_dist,self.s_ninp) # dec ninp elif event.key == K_6 : self.s_ninp -= 1 if self.s_ninp < 2 : self.s_ninp = 2 self.update_server_config(self.s_age,self.s_dist,self.s_ninp) # choose different app and restart server elif event.key == K_9 : self.choose_app() pass pass def toggle_screen_mode(self): '''Go back and forth between full screen mode.''' if self.fullscreen == False: globals()['_X'] = globals()['X'] globals()['_Y'] = globals()['Y'] globals()['X'] = XF globals()['Y'] = YF self.screen = pg.display.set_mode((X, Y)) # self.screen = pg.display.set_mode((0,0),pg.FULLSCREEN) self.fullscreen = True self.bg = pg.Surface(self.screen.get_size()) self.bg = self.bg.convert() self.bg.fill((0,0,0)) for i in self.indicators.itervalues() : i.reinit(self.bg) i.draw() if self.movingBlob != None : self.movingBlob.reinit(self.bg) self.movingBlob.draw() else : globals()['X'] = globals()['_X'] globals()['Y'] = globals()['_Y'] self.screen = pg.display.set_mode((X, Y)) self.fullscreen = False self.bg = pg.Surface(self.screen.get_size()) self.bg = self.bg.convert() self.bg.fill((0,0,0)) for i in self.indicators.itervalues() : i.reinit(self.bg) i.draw() if self.movingBlob != None : self.movingBlob.reinit(self.bg) self.movingBlob.draw() pg.display.toggle_fullscreen() def run(self): # setup connection self.connect() # main loop while True : # pg.draw.circle(screen, pg.Color(255,0,0), (300,50),20,0) # screen.blit(gradients.radial(99, scol, ecol), (401, 1)) self.read_keys() # Draw self.draw() # update display self.screen.blit(self.bg, (0, 0)) pg.display.flip() self.fpsClock.tick(FRAMERATE) if self.init_reconnect: self.reconnect() return True def choose_app(self): '''Experimental function for chaging served apps remotely. Disabled for now...''' return False try : print "Changing app and restarting... the connection will be lost." self.conn.putrequest("GET","/moodconductor/changeapp", skip_host=True) self.conn.putheader("Host", "www.isophonics.net") self.conn.endheaders() res = self.conn.getresponse() res.read() except : pass def configure_server(self): '''Send the server some configuration data.''' # age = 10.0 # dist = DIST # ninp = 18 self.update_server_config(self.s_age,self.s_dist,self.s_ninp) def update_server_config(self,age,dist,ninp,retry = 3): '''Send the server some configuration data.''' self.indicators["config"].on().now(self.screen) try : print "Sending configuration data." self.conn.putrequest("GET","/moodconductor/config?age=%(age)s&dist=%(dist)s&ninp=%(ninp)s" %locals(), skip_host=True) self.conn.putheader("Host", "www.isophonics.net") self.conn.endheaders() res = self.conn.getresponse() res.read() if not res.status == 200 : print "Server response:", res.status, res.reason self.indicators["conn"].off() time.sleep(0.5) self.conn.putrequest("GET","/moodconductor/getconf", skip_host=True) self.conn.putheader("Host", "www.isophonics.net") self.conn.endheaders() res = self.conn.getresponse() if not res.status == 200 : print "Server response:", res.status, res.reason self.indicators["conn"].off() # self.indicators["config"].on() print "Server configuration:", res.read() self.indicators["config"].off() self.indicators["conn"].on() except: print "Failed to send configuration data." time.sleep(2) retry -= 1 self.update_server_config(age,dist,ninp,retry) def connect(self,retry = 5): '''Connect to the server and test connection.''' if not retry : print "Server unreachable" pg.quit() raise SystemExit if retry < 5 : time.sleep(3) try : print "connecting to server..." self.conn = ht.HTTPConnection(IP,timeout=HTTP_TIMEOUT) self.indicators["conn"].on() except : self.indicators["conn"].off() self.connect(retry = retry-1) print "connection failed." try: print "Testing connection." # self.conn.putrequest("GET","/moodconductor/index.html", skip_host=True) self.conn.putrequest("GET","/moodconductor/test", skip_host=True) self.conn.putheader("Host", "www.isophonics.net") self.conn.endheaders() res = self.conn.getresponse() print res.read() if res.status == 200 : self.indicators["conn"].on() else : print "Server response:", res.status, res.reason self.indicators["conn"].off() if not hasattr(self,"noretry") and raw_input("Go offline? ") in ['y',''] : return False else : print "Failed. retrying." self.noretry = None self.connect(retry = retry-1) except Exception as e: print "Exception while testing connection.", e self.indicators["conn"].off() # comment out in offline mode if not hasattr(self,"noretry") and raw_input("Go offline? ") in ['y',''] : return False else : self.noretry = None self.connect(retry = retry-1) self.configure_server() print "OK. Starting update thread..." self.start_update_thread() return True def reconnect(self): '''Called when c is pressed.''' self.indicators["config"].on().now(self.screen) self.init_reconnect = False self.stop_update_thread() time.sleep(1) try : self.conn.close() except : self.indicators["conn"].off() try : self.conn = ht.HTTPConnection(IP,timeout=HTTP_TIMEOUT) self.conn.connect() self.indicators["conn"].on() print "Reconnected." except : self.indicators["conn"].off() print "Error while reconnecting." self.start_update_thread() self.indicators["config"].off().now(self.screen) def quit(self): print "Quitting.." self.indicators["conn"].off() self.stop_update_thread() self.conn.close() pg.quit() sys.exit() def main(): v = VisualClient() v.run() if __name__ == '__main__': pass main()