def Set(self, check=1, redo=1, updateOwnGui=True, **kw): """set data for this object: Set polylines's vertices check=1 : verify that all the keywords present can be handle by this func redo=1 : append self to viewer.objectsNeedingRedo updateOwnGui=True : allow to update owngui at the end this func """ redoFlags = apply(Geom.Set, (self, check, 0), kw) c = self.vertexSet.vertices rad = kw.get('radii') if rad is not None: values = valideFloat(rad, len(c)) if values is not None: self.radii = values redoFlags |= self._redoFlags['redoDisplayListFlag'] ang = getkw(kw, 'angles') if ang is not None: values = valideFloat(ang, len(c)) if values is not None: self.angles = values redoFlags |= self._redoFlags['redoDisplayListFlag'] quality = getkw(kw, 'quality') if quality is not None: assert type(quality) == types.IntType and quality > 1 self.nsegments = quality redoFlags |= self._redoFlags['redoDisplayListFlag'] return self.redoNow(redo, updateOwnGui, redoFlags)
def Set(self, check=1, redo=1, updateOwnGui=True, **kw): """set data for this object: Set polylines's vertices check=1 : verify that all the keywords present can be handle by this func redo=1 : append self to viewer.objectsNeedingRedo updateOwnGui=True : allow to update owngui at the end this func """ redoFlags = apply( Geom.Set, (self, check, 0), kw ) c = self.vertexSet.vertices rad = kw.get('radii') if rad is not None: values = valideFloat(rad, len(c)) if values is not None: self.radii = values redoFlags |= self._redoFlags['redoDisplayListFlag'] ang = getkw(kw, 'angles') if ang is not None: values = valideFloat(ang, len(c)) if values is not None: self.angles = values redoFlags |= self._redoFlags['redoDisplayListFlag'] quality = getkw(kw, 'quality') if quality is not None: assert type(quality)==types.IntType and quality > 1 self.nsegments = quality redoFlags |= self._redoFlags['redoDisplayListFlag'] return self.redoNow(redo, updateOwnGui, redoFlags)
def __init__(self, **kw): if __debug__: if hasattr(DejaVu, 'functionName'): DejaVu.functionName() self.Reset() self.Set(factor=getkw(kw, 'factor'), pattern=getkw(kw, 'pattern')) if len(kw): print 'WARNING5: Keyword(s) %s not used' % kw.keys()
def __init__(self, **kw): if __debug__: if hasattr(DejaVu, 'functionName'): DejaVu.functionName() self.Reset() self.Set( factor = getkw(kw, 'factor'), pattern = getkw(kw, 'pattern')) if len(kw): print 'WARNING5: Keyword(s) %s not used' % kw.keys()
def __init__(self, geom, **kw): if __debug__: if hasattr(DejaVu, 'functionName'): DejaVu.functionName() self.geom = geom self.Reset() self.Set(factor=getkw(kw, 'factor'), unit=getkw(kw, 'unit'), lineWidth=getkw(kw, 'lineWidth'), color=getkw(kw, 'color')) if len(kw): print 'WARNING3: Keyword(s) %s not used' % kw.keys()
def __init__(self, geom, **kw): if __debug__: if hasattr(DejaVu, 'functionName'): DejaVu.functionName() self.geom = geom self.Reset() self.Set( factor = getkw(kw, 'factor'), unit = getkw(kw, 'unit'), lineWidth = getkw(kw, 'lineWidth'), color = getkw(kw, 'color') ) if len(kw): print 'WARNING3: Keyword(s) %s not used' % kw.keys()
def Set(self, **kw): if __debug__: if hasattr(DejaVu, 'functionName'): DejaVu.functionName() """Set members""" val = getkw(kw, 'factor') if val: assert type(val).__name__ == 'int' and val > 0 self.factor = val val = getkw(kw, 'pattern') if val: assert val > 0x0000 and val < 0xFFFF self.pattern = val if len(kw): print 'WARNING4: Keyword(s) %s not used' % kw.keys()
def Set(self, **kw): """Set various light model parameters""" self.viewer.currentCamera.Activate() tagModified = True val = getkw(kw, 'tagModified') if val is not None: tagModified = val assert tagModified in [True, False] self._modified = tagModified ambi = getkw(kw, 'ambient') if not ambi is None: if len(ambi)==3 or len(ambi)==4: self.ambient = OneColor( ambi ) GL.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, self.ambient); else: raise ValueError('length of new color must be 3 or 4') localViewer = getkw(kw, 'localViewer') if not localViewer is None: if localViewer in (True,1): GL.glLightModelf(GL.GL_LIGHT_MODEL_LOCAL_VIEWER, GL.GL_TRUE); elif localViewer in (False,0): GL.glLightModelf(GL.GL_LIGHT_MODEL_LOCAL_VIEWER, GL.GL_FALSE); else: raise AttributeError('localViewer can only be True or False') self.localViewer = localViewer twoSide = getkw(kw, 'twoSide') if not twoSide is None: if twoSide in (True,1): GL.glLightModelf(GL.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_TRUE); elif twoSide in (False,0): GL.glLightModelf(GL.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_FALSE); else: raise AttributeError('twoSide can only be True or False') self.twoSide = twoSide self.broadcast() if len(kw): print 'WARNING8: Keyword(s) %s not used' % kw.keys()
def Set(self, **kw): if __debug__: if hasattr(DejaVu, 'functionName'): DejaVu.functionName() """Set members""" val = getkw(kw, 'pattern') if val is not None: assert type(val).__name__ == 'ndarray' assert val.shape[0] == 128 assert val.dtype.char == 'B' self.pattern = val if len(kw): print 'WARNING6: Keyword(s) %s not used' % kw.keys()
def Set(self, check=1, redo=1, updateOwnGui=True, **kw): """set data for this object: add faces (polygon or lines) to this object check=1 : verify that all the keywords present can be handle by this func redo=1 : append self to viewer.objectsNeedingRedo updateOwnGui=True : allow to update owngui at the end this func """ redoFlags = apply( Geom.Set, (self, check, 0), kw) c = self.vertexSet.vertices rad = kw.get('radii') if rad is not None: values = valideFloat(rad, len(c)) if values is not None: redoFlags |= self._redoFlags['redoDisplayListFlag'] self.radii = values ang = getkw(kw, 'angles') if ang is not None: values = valideFloat(ang, len(c)) if values is not None: redoFlags |= self._redoFlags['redoDisplayListFlag'] self.angles = values vec = getkw(kw, 'vectors') if vec is not None: if len(vec)==3: self.vectors = [vec] else: assert len(vec)==len(c) for v in vec: assert len(v)==3 self.vectors = vec redoFlags |= self._redoFlags['redoDisplayListFlag'] return self.redoNow(redo, updateOwnGui, redoFlags)
def Set(self, check=1, redo=1, updateOwnGui=True, **kw): """set data for this object: add faces (polygon or lines) to this object check=1 : verify that all the keywords present can be handle by this func redo=1 : append self to viewer.objectsNeedingRedo updateOwnGui=True : allow to update owngui at the end this func """ redoFlags = apply(Geom.Set, (self, check, 0), kw) c = self.vertexSet.vertices rad = kw.get('radii') if rad is not None: values = valideFloat(rad, len(c)) if values is not None: redoFlags |= self._redoFlags['redoDisplayListFlag'] self.radii = values ang = getkw(kw, 'angles') if ang is not None: values = valideFloat(ang, len(c)) if values is not None: redoFlags |= self._redoFlags['redoDisplayListFlag'] self.angles = values vec = getkw(kw, 'vectors') if vec is not None: if len(vec) == 3: self.vectors = [vec] else: assert len(vec) == len(c) for v in vec: assert len(v) == 3 self.vectors = vec redoFlags |= self._redoFlags['redoDisplayListFlag'] return self.redoNow(redo, updateOwnGui, redoFlags)
def Set(self, redo=0,**kw): if __debug__: if hasattr(DejaVu, 'functionName'): DejaVu.functionName() """Set members""" val = getkw(kw, 'factor') if val is not None: assert type(val).__name__ == 'float' self.factor = val val = getkw(kw, 'unit') if val is not None: assert type(val).__name__ == 'float' self.unit = val val = getkw(kw, 'lineWidth') if val is not None: assert val >= 1 self.lineWidth = int(val) val = getkw(kw, 'color') if val is not None: color = OneColor( val ) if color: self.color = color val = getkw(kw, 'lighting') if val is not None: assert val in [False, True] self.lighting = val val = getkw(kw, 'colorAsMaterial') if val is not None: assert val in [False,True] self.colorAsMaterial = val if self.geom.viewer is not None: #self.geom.RedoDisplayList() self.geom.viewer.deleteOpenglList() if len(kw): print 'WARNING2: Keyword(s) %s not used' % kw.keys()
def Set(self, redo=0, **kw): if __debug__: if hasattr(DejaVu, 'functionName'): DejaVu.functionName() """Set members""" val = getkw(kw, 'factor') if val is not None: assert type(val).__name__ == 'float' self.factor = val val = getkw(kw, 'unit') if val is not None: assert type(val).__name__ == 'float' self.unit = val val = getkw(kw, 'lineWidth') if val is not None: assert val >= 1 self.lineWidth = int(val) val = getkw(kw, 'color') if val is not None: color = OneColor(val) if color: self.color = color val = getkw(kw, 'lighting') if val is not None: assert val in [False, True] self.lighting = val val = getkw(kw, 'colorAsMaterial') if val is not None: assert val in [False, True] self.colorAsMaterial = val if self.geom.viewer is not None: #self.geom.RedoDisplayList() self.geom.viewer.deleteOpenglList() if len(kw): print 'WARNING2: Keyword(s) %s not used' % kw.keys()
def Set(self, check=1, **kw): """Set various parameters for texture objects: - image=im set an image as the texture to be used. im can be either a Numeric array of bytes with shape (n x m x d) where d can be either 3 or 4. Im can also be a PIL Image object. If thes image is not of type 'RGB' or 'RGBA' it will be converted to 'RGB' if possible, else ... error ! "The set command will also pad the image to make sure that width and height are powers of 2." (has been tested for Image but not for Numeric array yet - guillaume) """ if __debug__: if check: apply( checkKeywords, (self.name,self.keywords), kw) val = getkw(kw, 'enable') if not val is None: assert val in (viewerConst.YES, viewerConst.NO), "enable can only be YES or NO" self.enabled = val val = getkw(kw, 'wrapS') if not val is None: assert val in (GL.GL_REPEAT, GL.GL_CLAMP) self.wrap[0] = val val = getkw(kw, 'wrapT') if not val is None: assert val in (GL.GL_REPEAT, GL.GL_CLAMP) self.wrap[1] = val val = getkw(kw, 'magFilter') if not val is None: assert val in (GL.GL_NEAREST, GL.GL_LINEAR) self.magFilter = val val = getkw(kw, 'minFilter') if not val is None: assert val in (GL.GL_NEAREST, GL.GL_LINEAR) self.minFilter = val val = getkw(kw, 'genModS') if not val is None: assert val in (GL.GL_OBJECT_LINEAR, GL.GL_EYE_LINEAR, GL.GL_SPHERE_MAP) self.genMod[0] = val val = getkw(kw, 'genModT') if not val is None: assert val in (GL.GL_OBJECT_LINEAR, GL.GL_EYE_LINEAR, GL.GL_SPHERE_MAP) self.genMod[1] = val val = getkw(kw, 'genPlaneS') if not val is None: assert val in (GL.GL_OBJECT_PLANE, GL.GL_EYE_PLANE) self.genPlane[0] = val val = getkw(kw, 'genPlaneT') if not val is None: assert val in (GL.GL_OBJECT_PLANE, GL.GL_EYE_PLANE) self.genPlane[1] = val val = getkw(kw, 'planeS') if not val is None: assert len(val)==4 and type(val[0])==type(0.0), "Plane has to be 4Float vector" self.plane[0] = val val = getkw(kw, 'planeT') if not val is None: assert len(val)==4 and type(val[0])==type(0.0), "Plane has to be 4Float vector" self.plane[1] = val val = getkw(kw, 'level') if not val is None: assert type(val)==type(0) and val >=0 self.level = val val = getkw(kw, 'auto') if not val is None: assert val in (viewerConst.YES,viewerConst.NO), "auto can only be YES or NO" self.auto = val val = getkw(kw, 'envMode') if not val is None: assert val in (GL.GL_MODULATE, GL.GL_REPLACE, GL.GL_DECAL, GL.GL_BLEND ), "envMode can only be GL_MODULATE, GL_DECAL, or GL_BLEND" self.envMode = val val = getkw(kw, 'envColor') if val: col = OneColor(val) if col: self.envColor = col b = getkw(kw, 'border') f = getkw(kw, 'format') im = getkw(kw, 'image') if im is not None: if isinstance(im, Image.Image): lImInstaceImage = True else: lImInstaceImage = False if lImInstaceImage is True: width = im.size[0] height = im.size[1] else: height = im.shape[0] width = im.shape[1] # find smallest power of 2 larger than image size dim1 = 1 dim2 = 1 while dim1 < width: dim1 = dim1 << 1 while dim2 < height: dim2 = dim2 << 1 self.resizeRatio = ( width / float(dim1), height / float(dim2) ) if os.name != 'nt': #sys.platform != 'win32': lMaxTextureSize = GL.glGetInteger(GL.GL_MAX_TEXTURE_SIZE) #print "width", width, height, dim1, dim2, lMaxTextureSize if (dim1 > lMaxTextureSize) or (dim2 > lMaxTextureSize): warnings.warn('texture map too big for this implementation of opengl %d'%lMaxTextureSize) if lImInstaceImage is True: if im.mode !='RGB' and im.mode !='RGBA': im = im.convert('RGB') im = im.transpose(Image.FLIP_TOP_BOTTOM) imstr = im.tostring() imarr = Numeric.fromstring( imstr, 'B') if im.mode=='RGB': imarr.shape = (height, width, 3) elif im.mode=='RGBA': imarr.shape = (height, width, 4) im = imarr if (dim1 != width) or (dim2 != height): if len(im.shape) == 3: newArray = Numeric.zeros( (dim2, dim1, len(im[0][0]) ) ) else: newArray = Numeric.zeros( (dim2, dim1 ) ) for i in range(height): for j in range(width): newArray[i][j] = im[i][j] im = newArray.astype('B') if b: assert type(b)==type(0) else: b = 0 self.border = b if f: assert f in (GL.GL_RGB, GL.GL_RGBA), "format can only be GL_RGB or GL_RGBA" assert type(im).__name__ == 'ndarray' assert im.dtype.char == 'B' if im.shape[-1] == 3: if f and f != GL.GL_RGB: raise ValueError("bad image format") self.format = GL.GL_RGB elif im.shape[-1] == 4: if f and f != GL.GL_RGBA: raise ValueError("bad image format") self.format = GL.GL_RGBA for o in self.objects: o.transparent=1 o.inheritMaterial=0 if self.viewer: self.viewer.objectsNeedingRedo[o] = None l = len(im.shape) if l==2: w=im.shape[0] - 2*b q, r = divmod(math.log(w)/math.log(2), 1) if r != 0.0: raise ValueError("Image width must be 2**m +2b") self.dim = GL.GL_TEXTURE_1D self.image = im self.width = im.shape[0] elif l==3: w=im.shape[0] - 2*b q, r = divmod(math.log(w)/math.log(2), 1) if r != 0.0: raise ValueError("Image width must be 2**m +2b") h=im.shape[1] -2*b q, r = divmod(math.log(h)/math.log(2), 1) if r != 0.0: raise ValueError("Image height must be 2**m +2b") self.dim = GL.GL_TEXTURE_2D self.image = im self.width = im.shape[1] self.height = im.shape[0] else: raise ValueError("Bad shape for image") if self.viewer: self.viewer.deleteOpenglList() self.viewer.Redraw()
def Set(self, **kw): """Set various clipping plane parameters""" self.hasBeenCurrent = True # remember the light has been changed tagModified = True val = getkw(kw, 'tagModified') if val is not None: tagModified = val assert tagModified in [True, False] self._modified = tagModified val = getkw(kw, 'enabled') if not val is None: if val is True: self._Enable(1) elif val is False: self._Disable() else: raise AttributeError('enable can only be True or False') self.enabled = val val = getkw(kw, 'name') if not val is None: self.name = val val = getkw(kw, 'visible') if not val is None: if val in [False, True]: self.visible = val else: raise AttributeError('visible can only be 0 or 1') val = getkw(kw, 'color') if not val is None: col = OneColor(val) if col: self.color = col val = getkw(kw, 'lineWidth') if not val is None: try: int(val) except: raise ValueError('lineWidth must be a positive int') if val >= 1: self.lineWidth = int(val) else: raise ValueError('lineWidth must be a positive int') # val = getkw(kw, 'antialiased') # if not val is None: # if val in (True, False) : # self.antialiased = val # else: raise ValueError ('antialiased can only be YES or NO') val = getkw(kw, 'rotation') if not val is None: self.rotation = Numeric.identity(4, 'f').ravel() mat = Numeric.reshape(Numeric.array(val), (16, )).astype('f') self.ConcatRotation(mat) val = getkw(kw, 'translation') if not val is None: self.translation = Numeric.zeros((3, ), 'f') mat = Numeric.reshape(Numeric.array(val), (3, )).astype('f') self.ConcatTranslation(mat) val = getkw(kw, 'scale') if not val is None: self.SetScale(val) val = getkw(kw, 'pivot') if not val is None: self.SetPivot(val) if len(kw): print 'WARNING1: Keyword(s) %s not used' % kw.keys() if self.object.viewer: self.object.viewer.objectsNeedingRedo[self.object] = None self.object.viewer.Redraw()
def Set(self, **kw): """Set various clipping plane parameters""" self.hasBeenCurrent = True # remember the light has been changed tagModified = True val = getkw(kw, 'tagModified') if val is not None: tagModified = val assert tagModified in [True, False] self._modified = tagModified val = getkw(kw, 'enabled') if not val is None: if val is True: self._Enable(1) elif val is False: self._Disable() else: raise AttributeError('enable can only be True or False') self.enabled = val val = getkw(kw, 'name') if not val is None: self.name = val val = getkw(kw, 'visible') if not val is None: if val in [False,True]: self.visible = val else: raise AttributeError('visible can only be 0 or 1') val = getkw(kw, 'color') if not val is None: col = OneColor( val ) if col: self.color = col val = getkw(kw, 'lineWidth') if not val is None: try: int(val) except: raise ValueError ('lineWidth must be a positive int') if val>=1: self.lineWidth = int(val) else: raise ValueError ('lineWidth must be a positive int') # val = getkw(kw, 'antialiased') # if not val is None: # if val in (True, False) : # self.antialiased = val # else: raise ValueError ('antialiased can only be YES or NO') val = getkw(kw, 'rotation') if not val is None: self.rotation = Numeric.identity(4, 'f').ravel() mat = Numeric.reshape(Numeric.array(val), (16,)).astype('f') self.ConcatRotation(mat) val = getkw(kw, 'translation') if not val is None: self.translation = Numeric.zeros( (3,), 'f') mat = Numeric.reshape(Numeric.array(val), (3,)).astype('f') self.ConcatTranslation(mat) val = getkw(kw, 'scale') if not val is None: self.SetScale( val ) val = getkw(kw, 'pivot') if not val is None: self.SetPivot( val ) if len(kw): print 'WARNING1: Keyword(s) %s not used' % kw.keys() if self.object.viewer: self.object.viewer.objectsNeedingRedo[self.object] = None self.object.viewer.Redraw()
def Set(self, **kw): """ set light values. For direction, position, and spot direction, vals are given in absolute coordinates (independent of camera or object). For these three values, the flag is set to 1 when they are changed. """ #print "Light.Set" self.hasBeenCurrent = True # remember the light has been changed tagModified = True val = getkw(kw, 'tagModified') if val is not None: tagModified = val assert tagModified in [True, False] self._modified = tagModified self.viewer.currentCamera.Activate() val = getkw(kw, 'ambient') if not val is None: #self.ambient = OneColor( val ) #GL.glLightfv(self.num, GL.GL_AMBIENT, self.ambient ) if len(val)==3 or len(val)==4: self.ambient = OneColor( val ) GL.glLightModelfv(GL.GL_LIGHT_MODEL_AMBIENT, self.ambient) GL.glLightfv(self.num, GL.GL_AMBIENT, self.ambient) # needed for mesa else: raise ValueError('length of new color must be 3 or 4') val = getkw(kw, 'diffuse') if not val is None: self.diffuse = OneColor( val ) GL.glLightfv(self.num, GL.GL_DIFFUSE, self.diffuse ) val = getkw(kw, 'specular') if not val is None: self.specular = OneColor( val ) GL.glLightfv(self.num, GL.GL_SPECULAR, self.specular ) val = getkw(kw, 'direction') if not val is None: val = list(val) if len(val)==3: val += [0.] assert len(val)==4 self.direction = val self.direction[3] = 0.0 self.dirFlag = 1 # tell the camera to redraw this light self.positional = False val = getkw(kw, 'position') if not val is None: val = list(val) if len(val)==3: val += [1.] assert len(val)==4 self.position = val self.position[3] = 1.0 self.posFlag = 1 # tell the camera to redraw this light self.positional = True val = getkw(kw, 'spotDirection') if not val is None: val = list(val) if len(val)==3: val += [0.] assert len(val)==4 self.spotDirection = val self.spotDirection[3] = 0.0 self.spotFlag = 1 # tell the camera to redraw this light val = getkw(kw, 'spotExponent') if not val is None: self.spotExponent = float(val) GL.glLightfv(self.num, GL.GL_SPOT_EXPONENT, [self.spotExponent]) val = getkw(kw, 'spotCutoff') if not val is None: if val > 180.: raise ValueError("spotCutoff must be in [0., 90.] or 180.") self.spotCutoff = float( val ) GL.glLightfv(self.num, GL.GL_SPOT_CUTOFF, [self.spotCutoff] ) val = getkw(kw, 'constantAttenuation') if not val is None: self.constantAttenuation = float( val ) if self.constantAttenuation < 0.0: raise ValueError("constantAttenuation must be >= 0.0") GL.glLightfv(self.num, GL.GL_CONSTANT_ATTENUATION, [self.constantAttenuation] ) val = getkw(kw, 'linearAttenuation') if not val is None: self.linearAttenuation = float( val ) if self.linearAttenuation < 0.0: raise ValueError("linearAttenuation must be >= 0.0") GL.glLightfv(self.num, GL.GL_LINEAR_ATTENUATION, [self.linearAttenuation] ) val = getkw(kw, 'quadraticAttenuation') if not val is None: self.quadraticAttenuation = float( val ) if self.quadraticAttenuation < 0.0: raise ValueError("quadraticAttenuation must be >= 0.0") GL.glLightfv(self.num, GL.GL_QUADRATIC_ATTENUATION, [self.quadraticAttenuation] ) val = getkw(kw, 'positional') if not val is None: if val is True: self.position[3] = 1.0 elif val is False: self.position[3] = 0.0 else: raise AttributeError('positional can only be True or False') self.positional = val val = getkw(kw, 'enabled') if not val is None: if val in (True, 1): GL.glEnable(self.num) elif val in (False, 0): GL.glDisable(self.num) else: raise AttributeError('enabled can only be True or False') self.enabled = val val = getkw(kw, 'visible') if not val is None: if val in (True, False): self.visible = val else: raise AttributeError('visible can only be True or False') val = getkw(kw, 'lineWidth') if not val is None: if val >= 1: self.lineWidth = int(val) else: raise AttributeError('lineWidth has to be >= 1') val = getkw(kw, 'length') if not val is None: if val > 0.0: self.length = float ( val ) else: raise AttributeError('length has to be > 0.0') # val = getkw(kw, 'antialiased') # if not val is None: # if val in (True, False): # self.antialiased = val # else: raise ValueError ('antialiased can only be True or False') val = getkw(kw, 'rotation') if not val is None: mat = Numeric.reshape(Numeric.array(val), (16,)).astype('f') self.rotation = mat val = getkw(kw, 'translation') if not val is None: mat = Numeric.reshape(Numeric.array(val), (3,)).astype('f') self.translation = mat val = getkw(kw, 'scale') if not val is None: mat = Numeric.reshape(Numeric.array(val), (3,)).astype('f') self.scale = mat val = getkw(kw, 'pivot') if not val is None: mat = Numeric.reshape(Numeric.array(val), (3,)).astype('f') self.pivot = mat if len(kw): print 'WARNING9: Keyword(s) %s not used' % kw.keys() #guillaume vareille 9/29/2005 : # was c = self.viewer.cameras[0] # was generating alternativly good and wrong rendering when 2 cameras c = self.viewer.currentCamera # force light to be update in viewer c.Redraw() # brodcast to other application that want to know about that light # using aftere does not seem to make it better #c.after_idle(self.broadcast) self.broadcast()