class VectEntry(Tkinter.Frame): """ this widget is a specialized Entry for typing in vector values. A setValue() method has been added which checks that the input is correct. The vector is used *as is* and will not be normalized """ def __init__(self, master=None, label=None, width=18, callback=None, **kw): Tkinter.Frame.__init__(self, master) Tkinter.Pack.config(self) self.var = Tkinter.StringVar() self.frame = Tkinter.Frame(self) if label and label != '': d = {'fg': 'black', 'text': label} self.tklabel = apply(Tkinter.Label, (self.frame, ), d) self.tklabel.pack() kw['width'] = width self.tkwidget = apply(Tkinter.Entry, (self.frame, ), kw) self.tkwidget.bind('<Return>', self.setValue_cb) self.tkwidget.pack() self.tkwidget['textvariable'] = self.var self.point = [0., 0, 0] self.text = '0 0 0' self.frame.pack(side='top', expand=1) self.myCallback = callback self.callbacks = CallbackManager() if self.myCallback: self.callbacks.AddCallback(self.myCallback) def setValue_cb(self, event=None): v = self.var.get() try: val = string.split(v) except: self.updateField() return if val is None or len(val) != 3: self.updateField() return try: oldtext = self.text self.text = v self.point = [] self.point.append(float(val[0])) self.point.append(float(val[1])) self.point.append(float(val[2])) self.text = `self.point[0]`+' '+`self.point[1]`+' '+\ `self.point[2]` self.var.set(self.text) self.callbacks.CallCallbacks(self.point) except: self.text = oldtext self.updateField() def updateField(self): self.var.set(self.text)
class ColorWheel: def __init__(self, master, title=None, callback=None, immediate=1): if not master: master = tkinter.Toplevel() if title is not None: master.title(title) f = self.frame = tkinter.Frame(master) path = __import__('mglutil').__path__ iconfile = os.path.join(path[0], 'gui/BasicWidgets/Tk/cw.ppm') self.iconfile = iconfile self.cwim = tkinter.PhotoImage(file=iconfile, master=master) self.width = self.cwim.width() self.height = self.cwim.height() self.cwcanvas = tkinter.Canvas( f, width=self.width, height=self.height, ###relief='sunken', borderwidth=3) self.cwcanvas.create_image(3, 3, anchor=tkinter.NW, image=self.cwim) self.cwcanvas.pack() self.frame.pack() #self.callback = None self.cbManager = CallbackManager() if callback: if type(callback) in [ListType, TupleType]: list(map(self.cbManager.AddCallback, callback)) else: self.cbManager.AddCallback(callback) self.afterID = None self.immediate = immediate self.x = 0 self.y = 0 self.radius = 55 cx = self.cx = self.width / 2 + 3 cy = self.cy = self.height / 2 + 3 self.cursor = self.cwcanvas.create_line(cx - 3, cy - 3, cx - 3, cy + 3, cx + 3, cy + 3, cx + 3, cy - 3, cx - 3, cy - 3) self.hsvColor = [1., 1., 1.] self.cwcanvas.bind('<ButtonPress-1>', self.mouse1Down) #self.cursor = self.cwcanvas.create_line(cx, cy, cx-55, cy) def _MoveCursor(self, x, y, trigger=1): # find the saturation based on distance s = math.sqrt(x * x + y * y) / self.radius if s > 1.0: x = x / s y = y / s s = 1.0 # now find the hue based on the angle if x or y: angle = math.atan2(y, x) if angle < 0.0: angle = angle + (2. * math.pi) h = 1. - angle / (2.0 * math.pi) else: h = 0 # check if redraw and callback are needed if self.hsvColor[0] != h or self.hsvColor[1] != s: if trigger == 1: self.hsvColor[0] = h self.hsvColor[1] = s cx = self.cx + x cy = self.cy + y self.cwcanvas.coords(self.cursor, cx - 3, cy - 3, cx - 3, cy + 3, cx + 3, cy + 3, cx + 3, cy - 3, cx - 3, cy - 3) if self.cbManager.callbacks: self.cbManager.CallCallbacks(self.get('RGB')) def mouse1Down(self, event=None): self.cwcanvas.bind('<B1-Motion>', self.mouse1Move) self.cwcanvas.bind('<ButtonRelease-1>', self.mouse1Up) self._MoveCursor(event.x - self.cx, event.y - self.cy) def mouse1Move(self, event=None): if self.immediate: if self.afterID is not None: self.cwcanvas.after_cancel(self.afterID) self.afterID = None else: self.afterID = self.cwcanvas.after(15, self._MoveCursor, event.x - self.cx, event.y - self.cy) else: self._MoveCursor(event.x - self.cx, event.y - self.cy, trigger=0) def mouse1Up(self, event=None): self._MoveCursor(event.x - self.cx, event.y - self.cy) self.cwcanvas.unbind('<B1-Motion>') self.cwcanvas.unbind('<ButtonRelease-1>') def get(self, mode='HSV'): """Get the current color""" if mode == 'RGB': rgb = ToRGB(self.hsvColor) #return OneColor(rgb) return rgb elif mode == 'HSV': #return OneColor(self.hsvColor) return self.hsvColor elif mode == 'HEX': col = numpy.array(ToRGB(self.hsvColor[:]), 'f') * 255 return ToHEX(col) def set(self, color, mode='HSV', trigger=1): """Set the current color""" assert len(color) == 3 #color = OneColor(color) if mode == 'RGB': color = ToHSV(color[:]) self.hsvColor = list(color[:]) # update cursor rad = self.hsvColor[1] * self.radius angle = 2.0 * math.pi * (1. - self.hsvColor[0]) cx = self.cx + int(rad * math.cos(angle)) cy = self.cy + int(rad * math.sin(angle)) self.cwcanvas.coords(self.cursor, cx - 3, cy - 3, cx - 3, cy + 3, cx + 3, cy + 3, cx + 3, cy - 3, cx - 3, cy - 3) if trigger == 1 and self.immediate and self.cbManager.callbacks: self.cbManager.CallCallbacks(self.get('RGB'))
class Chooser(object): def __init__(self, master=None, title='Chooser', commands=None, immediate=0, exitFunction=None): if master is None: self.master = tkinter.Toplevel() self.ownmaster = 1 self.master.title(title) #self.master.protocol('WM_DELETE_WINDOW', self.dismiss) else: self.ownmaster = 0 self.master = master # The editFrame is the main Frame of the widget self.masterFrame = tkinter.Frame(self.master, borderwidth=2, relief='ridge') self.immediate = immediate # Create a cbManager self.cbManager = CallbackManager() self.createCommon() self.createChooser() if commands: if type(commands) in [ListType, TupleType]: list(map(self.cbManager.AddCallback, commands)) list(map(self.ce.cbManager.AddCallback, commands)) else: self.cbManager.AddCallback(commands) self.ce.cbManager.AddCallback(commands) def createCommon(self): # Create the Menu Bar self.menuBar = Pmw.MenuBar(self.masterFrame, hull_relief='raised', hull_borderwidth=1) self.menuBar.addmenu('File', 'Close this window or exit') self.mainFrame = tkinter.Frame(self.masterFrame, borderwidth=2, relief='ridge', width=150, height=200) self.menuBar.pack(fill='x') ## Create the ColorEditor self.ce = ColorEditor(self.mainFrame, immediate=self.immediate) self.hidden = 1 self.mainFrame.pack(fill='both', expand=1) ###################################################################### #### UTILITY FUNCTIONS #### ###################################################################### def createChooser(self): pass def pack(self, *args, **kw): self.masterFrame.pack(*args, **kw) def pack_forget(self, *args, **kw): self.masterFrame.pack_forget(*args, **kw) def grid(self, *args, **kw): self.masterFrame.grid(*args, **kw) def grid_forget(self, *args, **kw): self.masterFrame.grid_forget(*args, **kw)
class GraphApp: # Initialization def __init__(self,master=None,callback=None,continuous=1): self.master=master self.callback = None self.callbacks = CallbackManager() self.canvas=canvas = Canvas(self.master,width=345,height=320,bg='white') self.toolbar = Frame(master) # Create Toolbar self.toolbar.pack(side='top', expand=1, fill='both') self.menuFrame1 = Tkinter.Frame(self.toolbar, relief='raised', borderwidth=3) self.menuFrame1.pack(side='top', expand=1, fill='x') self.filebutton = Tkinter.Menubutton(self.menuFrame1, text='File') self.filebutton.pack(side='left') self.filemenu = Tkinter.Menu(self.filebutton, {}) self.filemenu.add_command(label='Read', command=self.read_cb) self.filemenu.add_command(label='Write', command=self.write_cb) self.filebutton['menu'] = self.filemenu self.editbutton = Tkinter.Menubutton(self.menuFrame1, text='Edit') self.editbutton.pack(side='left', anchor='w') self.editmenu = Tkinter.Menu(self.editbutton, {}) self.editmenu.add_command(label='Reset to first in history', command=self.resetAll_cb) self.editmenu.add_command(label='Step back in history loop', command=self.stepBack_cb) self.editmenu.add_command(label='Default Curve', command=self.defaultcurve_cb) self.editmenu.add_command(label='Invert Curve',command=self.invertGraph) self.histvar=IntVar() self.histvar.set(1) self.editmenu.add_checkbutton(label='Histogram',var=self.histvar,command=self.drawHistogram) self.editbutton['menu'] = self.editmenu self.optionType = IntVar() self.updatebutton = Tkinter.Menubutton(self.menuFrame1, text='Update') self.updatebutton.pack(side='left', anchor='w') self.updatemenu = Tkinter.Menu(self.updatebutton,{} ) for v,s in {0:'Continuous',1:'MouseButtonUp',2:'Update'}.items(): self.updatemenu.add_radiobutton(label=s, var=self.optionType, value = v,command=self.calloption) if continuous==1: self.optionType.set(0) self.updatebutton['menu'] = self.updatemenu #Curve Type self.CurveType = IntVar() self.CurveType.set(0) self.Smooth=1 self.curvebutton = Tkinter.Menubutton(self.menuFrame1, text='Curve') self.curvebutton.pack(side='left', anchor='w') self.curvemenu = Tkinter.Menu(self.curvebutton,{} ) for v,s in {0:'Smooth',1:'Freehand'}.items(): self.curvemenu.add_radiobutton(label=s, var=self.CurveType, value = v,command=self.curveoption) self.curvebutton['menu'] = self.curvemenu f1 = Tkinter.Frame(self.master) f1.pack(side='bottom', fill='both', expand=1) self.d1scalewheellab=Label(f1,text="Sensitivity") self.d1scalewheellab.pack(side="left") self.d1scalewheel=ThumbWheel(width=100, height=26,wheelPad=4,master=f1,labcfg={'fg':'black', 'side':'left', 'text':'Test:'},wheelLabcfg1={'font':(ensureFontCase('times'),14,'bold')},wheelLabcfg2={'font':(ensureFontCase('times'),14,'bold')},canvascfg={'bg':'blue'},min = 0.0,max = 1.0,precision =4,showlabel =0,value =0.013,continuous =0,oneTurn =0.01,size = 200) self.d1scalewheel.pack(side="left") #tooltip self.balloon = Pmw.Balloon(f1) self.balloon.bind(self.d1scalewheel,"cutoff value for differences in Z xoordinates,small values generate more contours") self.Updatebutton=Button(f1,text=' Update ',command=self.Update) self.Updatebutton.pack(side=LEFT) self.Quitbutton=Button(f1,text=' Dismiss ',command=self.dismiss_cb) self.Quitbutton.pack(side=RIGHT) self.canvas.bind("<Button-1>", self.OnCanvasClicked) self.canvas.bind("<B1-Motion>", self.OnCanvasMouseDrag) self.canvas.bind("<ButtonRelease-1>", self.OnCanvasMouseUp) self.canvas.config(closeenough=2.0) self.canvas.pack(side=BOTTOM, fill=BOTH,expand=1) self.startpoint=(px,py)=(50,275) self.endpoint=(px1,py1)=(305,20) self.newpoints=[(px,py),(px1,py1)] self.canvas.create_rectangle([(px-1,py),(px1+1,py1)],fill='white',outline="black",width=1) self.canvas.create_text(46,281,text=0,anchor=N) #Drawing Graph Sheet for i in range(1,6): x=50+i*50 canvas.create_line(x,280,x,275,width=1) canvas.create_text(x,281,text='%d' %(50*i),anchor=N) for i in range(1,5): x=50+i*50 canvas.create_line(x,275,x,20,width=1,fill="gray80") for i in range(1,6): y=275-i*50 canvas.create_line(45,y,50,y,width=1) canvas.create_text(44,y,text='%d' %(50*i),anchor=E) for i in range(1,5): y=275-i*50 canvas.create_line(50,y,305,y,width=1,fill="gray80") (x,y)=self.newpoints[0] (x1,y1)=self.newpoints[-1] self.curline=canvas.create_line(self.newpoints,fill='black',width=1) #GRAY SCALE grays=[] for i in range(0,100,1): grays.append("gray"+"%d" %i) #grays.reverse() #bottom one x1=48 x2=51 self.canvas.create_rectangle([(50,315),(307,300)],fill='white',outline="black",width=0.5) for a in grays: if x1>306: x1=x2=306 self.canvas.create_rectangle([(x1+2.5,314),(x2+2.5,301)],fill=a,outline=a,width=1) x1=x1+2.5 x2=x2+2.5 #left one y1=274 y2=271 self.canvas.create_rectangle([(20,275),(5,20)],fill='black',outline="black",width=0.5) for a in grays: if y1>275: y1=y2=275 self.canvas.create_rectangle([(19,y1-2.5),(6,y2-2.5)],fill=a,outline=a,width=1) y1=y1-2.5 y2=y2-2.5 self.oldpoints=[] self.canvas.configure(cursor='cross') self.curovals=[] self.default_points=[(50,275),(88, 238), (101, 150), (154, 78), (75, 271),(305,20)] # now set the constructor options correctly using the configure method apply( self.configure, (),{'callback':callback,'continuous':continuous}) self.continuous=continuous self.mousebuttonup=0 self.update=0 self.range_points=[] self.history=[] self.bars=[] self.default_ramp=[] self.histvalues=[] def calloption(self): tag=self.optionType.get() self.continuous=0 self.mousebuttonup=0 self.update=0 if tag==0: self.continuous=1 elif tag==1: self.mousebuttonup=1 elif tag==2: self.update=1 def curveoption(self): tag=self.CurveType.get() self.Smooth=0 self.Freehand=0 if tag==0: self.Smooth=1 self.canvas.delete(self.curline) self.curline=self.canvas.create_line(self.getControlPoints(),smooth=1) elif tag==1: self.Freehand=1 self.canvas.delete(self.curline) self.curline=self.canvas.create_line(self.getControlPoints()) def OnCanvasClicked(self,event): """Appends last drag point to controlpoint list if not appended by mouseleave func. when clicked on any controlpoint removes control point and draws line with remaining control points.""" self.CLICK_NODRAG=1 if self.history!=[]: if self.history[-1][1]!=self.d1scalewheel.get(): self.history[-1]=(self.history[-1][0],self.d1scalewheel.get()) if hasattr(self,"curx"): if (self.curx,self.cury) :#not in [self.startpoint,self.endpoint]: (self.ox,self.oy)=(self.curx,self.cury) if (self.ox,self.oy) not in self.oldpoints: self.oldpoints.append((self.ox,self.oy)) if hasattr(self,"curoval"): self.curovals.append(self.curoval) self.OrgX=event.x self.OrgY=event.y CtlPoints=[] xcoords=[] ycoords=[] #Limiting points not to cross self.limit_xcoord=[] for i in range(0,10): xcoords.append(self.OrgX-i) ycoords.append(self.OrgY-i) xcoords.append(self.OrgX+i) ycoords.append(self.OrgY+i) if xcoords!=[] and ycoords!=[]: for x in xcoords: for y in ycoords: CtlPoints.append((x,y)) self.range_points=self.oldpoints self.range_points.sort() for c in CtlPoints: if c in self.range_points: index_c=self.range_points.index(c) if index_c<len(self.range_points)-1: self.limit_xcoord.append(self.range_points[index_c+1]) if index_c>0: self.limit_xcoord.append(self.range_points[index_c-1]) return else: self.limit_xcoord.append(self.startpoint) return elif index_c==len(self.range_points)-1: self.limit_xcoord.append(self.range_points[index_c-1]) self.limit_xcoord.append(self.endpoint) return self.newd1ramp= self.caluculate_ramp() def OnCanvasMouseUp(self,event): CtlPoints=[] xcoords=[] ycoords=[] if hasattr(self,"curx"): (self.ox,self.oy)=(self.curx,self.cury) if (self.ox,self.oy) not in self.oldpoints :#not in [self.startpoint,self.endpoint] : self.oldpoints.append((self.ox,self.oy)) if hasattr(self,"curoval"): if self.curoval not in self.curovals: self.curovals.append(self.curoval) if self.CLICK_NODRAG==1: #finding out points around the selected point for i in range(0,10): xcoords.append(self.OrgX-i) ycoords.append(self.OrgY-i) xcoords.append(self.OrgX+i) ycoords.append(self.OrgY+i) if xcoords!=[] and ycoords!=[]: for x in xcoords: for y in ycoords: CtlPoints.append((x,y)) for c in CtlPoints: if c in self.oldpoints: ind=self.oldpoints.index(c) op=self.oldpoints[ind] if ind>0: prev_oldpoint=self.oldpoints[ind-1] else: prev_oldpoint=self.endpoint del self.oldpoints[ind] for co in self.curovals: ov_point1=self.canvas.coords(co) if len(ov_point1)!=0: ov_point=(int(ov_point1[0]+2),int(ov_point1[1]+2)) if ov_point==c and ov_point not in [self.startpoint,self.endpoint]: self.canvas.delete(co) self.curovals.remove(co) if hasattr(self,"curx"): if ov_point==(self.curx,self.cury): (self.curx,self.cury)=prev_oldpoint self.draw() if self.mousebuttonup: self.newd1ramp=self.caluculate_ramp() self.callbacks.CallCallbacks(self.newd1ramp) self.history.append((deepCopySeq(self.oldpoints),self.d1scalewheel.get())) def OnCanvasMouseDrag(self,event): self.CLICK_NODRAG=0 CtlPoints=[] xcoords=[] ycoords=[] #making active clickrange to be ten points around clicked point for i in range(0,10): xcoords.append(self.OrgX-i) ycoords.append(self.OrgY-i) xcoords.append(self.OrgX+i) ycoords.append(self.OrgY+i) if xcoords!=[] and ycoords!=[]: for x in xcoords: for y in ycoords: CtlPoints.append((x,y)) for c in CtlPoints: if c in self.oldpoints: ind=self.oldpoints.index(c) op=self.oldpoints[ind] del self.oldpoints[ind] for co in self.curovals: ov_point1=self.canvas.coords(co) if len(ov_point1)!=0: ov_point=(int(round(ov_point1[0],3))+2,int(round(ov_point1[1],3))+2) if ov_point==c : self.canvas.delete(co) self.curovals.remove(co) self.curx=dx=event.x self.cury=dy=event.y self.draw() def draw(self): """Draws line,ovals with current controlpoints. """ new1points=[] curve_points=[] self.smoothened_points=[] if self.CLICK_NODRAG==0: dx=self.curx dy=self.cury else: (dx,dy)=(self.curx,self.cury)=(self.endpoint) ###Limiting xcoords of the current point not to cross adjacent points if hasattr(self,"limit_xcoord"): if self.limit_xcoord!=[]: self.limit_xcoord.sort() if (self.curx,self.cury) not in [self.startpoint,self.endpoint]: if (self.curx,self.cury)< self.limit_xcoord[0]: if self.curx<=self.limit_xcoord[0][0] and self.cury<self.limit_xcoord[0][1]: dx=self.curx=self.limit_xcoord[0][0]+1 if self.curx<=self.limit_xcoord[0][0] and self.cury>self.limit_xcoord[0][1]: dx=self.curx=self.limit_xcoord[0][0] if (self.curx,self.cury)> self.limit_xcoord[1]: if self.curx>=self.limit_xcoord[1][0] and self.cury>self.limit_xcoord[1][1]: dx=self.curx=self.limit_xcoord[1][0]-1 if self.curx>=self.limit_xcoord[1][0] and self.cury<self.limit_xcoord[1][1]: dx=self.curx=self.limit_xcoord[1][0] #Limit graph with in the axis if self.curx not in range(50,305): if self.curx<50: self.curx=dx=50 else: self.curx=dx=305 if self.cury not in range(20,275): if self.cury<20: self.cury=dy=20 else: self.cury=dy=275 #adding start,end points new1points.append(self.startpoint) new1points.append(self.endpoint) #adding current point to list if (dx,dy) not in new1points and (dx,dy) not in [self.startpoint,self.endpoint]: new1points.append((dx,dy)) #adding oldpoints to list if hasattr(self,"ox"): for op in self.oldpoints: if op not in new1points: new1points.append(op) new1points.sort() #removing oval point that is on drag if hasattr(self,"curoval"): if self.curoval not in self.curovals: self.canvas.delete(self.curoval) self.canvas.delete(self.curline) #if points that start with 50 or 51 or 305,304 other than start ,end #points exists remove start or end points #remove ovals #finding oval for start point and endpoint for i in new1points: if i[0]==51 or i[0]==50: if i!=self.startpoint: if self.startpoint in new1points: new1points.remove(self.startpoint) ###removing start point oval x = 50 y = 275 st_oval_1= self.canvas.find_enclosed(x-3,y-3,x+3,y+3) if st_oval_1: for so in st_oval_1: if so!=[]: st_oval=so st_oval_coords=self.canvas.coords(st_oval) if (int(st_oval_coords[0]+2),int(st_oval_coords[1]+2))==self.startpoint: self.canvas.delete(st_oval) if st_oval in self.curovals: self.curovals.remove(st_oval) for i in new1points: if i[0]==304 or i[0]==305: if i!=self.endpoint : if self.endpoint in new1points: new1points.remove(self.endpoint) ###removing end point oval x = 305 y = 20 end_oval_1= self.canvas.find_enclosed(x-3,y-3,x+3,y+3) if end_oval_1: for eo in end_oval_1: if eo!=[]: end_oval=eo end_oval_coords=self.canvas.coords(end_oval) if (int(end_oval_coords[0]+2),int(end_oval_coords[1]+2))==self.endpoint: self.canvas.delete(end_oval) if end_oval in self.curovals: self.curovals.remove(end_oval) new1points.sort() for (x,y) in new1points: curve_points.append(x) curve_points.append(y) self.smoothened_points= self.smooth(curve_points) #drawing line if len(self.smoothened_points)>2: if self.Smooth: self.curline=self.canvas.create_line(self.smoothened_points) else: self.curline=self.canvas.create_line(curve_points) else: if curve_points[0]==50 or 51: if self.Smooth: self.curline=self.canvas.create_line(curve_points,smooth=1) else: self.curline=self.canvas.create_line(curve_points) else: self.curline=self.canvas.create_line(self.startpoint,self.endpoint) ##Adding oval when start or end point in new1points coval_coords=[] for i in self.curovals: coval_coords.append(self.canvas.coords(i)) if self.endpoint in new1points: co=self.canvas.create_oval(self.endpoint[0]-2,self.endpoint[-1]-2,self.endpoint[0]+2,self.endpoint[-1]+2,width=1,outline='black',fill='black') endco_coords =self.canvas.coords(co) if endco_coords not in coval_coords: self.curovals.append(co) if self.startpoint in new1points: co=self.canvas.create_oval(self.startpoint[0]-2,self.startpoint[-1]-2,self.startpoint[0]+2,self.startpoint[-1]+2,width=1,outline='black',fill='black') startco_coords=self.canvas.coords(co) if startco_coords not in coval_coords: self.curovals.append(co) #drawing ovals if (self.curx,self.cury)!=self.endpoint: self.curoval=self.canvas.create_oval(self.curx-2,self.cury-2,self.curx+2,self.cury+2,width=1,outline='black',fill='black') if (self.curx,self.cury)==self.endpoint and self.endpoint in new1points: self.curoval=self.canvas.create_oval(self.curx-2,self.cury-2,self.curx+2,self.cury+2,width=1,outline='black',fill='black') self.newd1ramp= self.caluculate_ramp() if self.continuous: self.callbacks.CallCallbacks(self.newd1ramp) ######## convert coordinates to ramp################## def caluculate_ramp(self): """ """ dramp=[] mypoints=[] mynewpoints=[] self.oldpoints.sort() calcpoints=[] #if self.continuous : if hasattr(self,"curx"): if (self.curx,self.cury) not in self.oldpoints and (self.curx,self.cury) not in [self.startpoint,self.endpoint]: calcpoints.append((self.curx,self.cury)) if len(self.oldpoints)!=0: for o in self.oldpoints: if o not in calcpoints: calcpoints.append(o) if self.startpoint not in calcpoints: calcpoints.append(self.startpoint) if self.endpoint not in calcpoints: calcpoints.append(self.endpoint) calcpoints.sort() length=len(calcpoints) for l in range(length): if l+1<=length-1: mypoints=[calcpoints[l],calcpoints[l+1]] if calcpoints[l] not in mynewpoints: mynewpoints.append( calcpoints[l]) (x1,y1)=calcpoints[l] (x2,y2)=calcpoints[l+1] if x1>x2: dcx=x1-x2 px=x1-1 else: dcx=x2-x1 px=x1+1 if y1>y2: dcy=y1-y2 if dcx>=1: py=y1-float(dcy)/float(dcx) else: py=y1 else: dcy=y2-y1 if dcx>=1: py=y1+float(dcy)/float(dcx) else: py=y2 mynewpoints.append( (px,int(round(py)))) for dc in range(dcx-1): if x1>x2: px=px-1 else: px=px+1 if y1>y2: if dcx>=1: py=py-float(dcy)/float(dcx) else: py=y1 else: if dcx>=1: py=py+float(dcy)/float(dcx) else: py=y2 mynewpoints.append( (px,int(round(py)))) ramp=[] for r in mynewpoints: #scale ra=float(275-r[1]) if ra>=256: ra=255.0 ramp.append(ra) dramp=Numeric.array(ramp,'f') if len(dramp)!=0: return dramp else: dramp=Numeric.arange(0,256,1,'f') return dramp def get(self): if hasattr(self,"newd1ramp"): return self.newd1ramp else: return self.caluculate_ramp() def configure(self, **kw): if 'type' in kw.keys(): # make sure type is set first self.setType(kw['type']) del kw['type'] for key,value in kw.items(): if key=='callback': self.setCallbacks(value) def setCallbacks(self, cb): """Set widget callback. Must be callable function. Callback is called every time the widget value is set/modified""" assert cb is None or callable(cb) or type(cb) is types.ListType,\ "Illegal callback: must be either None or callable. Got %s"%cb if cb is None: return elif type(cb) is types.ListType: for func in cb: assert callable(func), "Illegal callback must be callable. Got %s"%func self.callbacks.AddCallback(func) else: self.callbacks.AddCallback(cb) self.callback = cb def invertGraph(self): """This function is for inverting graph by reverse computing controlpoints""" if self.history!=[]: if self.history[-1][1]!=self.d1scalewheel.get(): self.history[-1]=(self.history[-1][0],self.d1scalewheel.get()) invert_points=[] #self.oldpoints=[] points=self.getControlPoints() if len(points)<2: points=[self.startpoint,self.endpoint] for p in points: if p[1] in range(20,276): y=275 -(p[1]-20) invert_points.append((p[0],y)) self.reset() ################################################### #Some times start and end points are not deleted #So for deleting them canvas.find_enclosed points at #startpoint and endpoint are caluculated(returns alist of #canvas objects present there) and if the coords of #any canvas objects matches with start or end point that gets deleted ##################################################### x = 50 y = 275 st_oval_1= self.canvas.find_enclosed(x-3,y-3,x+3,y+3) if st_oval_1: for so in st_oval_1: if so!=[]: st_oval=so st_oval_coords=self.canvas.coords(st_oval) if (int(st_oval_coords[0]+2),int(st_oval_coords[1]+2))==self.startpoint: self.canvas.delete(st_oval) if st_oval in self.curovals: self.curovals.remove(st_oval) x = 305 y = 20 end_oval_1= self.canvas.find_enclosed(x-3,y-3,x+3,y+3) if end_oval_1: for eo in end_oval_1: if eo!=[]: end_oval=eo end_oval_coords=self.canvas.coords(end_oval) if (int(end_oval_coords[0]+2),int(end_oval_coords[1]+2))==self.endpoint: self.canvas.delete(end_oval) if end_oval in self.curovals: self.curovals.remove(end_oval) self.canvas.delete(self.curline) if self.Smooth: self.curline=self.canvas.create_line(invert_points,smooth=1) else: self.curline=self.canvas.create_line(invert_points) self.oldpoints=invert_points for p in invert_points: self.curoval=self.canvas.create_oval(p[0]-2,p[1]-2,p[0]+2,p[1]+2,width=1,outline='black',fill='black') self.curovals.append(self.curoval) (self.curx,self.cury) =invert_points[-2] if self.continuous or self.mousebuttonup: self.newd1ramp=self.caluculate_ramp() self.callbacks.CallCallbacks([self.newd1ramp]) self.history.append((deepCopySeq(self.oldpoints),self.d1scalewheel.get())) def defaultcurve_cb(self): """draws curve with default points""" if self.history!=[]: if self.history[-1][1]!=self.d1scalewheel.get(): self.history[-1]=(self.history[-1][0],self.d1scalewheel.get()) points=[] self.default_points=[] self.oldpoints=[] self.d1scalewheel.set(0.013) self.default_points=[(50,275),(88, 238), (101, 150), (154, 78), (75, 271),(305,20)] self.reset() self.canvas.delete(self.curline) self.default_points.sort() if self.Smooth: self.curline=self.canvas.create_line(self.default_points,smooth=1) else: self.curline=self.canvas.create_line(self.default_points) self.oldpoints=self.default_points for p in self.default_points: self.curoval=self.canvas.create_oval(p[0]-2,p[1]-2,p[0]+2,p[1]+2,width=1,outline='black',fill='black') self.curovals.append(self.curoval) (self.curx,self.cury) =self.default_points[-2] if self.continuous or self.mousebuttonup: self.newd1ramp=self.caluculate_ramp() self.callbacks.CallCallbacks(self.newd1ramp) self.history.append((deepCopySeq(self.oldpoints),self.d1scalewheel.get())) self.default_ramp= self.newd1ramp def read_cb(self): fileTypes = [("Graph",'*_Graph.py'), ("any file",'*.*')] fileBrowserTitle = "Read Graph" fileName = self.fileOpenAsk(types=fileTypes, title=fileBrowserTitle) if not fileName: return self.read(fileName) def read( self,fileName): if self.history!=[]: if self.history[-1][1]!=self.d1scalewheel.get(): self.history[-1]=(self.history[-1][0],self.d1scalewheel.get()) fptr=open(fileName,"r") data=fptr.readlines() cpoints=data[0][:-1] sensitivity=data[1] self.d1scalewheel.set(eval(sensitivity)) if len(cpoints)==0: return else: points=cpoints self.oldpoints=[] self.reset() if hasattr(self,"curline"): self.canvas.delete(self.curline) for c in self.curovals: self.canvas.delete(c) self.curovals.remove(c) if hasattr(self,"curoval"): self.canvas.delete(self.curoval) self.curovals=[] if self.Smooth: self.curline=self.canvas.create_line(eval(points),smooth=1) else: self.curline=self.canvas.create_line(eval(points)) self.readpoints=self.oldpoints=eval(points)[1:-1] for p in eval(points)[1:-1]: self.curoval=self.canvas.create_oval(p[0]-2,p[1]-2,p[0]+2,p[1]+2,width=1,outline='black',fill='black') self.curovals.append(self.curoval) (self.curx,self.cury) =eval(points)[-2] self.history.append((deepCopySeq(self.oldpoints),self.d1scalewheel.get())) def fileOpenAsk(self, idir=None, ifile=None, types=None, title='Open'): if types==None: types = [ ('All files', '*') ] file = tkFileDialog.askopenfilename( filetypes=types, initialdir=idir, initialfile=ifile, title=title) if file=='': file = None return file def write_cb(self): fileTypes = [("Graph",'*_Graph.py'), ("any file",'*.*')] fileBrowserTitle = "Write Graph" fileName = self.fileSaveAsk(types=fileTypes, title=fileBrowserTitle) if not fileName: return self.write(fileName) def write(self,fileName): fptr=open(fileName,"w") points= self.getControlPoints() points.sort() fptr.write(str(points)) fptr.write("\n") fptr.write(str(self.d1scalewheel.get())) fptr.close() def fileSaveAsk(self, idir=None, ifile=None, types = None, title='Save'): if types==None: types = [ ('All files', '*') ] file = tkFileDialog.asksaveasfilename( filetypes=types, initialdir=idir, initialfile=ifile, title=title) if file=='': file = None return file def reset(self): """This function deletes current line removes current ovals""" self.canvas.delete(self.curline) self.oldpoints=[] for c in self.curovals: self.canvas.delete(c) if hasattr(self,"curoval"): self.canvas.delete(self.curoval) self.curovals=[] if hasattr(self,"curoval"): delattr(self,"curoval") if hasattr(self,"curx"): delattr(self,"curx") def resetAll_cb(self): """Resetting curve as slant line 0 to 255""" self.reset() self.curline=self.canvas.create_line([self.startpoint,self.endpoint],width=1,fill='black') for p in [self.startpoint,self.endpoint]: self.curoval=self.canvas.create_oval(p[0]-2,p[1]-2,p[0]+2,p[1]+2,width=1,outline='black',fill='black') self.curovals.append(self.curoval) self.oldpoints=[self.startpoint,self.endpoint] (self.curx,self.cury)=self.endpoint self.d1scalewheel.set(0.013) if self.continuous or self.mousebuttonup: self.newd1ramp=Numeric.arange(0,256,1,'f') self.callbacks.CallCallbacks(self.newd1ramp) #self.histvar.set(0) self.history=[] def stepBack_cb(self): """when stepBack button clicked previous step is displayed.History of all the steps done is remembered and when stepback clicked from history list previous step is shown and that step is removed from history list """ if self.history!=[]: if len(self.history)==1: self.resetAll_cb() else: del self.history[-1] pns = self.history[-1][0] #deleting self.oldpoints=pns self.canvas.delete(self.curline) for c in self.curovals: self.canvas.delete(c) if hasattr(self,"curoval"): self.canvas.delete(self.curoval) self.curovals=[] ################################################### #Some times start and end points are not deleted #So for deleting them canvas.find_enclosed points at #startpoint and endpoint are caluculated(returns alist of #canvas objects present there) and if the coords of #any canvas objects matches with start or end point that gets deleted ##################################################### x = 50 y = 275 st_oval_1= self.canvas.find_enclosed(x-3,y-3,x+3,y+3) if st_oval_1: for so in st_oval_1: if so!=[]: st_oval=so st_oval_coords=self.canvas.coords(st_oval) if (int(st_oval_coords[0]+2),int(st_oval_coords[1]+2))==self.startpoint: self.canvas.delete(st_oval) if st_oval in self.curovals: self.curovals.remove(st_oval) x = 305 y = 20 end_oval_1= self.canvas.find_enclosed(x-3,y-3,x+3,y+3) if end_oval_1: for eo in end_oval_1: if eo!=[]: end_oval=eo end_oval_coords=self.canvas.coords(end_oval) if (int(end_oval_coords[0]+2),int(end_oval_coords[1]+2))==self.endpoint: self.canvas.delete(end_oval) if end_oval in self.curovals: self.curovals.remove(end_oval) pns.sort() #if no start or end points if pns[0][0]>51 : pns.insert(0,self.startpoint) l=len(pns) if pns[-1][0]<304: pns.insert(l,self.endpoint) #if start or endpoints and points with (50or 51) or (305or305) if self.startpoint in pns: for p in pns: if p!=self.startpoint: if p[0]== 50 or p[0]==51: pns.remove(self.startpoint) if self.endpoint in pns: for p in pns: if p!=self.endpoint: if p[0]==305 or p[0]==304: pns.remove(self.endpoint) print pns if self.Smooth: self.curline=self.canvas.create_line(pns,width=1,fill='black',smooth=1) else: self.curline=self.canvas.create_line(pns,width=1,fill='black') for p in pns: self.curoval=self.canvas.create_oval(p[0]-2,p[1]-2,p[0]+2,p[1]+2,width=1,outline='black',fill='black') self.curovals.append(self.curoval) self.d1scalewheel.set(self.history[-1][1]) if self.continuous or self.mousebuttonup: self.newd1ramp=Numeric.arange(0,256,1,'f') self.callbacks.CallCallbacks(self.newd1ramp) (self.curx,self.cury)=self.endpoint def getControlPoints(self): """fuction to get current control points of the curve""" if not self.oldpoints==[self.startpoint,self.endpoint]: for i in range(len(self.oldpoints)): if self.startpoint in self.oldpoints: self.oldpoints.remove(self.startpoint) if self.endpoint in self.oldpoints: self.oldpoints.remove(self.endpoint) self.controlpoints=[] if hasattr(self,"curoval"): c=self.canvas.coords(self.curoval) if len(c)!=0: if (int(c[0]+2),int(c[1]+2)) not in self.oldpoints and (int(c[0]+2),int(c[1]+2)) not in [self.startpoint,self.endpoint]: self.controlpoints.append((int(c[0]+2),int(c[1]+2))) for op in self.oldpoints: self.controlpoints.append(op) self.controlpoints.sort() if len(self.controlpoints)>0: if self.controlpoints[0][0]==50 or self.controlpoints[0][0]==51 : pass else: self.controlpoints.append(self.startpoint) self.controlpoints.sort() if self.controlpoints[-1][0]==305 or self.controlpoints[-1][0]==304: pass else: self.controlpoints.append(self.endpoint) self.controlpoints.sort() return self.controlpoints def setControlPoints(self,points): """function to set curve control points""" assert isinstance(points, types.ListType),"Illegal type for points" for (x,y) in points: assert x in range(50,306),"coordinates are out of range,x should be in [50,305]" assert y in range(20,276),"coordinates are out of range,y should be in [20,275]" self.oldpoints=[] self.controlpoints=[] self.reset() self.oldpoints=self.controlpoints=points self.controlpoints.sort() if self.controlpoints[0]!=self.startpoint: self.controlpoints.append(self.startpoint) if self.controlpoints[-1]!=self.endpoint: self.controlpoints.append(self.endpoint) self.canvas.delete(self.curline) self.controlpoints.sort() if self.Smooth: self.curline=self.canvas.create_line( self.controlpoints,smooth=1) else: self.curline=self.canvas.create_line( self.controlpoints) for p in self.controlpoints[1:-1]: self.curoval=self.canvas.create_oval(p[0]-2,p[1]-2,p[0]+2,p[1]+2,width=1,outline='black',fill='black') self.curovals.append(self.curoval) (self.curx,self.cury)= self.controlpoints[-2] self.history.append((deepCopySeq(self.oldpoints),self.d1scalewheel.get())) if self.continuous or self.mousebuttonup: self.newd1ramp=self.caluculate_ramp() self.callbacks.CallCallbacks(self.newd1ramp) def setSensitivity(self,val): self.d1scalewheel.set(val) def Update(self): if self.update==1: dramp=self.caluculate_ramp() self.newd1ramp=dramp self.callbacks.CallCallbacks(dramp) def dismiss_cb(self): try: if self.master.winfo_ismapped(): self.master.withdraw() except: if self.master.master.winfo_ismapped(): self.master.master.withdraw() #draw Histogram def removeHistogram(self): """removes Histograms""" for b in self.bars: self.canvas.delete(b) self.bars=[] def drawHistogram(self): """This function draws histogram from list of pixel counts ,one for each value in the source image""" self.removeHistogram() if self.histvar.get(): h=self.histvalues if h==[]: return list_pixels_count=h c=[] maxc=max(list_pixels_count) if maxc==0: return if list_pixels_count.index(maxc): list_pixels_count.remove(maxc) list_pixels_count.insert(255,0) for i in list_pixels_count[:256]: max_list=max(list_pixels_count) if max_list==0: return val=i*200/max_list c.append(val) for i in range(0,len(c)): x1=50+i x2=50+i y1=275-c[i] y2=275 r=self.canvas.create_line([(x1,y1),(x2,y2)],fill="gray70",width=1) self.bars.append(r) #displaying line and ovals ontop self.canvas.tkraise(self.curline) for i in self.curovals: self.canvas.tkraise(i) if hasattr(self,"curoval"): self.canvas.tkraise(self.curoval) self.canvas.update() ##Update Histograms on Graphtool #if self.update!=1: # prev_option=self.optionType.get() # self.optionType.set(2) # self.update=1 # self.Update() # self.optionType.set(prev_option) # self.update=0 # return ################SMOOTHING CODE############################ def addcurve(self,out, xy, steps): add = out.append for i in range(1, steps+1): t = float(i) / steps; t2 = t*t; t3 = t2*t u = 1.0 - t; u2 = u*u; u3 = u2*u add(xy[0]*u3 + 3*(xy[2]*t*u2 + xy[4]*t2*u) + xy[6]*t3) add(xy[1]*u3 + 3*(xy[3]*t*u2 + xy[5]*t2*u) + xy[7]*t3) def smooth(self,xy, steps=12): if not xy: return xy closed = xy[0] == xy[-2] and xy[1] == xy[-1] out = [] if closed: # connect end segment to first segment control = ( 0.500*xy[-4] + 0.500*xy[0], 0.500*xy[-3] + 0.500*xy[1], 0.167*xy[-4] + 0.833*xy[0], 0.167*xy[-3] + 0.833*xy[1], 0.833*xy[0] + 0.167*xy[2], 0.833*xy[1] + 0.167*xy[3], 0.500*xy[0] + 0.500*xy[2], 0.500*xy[1] + 0.500*xy[3], ) out = [control[0], control[1]] self.addcurve(out, control, steps) else: out = [xy[0], xy[1]] for i in range(0, len(xy)-4, 2): if i == 0 and not closed: control = (xy[i],xy[i+1],0.333*xy[i] + 0.667*xy[i+2],0.333*xy[i+1] + 0.667*xy[i+3],) else: control = ( 0.500*xy[i] + 0.500*xy[i+2], 0.500*xy[i+1] + 0.500*xy[i+3], 0.167*xy[i] + 0.833*xy[i+2], 0.167*xy[i+1] + 0.833*xy[i+3], ) if i == len(xy)-6 and not closed: control = control + ( 0.667*xy[i+2] + 0.333*xy[i+4], 0.667*xy[i+3] + 0.333*xy[i+5], xy[i+4], xy[i+5], ) else: control = control + ( 0.833*xy[i+2] + 0.167*xy[i+4], 0.833*xy[i+3] + 0.167*xy[i+5], 0.500*xy[i+2] + 0.500*xy[i+4], 0.500*xy[i+3] + 0.500*xy[i+5], ) if ((xy[i] == xy[i+2] and xy[i+1] == xy[i+3]) or (xy[i+2] == xy[i+4] and xy[i+3] == xy[i+5])): out.append(control[6]) out.append(control[7]) else: self.addcurve(out, control, steps) return out
class ColorEditor: """ The ColorEditor is a widget providing a colorwheel, a value scale, HSV entries, RGB entries and HEX(HexTriplet) entries """ def __init__(self, master=None, currentColor=(1.0, 1.0, 1.0), mode='RGB', commands=None, immediate=1): assert mode in ['RGB', 'HSV', 'HEX'] self.mode = mode if not master: self.master = tkinter.Toplevel() else: self.master = master self.afterID = None self.immediate = immediate # The editFrame is the main Frame of the widget self.editFrame = tkinter.Frame(self.master, borderwidth=2, relief='ridge') self.cbManager = CallbackManager() if commands: if type(commands) in [ListType, TupleType]: list(map(self.cbManager.AddCallback, commands)) else: self.cbManager.AddCallback(commands) if mode == 'HSV': self.currentHSV = list(currentColor) self.currentRGB = list(ToRGB(currentColor)) self.currentHEX = ToHEX(currentColor, mode='HSV') elif mode == 'RGB': self.currentRGB = list(currentColor) self.currentHSV = list(ToHSV(currentColor)) self.currentHEX = ToHEX(currentColor, mode='RGB') elif mode == 'HEX': self.currentRGB = ToHEX(currentColor, mode='RGB') self.currentHSV = ToHEX(currentColor, mode='HSV') self.currentHEX = currentColor else: print('mode not recognized mode set to RGB') self.createColorEditor() def createColorEditor(self): # The chooserFrame containinsg the colorwheel and the value scale chooserFrame = tkinter.Frame(self.editFrame) # Create a Tkinter Scale widget bound a callback : self.scale_cb self.vScale = tkinter.Scale( chooserFrame, from_=1.0, to_=0.0, orient='vertical', resolution=0.01, ) ## command = self.scale_cb) if self.immediate: self.vScale.configure(command=self.scale_cb) else: self.vScale.bind('<ButtonRelease-1>', self.scaleUp_cb) # pack the scale to be on the left side of the colorwheel self.vScale.pack(side='right', padx=5, pady=5, expand=1, fill='both') self.vScale.set(1.0) # Create the colorWheel Frame wheelFrame = tkinter.Frame(chooserFrame, relief='ridge', borderwidth=1) # Pack the ColorWheel wheelFrame.pack(side='left', pady=5, padx=10, fill='both', expand=1) # Create the ColorWheel # to silent an error report from pychecker self.cw = ColorWheel(wheelFrame, None, self.colorWidget_cb, self.immediate) #self.cw = ColorWheel(wheelFrame,title=None, # callback=self.colorWidget_cb, # immediate=self.immediate) self.cw.set(self.currentRGB, mode='RGB', trigger=0) # Bind the colorwidget to a callback function self.colorWidget_cb #self.cw.callback = self.colorWidget_cb # Pack the chooserFrame chooserFrame.pack(expand=1, fill='both') bottomFrame = tkinter.Frame(self.editFrame) #The preview frame will contain the frame to show the choosen color previewFrame = tkinter.Frame(bottomFrame) previewFrame.pack(side='left', fill='both', expand=1) preview = tkinter.Frame(previewFrame, ) bg = self.currentHEX self.chip = tkinter.Frame(previewFrame, borderwidth=3, width=50, height=30, bg=bg, relief='ridge') # Pack the chipFrame self.chip.pack(fill='both', expand=1) #The entriesFrame will contain all the entryFields entriesFrame = tkinter.Frame(bottomFrame) entriesOption = { 'labelpos': 'w', 'validate': { 'validator': 'real', 'min': 0.0, 'max': 1.0 }, 'entry_width': 4, } # the hsvFrame will contain the H,S,V entryFields hsvFrame = tkinter.Frame(entriesFrame) entriesOption['label_text'] = 'H' entriesOption['value'] = "%4.2f" % self.currentHSV[0] entriesOption['command'] = self.hVal_cb self.hVal = Pmw.EntryField(*(hsvFrame, ), **entriesOption) self.hVal.pack(side='left') entriesOption['label_text'] = 'S' entriesOption['value'] = "%4.2f" % self.currentHSV[1] entriesOption['command'] = self.sVal_cb self.sVal = Pmw.EntryField(*(hsvFrame, ), **entriesOption) self.sVal.pack(side='left') entriesOption['label_text'] = 'V' entriesOption['value'] = "%4.2f" % self.currentHSV[2] entriesOption['command'] = self.vVal_cb self.vVal = Pmw.EntryField(*(hsvFrame, ), **entriesOption) self.vVal.pack(side='left') hsvFrame.pack(padx=4, pady=4, fill='both', expand=1) rgbFrame = tkinter.Frame(entriesFrame) # RGB entries entriesOption['label_text'] = 'R' entriesOption['value'] = "%4.2f" % self.currentRGB[0] entriesOption['command'] = self.rVal_cb self.rVal = Pmw.EntryField(*(rgbFrame, ), **entriesOption) self.rVal.pack(side='left') entriesOption['label_text'] = 'G' entriesOption['value'] = "%4.2f" % self.currentRGB[1] entriesOption['command'] = self.gVal_cb self.gVal = Pmw.EntryField(*(rgbFrame, ), **entriesOption) self.gVal.pack(side='left') entriesOption['label_text'] = 'B' entriesOption['value'] = "%4.2f" % self.currentRGB[2] entriesOption['command'] = self.bVal_cb self.bVal = Pmw.EntryField(*(rgbFrame, ), **entriesOption) self.bVal.pack(side='left') rgbFrame.pack(padx=4, pady=4, fill='both', expand=1) hexFrame = tkinter.Frame(entriesFrame) entriesOption['label_text'] = 'Hex triplet' entriesOption['value'] = self.currentHEX entriesOption['command'] = self.hexVal_cb del entriesOption['validate'] entriesOption['entry_width'] = 8 #entriesOption['validate']='alphanumeric' self.hexVal = Pmw.EntryField(*(hexFrame, ), **entriesOption) self.hexVal.pack(padx=4, pady=4, side='left') hexFrame.pack(fill='both', expand=1) entriesFrame.pack(side='right', fill='both', expand=1) bottomFrame.pack(side='bottom', fill='both', expand=1) ############################################################### #### COLOR CHOOSER UTILITY FUNCTIONs #### ############################################################### #def caluculate(self): def set(self, color, mode='RGB', trigger=1): """Set the current color""" #assert len(color)==3 assert mode in ['HSV', 'RGB', 'HEX'] self.mode = mode if mode == 'HSV': newRGB = [float("%4.2f" % x) for x in ToRGB(color)] elif mode == 'HEX': newRGB = ToRGB(color, mode='HEX') else: newRGB = color if newRGB != self.currentRGB: self.updateWidgetsColor(newRGB) if trigger == 1 and self.immediate and self.cbManager.callbacks: self.cbManager.CallCallbacks(self.currentRGB) def get(self, mode='RGB'): assert mode in ['RGB', 'HSV', 'HEX'] self.mode = mode if mode == 'RGB': return self.currentRGB elif mode == 'HSV': return self.currentHSV elif mode == 'HEX': col = ToHEX(self.currentRGB, mode='HEX') return col def pack(self, *args, **kw): self.editFrame.pack(*args, **kw) def pack_forget(self, *args, **kw): self.editFrame.pack_forget(*args, **kw) def grid(self, *args, **kw): self.editFrame.grid(*args, **kw) def grid_forget(self, *args, **kw): self.editFrame.grid_forget(*args, **kw) ############################################################### #### WIDGETS CALLBACK FUNCTIONS #### ############################################################### def colorWidget_cb(self, rgbcolor): # Do this test because updateCurrent is called after the set # cw. # if color is different from current color then update chip color = list(ToHSV(rgbcolor))[:] color[2] = float(self.vScale.get()) newrgb = list(ToRGB(color)) if newrgb != self.currentRGB: self.updateWidgetsColor(newrgb, who='cw') def scale_cb(self, val): if self.afterID is not None: self.vScale.after_cancel(self.afterID) self.afterID = None else: self.afterID = self.vScale.after(17, self.scaleImm_cb, val) def scaleImm_cb(self, val): newHSV = [float(self.hVal.get()), float(self.sVal.get()), float(val)] if newHSV != self.currentHSV: self.updateWidgetsColor(ToRGB(newHSV), who='scale') def scaleUp_cb(self, event=None): val = float(self.vScale.get()) newHSV = [float(self.hVal.get()), float(self.sVal.get()), float(val)] if newHSV != self.currentHSV: self.updateWidgetsColor(ToRGB(newHSV), who='scale') def hVal_cb(self): val = float(self.hVal.get()) newColor = self.currentHSV newColor[0] = val newHSV = [float("%4.2f" % x) for x in newColor] if (not (float(self.vVal.get())==0.00 or \ (float(self.sVal.get())==0 and float(self.vVal.get())==1.0))): self.updateWidgetsColor(ToRGB(newHSV), who='h') def sVal_cb(self): val = float(self.sVal.get()) newColor = list(ToHSV(self.currentRGB[:])) newColor[1] = val newHSV = [float("%4.2f" % x) for x in newColor] if (not float(self.vVal.get()) == 0) and newHSV != self.currentHSV: self.updateWidgetsColor(ToRGB(newHSV), who='s') def vVal_cb(self): newColor = [ float(self.hVal.get()), float(self.sVal.get()), float(self.vVal.get()) ] newHSV = [float("%4.2f" % x) for x in newColor] if newHSV != self.currentHSV: self.updateWidgetsColor(ToRGB(newHSV), who='v') def rVal_cb(self): val = float(self.rVal.get()) newColor = self.currentRGB[:] newColor[0] = val newRGB = [float("%4.2f" % x) for x in newColor] if newRGB != self.currentRGB: self.updateWidgetsColor(newRGB, who='r') def gVal_cb(self): val = float(self.gVal.get()) newColor = self.currentRGB[:] newColor[1] = val newRGB = [float("%4.2f" % x) for x in newColor] if newRGB != self.currentRGB: self.updateWidgetsColor(newRGB, who='g') def bVal_cb(self): val = float(self.bVal.get()) newColor = self.currentRGB[:] newColor[2] = val newRGB = [float("%4.2f" % x) for x in newColor] if newRGB != self.currentRGB: self.updateWidgetsColor(newRGB, who='b') def hexVal_cb(self): val = self.hexVal.get() if val[0] != '#' or len(val) != 7: val = self.currentHEX newRGB = ToRGB(val, 'HEX') if newRGB != self.currentRGB: self.updateWidgetsColor(newRGB, who='hex') ############################################################### #### WIDGETS UPDATE FUNCTIONS #### ############################################################### def updateWidgetsColor(self, rgbcolor, who='set', trigger=1): oldRGB = list(self.currentRGB) self.currentRGB = [float("%4.2f" % x) for x in rgbcolor] # If newcolor is the same than old color nothing to update. if oldRGB == self.currentRGB: return hsvcolor = ToHSV(rgbcolor[:]) self.currentHSV = [float("%4.2f" % x) for x in hsvcolor] self.currentHEX = ToHEX(self.currentRGB) # Update the preview chip self.chip.configure(bg=self.currentHEX) # ColorWidget: cwColor = self.cw.get(mode='RGB') newRGB = [float("%4.2f" % x) for x in cwColor] if newRGB != self.currentRGB and not who in ['v', 'scale']: self.cw.set(self.currentRGB, mode='RGB', trigger=0) # Value Scale: scaleCol = self.vScale.get() if scaleCol != self.currentHSV[2]: self.vScale.set(self.currentHSV[2]) # H Entry: h = float(self.hVal.get()) hCol = float("%4.2f" % h) if hCol != self.currentHSV[0] and not who in ['v', 'scale']: self.hVal.setentry(self.currentHSV[0]) # S Entry: s = float(self.sVal.get()) sCol = float("%4.2f" % s) if sCol != self.currentHSV[1] and self.currentHSV[2] !=0 \ and not who in ['v', 'scale']: self.sVal.setentry(self.currentHSV[1]) # V Entry: v = float(self.vVal.get()) vCol = float("%4.2f" % v) if vCol != self.currentHSV[2] and self.currentHSV[2] != 0: self.vVal.setentry(self.currentHSV[2]) # R Entry: r = float(self.rVal.get()) rCol = float("%4.2f" % r) if rCol != self.currentRGB[0]: self.rVal.setentry(self.currentRGB[0]) # G Entry: g = float(self.gVal.get()) gCol = float("%4.2f" % g) if gCol != self.currentRGB[1]: self.gVal.setentry(self.currentRGB[1]) # B Entry: b = float(self.bVal.get()) bCol = float("%4.2f" % b) if bCol != self.currentRGB[2]: self.bVal.setentry(self.currentRGB[2]) # Hex Entry: hexCol = self.hexVal.get() if hexCol != self.currentHEX: self.hexVal.setentry(self.currentHEX) # This might depend of the mode. ? if trigger == 1 and self.immediate and self.cbManager.callbacks: self.cbManager.CallCallbacks(self.currentRGB)
class ThumbWheel(Tkinter.Frame, KeyboardEntry): """ This class builds a thumbwheel put onto a wheelpad. constructor options: - master: the master into the thumwheel can be packed. If one is specified, the widget gets packed in a toplevel() window - height, width, orient specify the size and orientation of the widget. - wheelpad specifies the pad onto which the thumbwheel gets packed optional is a canvascfg to configure the wheelpad canvas Tkinter options. for example if the wheelpad needs a blue background: canvascfg={'bg':'blue'} - continuous: boolean. When set to True, continuous is 'on' and the callback functions will be called each time the value changes. Otherwise continuous will be 'off' and callback functions will be called on mouse release. - callback: (None, callable function or list of callable functions).to specify function to be called when the value of the thumbwheel is modified. - type: ('float', 'int' ...) string describing the type of the thumbwheel - min, max, increment, precision, oneTurn specify the parameters of the thumbwheel. - labCfg describes the label of the thumbwheel which will be packed to the left of the widget by default unless 'side' is specified. Possible keywords are text and side. - wheelLabCfg describes the label located on the wheel where the value of the thumbwheel will be displayed - showLabel Flag to specify whether to display the wheelLabel always 1, never 0, only on motion 2. - canvasCfg describe the canvas containing the thumbwheel. An option panel is available to the user to modify the thumbwheel settings by right clicking on the widget - lockMin, lockBMin, lockMax, lockBMax, lockIncrement, lockBIncrement, lockPrecision, lockShowLabel, lockValue, lockType, lockContinuous, lockOneTurn: These flags specifies whether (when set to 1) or not (when set to 0) the user will be allowed to change the setting of the thumbwheel. - reportDelta is flag to specify whether value differences should be reported rathe than absolute values The widget has a configure() method: type, min, max, increment, precision, showLabel, value, continuous, oneTurn, orient, reportDelta can be set this way. a lock() method is used to disable the various gui components of the options panel. Usage: <instance>.configure(<component>=<value>) components see configure(). value is 0 or 1. 1 disables,0 enables. """ #FIXME: this would be the mechanism to remove all arguments from the # init (see DejaVu) # keywords = [ # 'master', # 'oneTurn', # ] def __init__(self, master=None, labCfg={ 'fg': 'black', 'side': 'left', 'text': None }, wheelLabCfg={}, canvasCfg={}, callback=None, type='float', min=None, max=None, increment=0.0, precision=2, showLabel=1, value=0.0, continuous=True, width=200, height=40, oneTurn=10., wheelPad=6, lockMin=0, lockBMin=0, lockMax=0, lockBMax=0, lockIncrement=0, lockBIncrement=0, lockPrecision=0, lockShowLabel=0, lockValue=0, lockType=0, lockContinuous=0, lockOneTurn=0, orient=None, reportDelta=0, **kw): # See FIXME init # if __debug__: # checkkeywords(kw) Tkinter.Frame.__init__(self, master) Tkinter.Pack.config(self, side='left', anchor='w') #FIXME: nblines are not dynamically computed self.nblines = 30 self.callback = None self.callbacks = CallbackManager() # object to manage callback # functions. They get called with the # current value as an argument self.width = 200 self.height = 40 self.setWidth(width) self.setHeight(height) # set widget orientation: either horizontal or vertical self.setOrient(orient) # initialize various attributes with default values self.precision = 2 # decimal places self.min = None # minimum value self.max = None # maximum value self.increment = increment # value increment self.minOld = 0. # used to store old values self.maxOld = 0. self.incrementOld = increment self.size = 50 # defines widget size self.offsetValue = 0. # used to set increment correctly self.lab = None # label self.opPanel = None # option panel widget self.oneTurn = 360. # value increment for 1 full turn self.value = 0.0 # current value of widget self.oldValue = 0.0 # old value of widget self.showLabel = 1 # turn on to display label on self.continuous = True # set to 1 to call callbacks at # each value change, else gets called # on button release event self.angle = 0. # angle corresponding to value self.labCfg = labCfg # Tkinter Label options self.labelFont = (ensureFontCase('helvetica'), 14, 'bold' ) # label font self.labelColor = 'yellow' # label color self.canvas = None # the canvas to create the widget in self.usedArcColor = '#aaaaaa' # filled arc color of used portion self.unusedArcColor = '#cccccc' # filled arc color of unused portion self.pyOver180 = math.pi / 180.0 # constants used in various places self.threeSixtyOver1turn = 1 self.piOver1turn = math.pi / 360. self.wheelLabCfg = wheelLabCfg # Tkinter wheel label options self.canvasCfg = canvasCfg # Tkinter Canvas options self.wheelPad = wheelPad # pad between wheel and widget borders self.deltaVal = 0. # delta with value at last callback self.valueLabel = None # Tkinter Label self.setType(type) # can be float or int self.discreteValue = 0. # value in discrete space self.lockMin = lockMin # lock<X> variables in configure() self.lockMax = lockMax # to lock/unlock entries in options # panel self.lockMin = lockMin # lock<X> vars are used in self.lock() self.lockMax = lockMax # to lock/unlock entries in optionpanel self.lockIncrement = lockIncrement self.lockBMin = lockBMin self.lockBMax = lockBMax self.lockBIncrement = lockBIncrement self.lockPrecision = lockPrecision self.lockShowLabel = lockShowLabel self.lockValue = lockValue self.lockType = lockType self.lockContinuous = lockContinuous self.lockOneTurn = lockOneTurn self.reportDelta = reportDelta self.setLabel(self.labCfg) self.createCanvas(master, wheelPad=wheelPad) self.canvas.bind("<ButtonPress-1>", self.mouseDown) self.canvas.bind("<ButtonRelease-1>", self.mouseUp) self.canvas.bind("<B1-Motion>", self.mouseMove) self.canvas.bind("<Button-3>", self.toggleOptPanel) self.valueLabel.bind("<ButtonPress-1>", self.mouseDown) self.valueLabel.bind("<ButtonRelease-1>", self.mouseUp) self.valueLabel.bind("<B1-Motion>", self.mouseMove) self.valueLabel.bind("<Button-3>", self.toggleOptPanel) if os.name == 'nt': #sys.platform == 'win32': self.canvas.bind("<MouseWheel>", self.mouseWheel) self.valueLabel.bind("<MouseWheel>", self.mouseWheel) else: self.canvas.bind("<Button-4>", self.mouseWheel) self.valueLabel.bind("<Button-4>", self.mouseWheel) self.canvas.bind("<Button-5>", self.mouseWheel) self.valueLabel.bind("<Button-5>", self.mouseWheel) self.bind("<Button-3>", self.toggleOptPanel) KeyboardEntry.__init__(self, (self, self.canvas, self.valueLabel), self.setFromEntry) self.opPanel = OptionsPanel(master=self, title="Thumbwheel Options") # now set the constructor options correctly using the configure method self.setValue(value) apply( self.configure, (), { 'type': type, 'min': min, 'max': max, 'increment': increment, 'precision': precision, 'showLabel': showLabel, 'continuous': continuous, 'oneTurn': oneTurn, 'lockType': lockType, 'lockMin': lockMin, 'lockBMin': lockBMin, 'lockMax': lockMax, 'lockBMax': lockBMax, 'lockIncrement': lockIncrement, 'lockBIncrement': lockBIncrement, 'lockPrecision': lockPrecision, 'lockShowLabel': lockShowLabel, 'lockValue': lockValue, 'lockContinuous': lockContinuous, 'lockOneTurn': lockOneTurn, 'orient': orient, 'reportDelta': reportDelta, 'callback': callback }) def setFromEntry(self, valueString): try: self.set(self.type(valueString), force=1) except ValueError: # fixme we would like to pop this up in a window maybe import traceback traceback.print_stack() traceback.print_exc() def handleKeyStroke(self, event): # handle key strokes for numbers only in widget keyboard entry label key = event.keysym if key.isdigit() or key == 'period' or key == 'minus' or key == 'plus': if key == 'period': key = '.' elif key == 'minus': key = '-' elif key == 'plus': key = '+' self.typedValue += key self.typedValueTK.configure(text=self.typedValue) else: KeyboardEntry.handleKeyStroke(self, event) def setWidth(self, width): assert isinstance(width, types.IntType),\ "Illegal width: expected %s, got %s"%(type(1),type(width)) assert width > 0, "Illegal width: must be > 0, got %s" % width self.width = width def setHeight(self, height): assert isinstance(height, types.IntType),\ "Illegal height: expected %s, got %s"%(type(1),type(height)) assert height > 0, "Illegal height: must be > 0, got %s" % height self.height = height def setCallbacks(self, cb): """Set widget callback. Must be callable function. Callback is called every time the widget value is set/modified""" assert cb is None or callable(cb) or type(cb) is types.ListType,\ "Illegal callback: must be either None or callable. Got %s"%cb if cb is None: return elif type(cb) is types.ListType: for func in cb: assert callable( func), "Illegal callback must be callable. Got %s" % func self.callbacks.AddCallback(func) else: self.callbacks.AddCallback(cb) self.callback = cb def toggleOptPanel(self, event=None): if self.opPanel.flag: self.opPanel.Dismiss_cb() else: if not hasattr(self.opPanel, 'optionsForm'): self.opPanel.displayPanel(create=1) else: self.opPanel.displayPanel(create=0) def mouseDown(self, event): # remember where the mouse went down if self.orient == 'horizontal': self.lastx = event.x else: self.lastx = event.y def mouseUp(self, event): # call callbacks if not in continuous mode newVal = self.get() if self.oldValue != newVal: if not self.continuous: self.oldValue = newVal self.callbacks.CallCallbacks(newVal) if self.showLabel == 2: # no widget labels on mouse release self.valueLabel.place_forget() def mouseMove(self, event): if self.orient == 'horizontal': dx = event.x - self.lastx self.lastx = event.x else: dx = event.y - self.lastx self.lasty = event.y dang = dx * math.pi / 180. val = dang * self.oneTurnOver2pi self.computeNewValue(val) def mouseWheel(self, event): #print "mouseWheel", event, event.num if os.name == 'nt': #sys.platform == 'win32': if event.delta > 0: lEventNum = 4 else: lEventNum = 5 else: lEventNum = event.num if lEventNum == 4: self.computeNewValue(self.oneTurn / 10) else: self.computeNewValue(-self.oneTurn / 10) def get(self): if self.reportDelta: return self.type(self.deltaVal) else: return self.type(self.value) def printLabel(self): if not self.showLabel in [1, 2]: return hl = self.valueLabel.winfo_reqheight() wl = self.valueLabel.winfo_reqwidth() h = self.canvas.winfo_reqheight() w = self.canvas.winfo_reqwidth() self.valueLabel.place(in_=self.canvas, x=(w - wl) * .5, y=((h - hl) * 0.5) - self.height / 4 + 2) self.valueLabel.configure(text=self.labelFormat % self.value) def set(self, val, update=1, force=0): # if force is set to 1, we call this method regardless of the # widget configuration. This is for example the case if the thumwheel # is set to continuous=0, but the value is set in the options panel if self.min is not None and val <= self.min: val = self.min elif self.max is not None and val >= self.max: val = self.max oldval = self.value self.value = val self.deltaVal = self.value - oldval newVal = self.get() if update and self.continuous or force: if self.oldValue != self.value or force: self.oldValue = newVal self.callbacks.CallCallbacks(newVal) if self.showLabel == 2: self.printLabel() if self.showLabel == 1: self.printLabel() if self.opPanel: self.opPanel.valInput.set(self.labelFormat % self.value) #FIXME: this could be renamed increment (to parallel the set method) # some code in this method is duplicated in set method def computeNewValue(self, val): # snap to closest increment oldval = self.value if self.increment is not None and self.increment != 0.: self.discreteValue = self.discreteValue + val if self.discreteValue >= self.increment: self.value = self.value + self.increment self.discreteValue = 0. elif -self.discreteValue >= self.increment: self.value = self.value - self.increment self.discreteValue = 0. else: self.value = self.value + val if self.min is not None and self.value <= self.min: self.value = self.min val = 0 elif self.max is not None and self.value >= self.max: self.value = self.max val = 0 self.deltaVal = self.value - oldval # self.angle is used in drawLines() self.angle = val / self.oneTurnOver2pi self.drawLines() if self.showLabel > 0: # ALWAYS self.printLabel() if self.opPanel: self.opPanel.valInput.set(self.labelFormat % self.value) newVal = self.get() if self.oldValue != newVal and not self.reportDelta: # this part is called either when the value is float OR when # continuous is off if self.continuous: self.oldValue = newVal self.callbacks.CallCallbacks(newVal) else: pass else: # this part is usually called when the datatype is INT AND # continuous is on if self.oldValue != newVal: # only call this part when we "reach" a new value self.oldValue = newVal self.callbacks.CallCallbacks(newVal) else: # else we need nothing to do pass def createCanvas(self, master, wheelPad=6): bw = self.borderWidth = wheelPad # distance between wheel and raise cd = { 'width': self.width + bw, 'height': self.height + bw, 'relief': 'raised', 'borderwidth': 3 } for k, w in self.canvasCfg.items(): cd[k] = w self.canvas = Tkinter.Canvas(self, cd) cbdw = int(self.canvas.cget('borderwidth')) bd = self.borderWidth + cbdw + 1 # +1 for pixel0 that is not drawn height = self.height - bw width = self.width - bw cp = self.canvas.create_polygon self.outline1 = cp(bd, bd, bd, height + cbdw, width + cbdw, height + cbdw, width + cbdw, bd, bd, bd, width=1, outline='gray60', fill='gray85') ul = bd + 1 # upper left pixel l = (width + cbdw - 1) - (bd + 1) # length of the inner box cl25 = 2. * l / 25. cl = self.canvas.create_line self.outline2 = cl(ul + int(cl25), ul, ul, ul, ul, height + cbdw - 1, ul + int(cl25), height + cbdw - 1, width=1, fill='gray20') self.outline3 = cl(ul + int(cl25), ul, ul + int(3 * cl25), ul, width=1, fill='gray60') self.outline4 = cl(ul + int(cl25), height + cbdw - 1, ul + int(3 * cl25), height + cbdw - 1, width=1, fill='gray60') self.outline5 = cl(ul + int(5 * cl25), ul, ul + int(7.5 * cl25), ul, width=1, fill='white') self.outline4 = cl(ul + int(5 * cl25), height + cbdw - 1, ul + int(7.5 * cl25), height + cbdw - 1, width=1, fill='white') self.outline6 = cl(ul + int(9.5 * cl25), ul, ul + int(11.5 * cl25), ul, width=1, fill='gray60') self.outline7 = cl(ul + int(9.5 * cl25), height + cbdw - 1, ul + int(11.5 * cl25), height + cbdw - 1, width=1, fill='gray60') re = ul + l self.outline8 = cl(ul + int(11.5 * cl25), ul, re, ul, re, height + cbdw - 1, ul + int(11.5 * cl25), height + cbdw - 1, width=1, fill='gray20') # corners of the box where the lines have to be drawn self.innerbox = (ul + 1, ul + 1, re - 1, height + cbdw - 1) self.circlePtsAngles = [] inc = 2 * math.pi / float(self.nblines) for i in range(self.nblines): self.circlePtsAngles.append(i * inc) self.circlePtsAngles = Numeric.array(self.circlePtsAngles, 'f') self.linesIds = [] self.shLinesIds = [] # this table should depend on the number of lines # currently it is of length 15 (self.nblines/2) # It should be resized automatically to self.nblines/2 self.shadowLinesOptions = [ (0, 'black', 1), # offset, color, width (2, 'gray30', 1), (2, 'gray30', 1), (0, 'gray30', 1), (-1, 'white', 1), (-1, 'white', 1), (-1, 'white', 2), (-1, 'white', 2), (-1, 'white', 2), (-1, 'white', 2), (-1, 'white', 1), (-1, 'white', 1), (0, 'gray30', 1), (-2, 'gray30', 1), (-2, 'gray30', 1), (0, 'black', 1), # offset, color, width (0, 'black', 1), # offset, color, width ] for i in range(self.nblines): self.linesIds.append(cl(0, 0, 0, 0, width=1, fill='black')) self.shLinesIds.append(cl(0, 0, 0, 0)) wlabCfg = {'padx': 0, 'pady': 0} wlabCfg.update(self.wheelLabCfg) self.valueLabel = apply(Tkinter.Label, (self.master, ), wlabCfg) self.drawLines() self.canvas.pack(side=Tkinter.LEFT) self.toggleWidgetLabel(self.showLabel) def drawLines(self): # angle has to be provided in radians angle = self.angle self.circlePtsAngles = self.circlePtsAngles + angle self.circlePtsAngles = Numeric.remainder(self.circlePtsAngles, 2 * math.pi) xcoords = Numeric.cos(self.circlePtsAngles) xsin = Numeric.sin(self.circlePtsAngles) if self.orient == 'horizontal': w = self.innerbox[2] - self.innerbox[0] r = w / 2 c = self.innerbox[0] + r y1 = self.innerbox[1] y2 = self.innerbox[3] else: w = self.innerbox[3] - self.innerbox[1] r = w / 2 c = self.innerbox[1] + r y1 = self.innerbox[0] y2 = self.innerbox[2] cl = self.canvas.create_line setCoords = self.canvas.coords setOpt = self.canvas.itemconfigure pi2 = math.pi / 2. drawLines = 0 co = Numeric.take(xcoords, Numeric.nonzero(Numeric.greater_equal(xsin, 0.0))) co = Numeric.sort(co) co = [-1] + list(co) for i in range(len(co)): x = c - int(co[i] * r) if self.orient == 'horizontal': setCoords(self.linesIds[i], x, y1, x, y2) else: setCoords(self.linesIds[i], y1, x, y2, x) shopt = self.shadowLinesOptions[i] x = x + shopt[0] if self.orient == 'horizontal': setCoords(self.shLinesIds[i], x, y1, x, y2) else: setCoords(self.shLinesIds[i], y1, x, y2, x) setOpt(self.shLinesIds[i], fill=shopt[1], width=shopt[2]) def toggleWidgetLabel(self, val): if val == 0: # no widget labels self.showLabel = 0 self.valueLabel.place_forget() if val == 1: # show always widget labels self.showLabel = 1 self.printLabel() if val == 2: # show widget labels only when mouse moves self.showLabel = 2 self.valueLabel.place_forget() def setValue(self, val): assert type(val) in [types.IntType, types.FloatType],\ "Illegal type for value: expected %s or %s, got %s"%( type(1), type(1.0), type(val) ) # setValue does NOT call a callback! if self.min is not None and val < self.min: val = self.min if self.max is not None and val > self.max: val = self.max self.value = self.type(val) self.oldValue = self.value self.offsetValue = self.value if self.showLabel == 1: self.printLabel() ##################################################################### # the 'configure' methods: ##################################################################### def configure(self, **kw): if 'type' in kw.keys(): # make sure type is set first self.setType(kw['type']) del kw['type'] for key, value in kw.items(): # the 'set' parameter callbacks if key == 'labCfg': self.setLabel(value) elif key == 'callback': self.setCallbacks(value) elif key == 'wheelLabCfg': self.wheelLablCfg.update(value) self.printLabel() elif key == 'min': self.setMin(value) elif key == 'max': self.setMax(value) elif key == 'increment': self.setIncrement(value) elif key == 'precision': self.setPrecision(value) elif key == 'showLabel': self.setShowLabel(value) elif key == 'continuous': self.setContinuous(value) elif key == 'oneTurn': self.setOneTurn(value) elif key == 'orient': self.setOrient(value) elif key == 'reportDelta': self.setReportDelta(value) # the 'lock' entries callbacks elif key == 'lockType': self.lockTypeCB(value) elif key == 'lockMin': self.lockMinCB(value) elif key == 'lockBMin': self.lockBMinCB(value) elif key == 'lockMax': self.lockMaxCB(value) elif key == 'lockBMax': self.lockBMaxCB(value) elif key == 'lockIncrement': self.lockIncrementCB(value) elif key == 'lockBIncrement': self.lockBIncrementCB(value) elif key == 'lockPrecision': self.lockPrecisionCB(value) elif key == 'lockShowLabel': self.lockShowLabelCB(value) elif key == 'lockValue': self.lockValueCB(value) elif key == 'lockContinuous': self.lockContinuousCB(value) elif key == 'lockOneTurn': self.lockOneTurnCB(value) def setLabel(self, labCfg): self.labCfg = labCfg text = labCfg.get('text', None) if text is None or text == '': return d = {} for k, w in self.labCfg.items(): if k == 'side': continue else: d[k] = w if not 'side' in self.labCfg.keys(): self.labCfg['side'] = 'left' if not self.lab: self.lab = Tkinter.Label(self, d) self.lab.pack(side=self.labCfg['side']) self.lab.bind("<Button-3>", self.toggleOptPanel) else: self.lab.configure(text) def setType(self, Type): assert type(Type) in [types.StringType, types.TypeType],\ "Illegal type for datatype. Expected %s or %s, got %s"%( type('a'), type(type), type(Type) ) if type(Type) == type(""): # type str assert Type in ('int','float'),\ "Illegal type descriptor. Expected 'int' or 'float', got '%s'"%Type self.type = eval(Type) else: self.type = Type if self.type == int: self.labelFormat = "%d" self.int_value = self.value else: self.labelFormat = "%." + str(self.precision) + "f" if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togIntFloat']['widget'] if self.type == int: w.setvalue('int') elif self.type == 'float': w.setvalue('float') if self.opPanel: self.opPanel.updateDisplay() if self.valueLabel and self.showLabel == 1: self.printLabel() def setMin(self, min): if min is not None: assert type(min) in [types.IntType, types.FloatType],\ "Illegal type for minimum. Expected type %s or %s, got %s"%( type(0), type(0.0), type(min) ) if self.max and min > self.max: min = self.max self.min = self.type(min) if self.showLabel == 1: self.printLabel() if self.value < self.min: self.set(self.min) if hasattr(self.opPanel, 'optionsForm'): self.opPanel.minInput.set(self.labelFormat % self.min) self.opPanel.toggleMin.set(1) self.opPanel.min_entry.configure(state='normal', fg='gray0') self.minOld = self.min else: self.min = None if hasattr(self.opPanel, 'optionsForm'): self.opPanel.toggleMin.set(0) self.opPanel.min_entry.configure(state='disabled', fg='gray40') def setMax(self, max): if max is not None: assert type(max) in [types.IntType, types.FloatType],\ "Illegal type for maximum. Expected type %s or %s, got %s"%( type(0), type(0.0), type(max) ) if self.min and max < self.min: max = self.min self.max = self.type(max) if self.showLabel == 1: self.printLabel() if self.value > self.max: self.set(self.max) if hasattr(self.opPanel, 'optionsForm'): self.opPanel.maxInput.set(self.labelFormat % self.max) self.opPanel.toggleMax.set(1) self.opPanel.max_entry.configure(state='normal', fg='gray0') self.maxOld = self.max else: self.max = None if hasattr(self.opPanel, 'optionsForm'): self.opPanel.toggleMax.set(0) self.opPanel.max_entry.configure(state='disabled', fg='gray40') def setIncrement(self, incr): if incr is not None: assert type(incr) in [types.IntType, types.FloatType],\ "Illegal type for increment. Expected type %s or %s, got %s"%( type(0), type(0.0), type(incr) ) self.increment = self.type(incr) self.offsetValue = self.value self.incrementOld = self.increment if hasattr(self.opPanel, 'optionsForm'): self.opPanel.incrInput.set(self.labelFormat % self.increment) self.opPanel.toggleIncr.set(1) self.opPanel.incr_entry.configure(state='normal', fg='gray0') else: self.increment = None if hasattr(self.opPanel, 'optionsForm'): self.opPanel.toggleIncr.set(0) self.opPanel.incr_entry.configure(state='disabled', fg='gray40') def setPrecision(self, val): assert type(val) in [types.IntType, types.FloatType],\ "Illegal type for precision. Expected type %s or %s, got %s"%( type(0), type(0.0), type(val) ) val = int(val) if val > 10: val = 10 if val < 1: val = 1 self.precision = val if self.type == float: self.labelFormat = "%." + str(self.precision) + "f" else: self.labelFormat = "%d" if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['selPrec']['widget'] w.setvalue(val) if self.opPanel: self.opPanel.updateDisplay() # and update the printed label if self.canvas and self.showLabel == 1: self.printLabel() def setContinuous(self, cont): """ cont can be None, 0 or 1 """ assert cont in [True, False, 0, 1],\ "Illegal value for continuous: expecting a boolean True, False, got %s"%cont self.continuous = cont if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togCont']['widget'] if cont: w.setvalue('on') else: w.setvalue('off') def setShowLabel(self, val): """Show label can be 0, 1 or 2 0: no label 1: show label only when value changes 2: label is always shown""" assert val in [0,1,2],\ "Illegal value for showLabel. Expected 0, 1 or 2, got %s"%val if val != 0 and val != 1 and val != 2: print "Illegal value. Must be 0, 1 or 2" return self.showLabel = val self.toggleWidgetLabel(val) if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togLabel']['widget'] if self.showLabel == 0: label = 'never' elif self.showLabel == 1: label = 'always' elif self.showLabel == 2: label = 'move' w.setvalue(label) if self.opPanel: self.opPanel.updateDisplay() def setOneTurn(self, oneTurn): assert type(oneTurn) in [types.IntType, types.FloatType],\ "Illegal type for oneTurn. Expected %s or %s, got %s"%( type(0), type(0.0), type(oneTurn) ) self.oneTurn = oneTurn self.threeSixtyOver1turn = 360. / oneTurn self.piOver1turn = math.pi / oneTurn self.oneTurnOver2pi = oneTurn / (2 * math.pi) if self.opPanel: self.opPanel.updateDisplay() def setOrient(self, orient): if orient is None: if self.width > self.height: orient = 'horizontal' else: orient = 'vertical' assert orient in ['horizontal', 'vertical'],\ "Expected 'vertical' or 'horizontal', got '%s'"%orient self.orient = orient def setReportDelta(self, rD): assert rD in [None, 0, 1],\ "Expected None, 0 or 1, got %s"%rD if rD is None or rD == 0: self.reportDelta = 0 else: self.reportDelta = 1 ##################################################################### # the 'lock' methods: ##################################################################### def lockTypeCB(self, mode): if mode != 0: mode = 1 self.lockType = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockMinCB(self, mode): #min entry field if mode != 0: mode = 1 self.lockMin = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockBMinCB(self, mode): # min checkbutton if mode != 0: mode = 1 self.lockBMin = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockMaxCB(self, mode): # max entry field if mode != 0: mode = 1 self.lockMax = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockBMaxCB(self, mode): # max checkbutton if mode != 0: mode = 1 self.lockBMax = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockIncrementCB(self, mode): # increment entry field if mode != 0: mode = 1 self.lockIncrement = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockBIncrementCB(self, mode): # increment checkbutton if mode != 0: mode = 1 self.lockBIncrement = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockPrecisionCB(self, mode): if mode != 0: mode = 1 self.lockPrecision = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockShowLabelCB(self, mode): if mode != 0: mode = 1 self.lockShowLabel = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockValueCB(self, mode): if mode != 0: mode = 1 self.lockValue = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockContinuousCB(self, mode): if mode != 0: mode = 1 self.lockContinuous = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockOneTurnCB(self, mode): if mode != 0: mode = 1 self.lockOneTurn = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay()
class Dial(Tkinter.Frame, KeyboardEntry): """This class implements a Dial widget. The widget has a pointer that can be moved around a circle. The range corresponding to one full turn can be specified as well as the min and max values that are allowed. By defaults these are set to None meaning that there is no min and no max. One turn corresponds to 360 units by default. A dial can also operate in discrete mode (if self.increment is set to x). In this mode the values will be restrained to be multiples of self.increment. The Widget has a Callback manager. Callback functions get called at every value change if self.contiguous is set to 1, else they get called when the mouse button is released. They always get called with the current value as an argument. An optional label can be displayed at the center of the Dial widget. The size of the dial has to be specified at instanciation. Other parameters can be set after the widget has been created. The widget tried to adjust automatically the size of the arrow according to the size of the dial. The widget has a configure() method: type, min, max, increment, precision, showLabel, value, continuous, oneTurn can be set this way. master, labCfg and size can be passed only to the constructor. a lock() method is used to disable the various gui components of the options panel. Usage: <instance>.lock(<component>=<value>) components see configure(). value is 0 or 1. 1 disables, 0 enables. Setting values with increment enabled: if using the method set(), the actual value will 'snap' to the next increment. i.e., if the value is set to 3, and the increment is set to 2, setting the value to 6 will actually result in 7 (3,5,7,9,.....) To still be able to set the value, disregarding the current active increment, the set method understands the optional keyword force=True, i.e. dial.set(<value>, force=True)), which will set the value to <value>. The increment will now be added to this new <value> """ def __init__(self, master=None, type='float', labCfg={ 'fg': 'black', 'side': 'left', 'text': None }, min=None, max=None, increment=.0, precision=2, showLabel=1, value=0.0, continuous=1, oneTurn=360., size=50, callback=None, lockMin=0, lockBMin=0, lockMax=0, lockBMax=0, lockIncrement=0, lockBIncrement=0, lockPrecision=0, lockShowLabel=0, lockValue=0, lockType=0, lockContinuous=0, lockOneTurn=0, **kw): Tkinter.Frame.__init__(self, master) Tkinter.Pack.config(self) self.callbacks = CallbackManager() # object to manage callback # functions. They get called with the # current value as an argument # initialize various attributes with default values self.precision = 2 # decimal places self.min = None # minimum value self.max = None # maximum value self.increment = increment # value increment self.minOld = 0. # used to store old values self.maxOld = 0. self.incrementOld = increment self.size = 50 # defines widget size self.offsetValue = 0. # used to set increment correctly self.lab = None # label self.callback = None # user specified callback self.opPanel = None # option panel widget self.oneTurn = 360. # value increment for 1 full turn self.value = 0.0 # current value of widget self.oldValue = 0.0 # old value of widget self.showLabel = 1 # turn on to display label on self.continuous = 1 # set to 1 to call callbacks at # each value change, else gets called # on button release event self.angle = 0. # angle corresponding to value self.labCfg = labCfg # Tkinter Label options self.labelFont = (ensureFontCase('helvetica'), 14, 'bold' ) # label font self.labelColor = 'yellow' # label color self.canvas = None # the canvas to create the widget in self.usedArcColor = '#aaaaaa' # filled arc color of used portion self.unusedArcColor = '#cccccc' # filled arc color of unused portion self.pyOver180 = math.pi / 180.0 # constants used in various places self.threeSixtyOver1turn = 1 self.piOver1turn = math.pi / 360. self.lockMin = lockMin # lock<X> vars are used in self.lock() self.lockMax = lockMax # to lock/unlock entries in optionpanel self.lockIncrement = lockIncrement self.lockBMin = lockBMin self.lockBMax = lockBMax self.lockBIncrement = lockBIncrement self.lockPrecision = lockPrecision self.lockShowLabel = lockShowLabel self.lockValue = lockValue self.lockType = lockType self.lockContinuous = lockContinuous self.lockOneTurn = lockOneTurn self.setArrow() # configure with user-defined values self.setSize(size) self.setCallback(callback) self.setContinuous(continuous) self.setType(type) self.setPrecision(precision) self.setOneTurn(oneTurn) self.setMin(min) self.setMax(max) self.setIncrement(increment) self.setShowLabel(showLabel) self.setValue(value) self.setLabel(self.labCfg) self.createCanvas(master) canvas = self.canvas canvas.bind("<ButtonPress-1>", self.mouseDown) canvas.bind("<ButtonRelease-1>", self.mouseUp) canvas.bind("<B1-Motion>", self.mouseMove) canvas.bind("<Button-3>", self.toggleOptPanel) if os.name == 'nt': #sys.platform == 'win32': canvas.bind("<MouseWheel>", self.mouseWheel) else: canvas.bind("<Button-4>", self.mouseWheel) canvas.bind("<Button-5>", self.mouseWheel) KeyboardEntry.__init__(self, (canvas, ), self.setFromEntry) self.opPanel = OptionsPanel(master=self, title="Dial Options") ## if self.callback: ## self.callbacks.AddCallback(self.callback) def setFromEntry(self, valueString): try: self.set(self.type(valueString)) except ValueError: # fixme we would like to pop this up in a window maybe import traceback traceback.print_stack() traceback.print_exc() def handleKeyStroke(self, event): # handle key strokes for numbers only in widget keyboard entry label key = event.keysym if key.isdigit() or key == 'period' or key == 'minus' or key == 'plus': if key == 'period': key = '.' elif key == 'minus': key = '-' elif key == 'plus': key = '+' self.typedValue += key self.typedValueTK.configure(text=self.typedValue) else: KeyboardEntry.handleKeyStroke(self, event) def setSize(self, size): """Set widget size. Size must be of type int and greater than 0""" assert isinstance(size, types.IntType),\ "Illegal size: expected type %s, got %s"%(type(1), type(size) ) assert size > 0, "Illegal size: must be > 0, got %s" % size self.size = size def setCallback(self, cb): """Set widget callback. Must be callable function. Callback is called every time the widget value is set/modified""" assert cb is None or callable(cb) or type(cb) is types.ListType,\ "Illegal callback: must be either None or callable, or list. Got %s"%cb if cb is None: return elif type(cb) is types.ListType: for func in cb: assert callable( func), "Illegal callback must be callable. Got %s" % func self.callbacks.AddCallback(func) else: self.callbacks.AddCallback(cb) self.callback = cb def toggleOptPanel(self, event=None): if self.opPanel.flag: self.opPanel.Dismiss_cb() else: if not hasattr(self.opPanel, 'optionsForm'): self.opPanel.displayPanel(create=1) else: self.opPanel.displayPanel(create=0) def setArrow(self, size=None): if size is not None: self.setSize(size) aS = self.size / 40 self.arrowLength = max(3, 3 * aS) # arrow head length self.arrowWidth = max(2, aS) # half the arrow body width self.arrowBorderwidth = max(1, self.arrowWidth / 2) # width of arrow # shadow lines self.arrowHeadWidth = 2 * self.arrowWidth # width of arrow head base def mouseDown(self, event): # remember where the mouse went down self.lastx = event.x self.lasty = event.y def mouseUp(self, event): # call callbacks if not in continuous mode if not self.continuous: self.callbacks.CallCallbacks(self.opPanel.valInput.get()) if self.showLabel == 2: # no widget labels on mouse release self.canvas.itemconfigure(self.labelId2, text='') self.canvas.itemconfigure(self.labelId, text='') def mouseMove(self, event): dx = event.x - self.xm dy = self.ym - event.y n = math.sqrt(dx * dx + dy * dy) if n == 0.0: v = [0.0, 0.0] else: v = [dx / n, dy / n] # find the cosine of the angle between new hand position and previous # hand position ma = v[0] * self.vector[0] + v[1] * self.vector[1] # assure no rounding errors if ma > 1.0: ma = 1.0 elif ma < -1.0: ma = -1.0 # compute angle increment compared to current vector ang = math.acos(ma) # find the sign of the rotation, sign of z component of vector prod. oldv = self.vector normz = oldv[0] * v[1] - oldv[1] * v[0] if normz > 0: ang = -1. * ang # compute the new value val = self.value + ang * self.oneTurnOver2pi self.set(val) self.lastx = event.x self.lasty = event.y def mouseWheel(self, event): #print "mouseWheel", event, event.num if os.name == 'nt': #sys.platform == 'win32': if event.delta > 0: lEventNum = 4 else: lEventNum = 5 else: lEventNum = event.num if lEventNum == 4: self.set(self.value + self.oneTurn) else: self.set(self.value - self.oneTurn) def get(self): return self.type(self.value) def printLabel(self): if self.canvas is None: return self.canvas.itemconfigure(self.labelId2, text=self.labelFormat % self.value) #newVal) self.canvas.itemconfigure(self.labelId, text=self.labelFormat % self.value) #newVal) def set(self, val, update=1, force=0): # if force is set to 1, we call this method regardless of the # widget configuration. This is for example the case if the dial # is set to continuous=0, but the value is set in the options panel # snap to closest increment if self.increment is not None and self.increment != 0. and not force: offset = self.offsetValue % self.increment dval = round(val / self.increment) * self.increment if val < dval: dval = dval + offset - self.increment else: dval = dval + offset if self.min is not None and dval < self.min: dval = self.min elif self.max is not None and dval > self.max: dval = self.max # recompute vector and angle corresponding to val self.angle = (dval % self.oneTurn) * self.threeSixtyOver1turn if dval < 0.0: self.angle = self.angle - 360.0 a = self.angle * self.pyOver180 self.vector = [math.sin(a), math.cos(a)] self.value = dval self.offsetValue = dval else: # 'regular' mode, i.e. no step-wise increment if self.min is not None and val < self.min: val = self.min elif self.max is not None and val > self.max: val = self.max # recompute vector and angle corresponding to val self.angle = (val % self.oneTurn) * self.threeSixtyOver1turn if val < 0.0: self.angle = self.angle - 360.0 a = self.angle * self.pyOver180 self.vector = [math.sin(a), math.cos(a)] self.value = val self.offsetValue = val #update arrow in display self.drawArrow() newVal = self.get() if self.continuous or force: if update and self.oldValue != newVal or force: self.oldValue = newVal self.callbacks.CallCallbacks(newVal) if self.showLabel == 2: self.printLabel() else: if self.showLabel == 2: self.printLabel() if self.showLabel == 1: self.printLabel() if self.opPanel: self.opPanel.valInput.set(self.labelFormat % newVal) def drawArrow(self): if self.canvas is None: return # end point x1 = self.xm + self.vector[0] * self.rad y1 = self.ym - self.vector[1] * self.rad # point at arrow head base xb = self.xm + self.vector[0] * self.radNoArrow yb = self.xm - self.vector[1] * self.radNoArrow # vector orthogonal to arrow n = [-self.vector[1], -self.vector[0]] pts1 = [ self.xm + n[0] * self.arrowWidth, self.ym + n[1] * self.arrowWidth, xb + n[0] * self.arrowWidth, yb + n[1] * self.arrowWidth, xb + n[0] * self.arrowHeadWidth, yb + n[1] * self.arrowHeadWidth, x1, y1 ] pts2 = [ x1, y1, xb - n[0] * self.arrowHeadWidth, yb - n[1] * self.arrowHeadWidth, xb - n[0] * self.arrowWidth, yb - n[1] * self.arrowWidth, self.xm - n[0] * self.arrowWidth, self.ym - n[1] * self.arrowWidth ] canvas = self.canvas if self.vector[0] > 0.0: col1 = '#DDDDDD' col2 = 'black' else: col1 = 'black' col2 = '#DDDDDD' apply(canvas.coords, (self.arrowPolId, ) + tuple(pts1 + pts2)) apply(canvas.coords, (self.arrowPolborder1, ) + tuple(pts1)) canvas.itemconfigure(self.arrowPolborder1, fill=col1) apply(canvas.coords, (self.arrowPolborder2, ) + tuple(pts2)) canvas.itemconfigure(self.arrowPolborder2, fill=col2) canvas.itemconfigure(self.arcId, extent=0.0 - self.angle) def createCanvas(self, master): size = self.size self.frame = Tkinter.Frame(self, borderwidth=3, relief='sunken') self.canvas = Tkinter.Canvas(self.frame, width=size + 2, height=size + 2) self.xm = self.ym = size / 2 + 2 self.rad = size / 2 self.radNoArrow = self.rad - self.arrowLength self.vector = [0, 1] x1 = self.xm + self.vector[0] * self.rad y1 = self.ym + self.vector[1] * self.rad canvas = self.canvas self.circleId = canvas.create_oval(2, 2, size, size, width=1, fill=self.unusedArcColor) self.arcId = canvas.create_arc(2, 2, size, size, start=90., extent=0, fill=self.usedArcColor) canvas.create_line(2, self.ym, size + 2, self.ym) canvas.create_line(self.xm, 2, self.ym, size + 2) self.arrowPolId = canvas.create_polygon(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, fill='gray75') self.arrowPolborder1 = canvas.create_line(0, 0, 0, 0, 0, 0, 0, 0, fill='black', width=self.arrowBorderwidth) self.arrowPolborder2 = canvas.create_line(0, 0, 0, 0, 0, 0, 0, 0, fill='white', width=self.arrowBorderwidth) r = size / 20 off = self.arrowBorderwidth canvas.create_oval(self.xm - r, self.ym - r - off / 2, self.xm + r, self.ym + r - off / 2, fill='#DDDDDD', outline='white') canvas.create_oval(self.xm - r, self.ym - r + off, self.xm + r, self.ym + r + off, fill='black', outline='black') canvas.create_oval(self.xm - r, self.ym - r, self.xm + r, self.ym + r, fill='gray70', outline='#DDDDDD') self.labelId2 = canvas.create_text(self.xm + 2, self.ym + 2, fill='black', justify='center', text='', font=self.labelFont) self.labelId = canvas.create_text(self.xm, self.ym, fill=self.labelColor, justify='center', text='', font=self.labelFont) self.drawArrow() self.opPanel = OptionsPanel(master=self, title="Dial Options") # pack em up self.canvas.pack(side=Tkinter.TOP) self.frame.pack(expand=1, fill='x') self.toggleWidgetLabel(self.showLabel) def toggleWidgetLabel(self, val): if val == 0: # no widget labels self.showLabel = 0 self.canvas.itemconfigure(self.labelId2, text='') self.canvas.itemconfigure(self.labelId, text='') if val == 1: # show always widget labels self.showLabel = 1 self.printLabel() if val == 2: # show widget labels only when mouse moves self.showLabel = 2 self.canvas.itemconfigure(self.labelId2, text='') self.canvas.itemconfigure(self.labelId, text='') def setValue(self, val): if type(val) == types.StringType: val = float(val) assert type(val) in [types.IntType, types.FloatType],\ "Illegal type for value: expected %s or %s, got %s"%( type(1), type(1.0), type(val) ) # setValue does NOT call a callback! if self.min is not None and val < self.min: val = self.min if self.max is not None and val > self.max: val = self.max self.value = self.type(val) self.offsetValue = self.value self.oldValue = self.value #update arrow in display self.angle = (self.value % self.oneTurn) * self.threeSixtyOver1turn if self.value < 0.0: self.angle = self.angle - 360.0 a = self.angle * self.pyOver180 self.vector = [math.sin(a), math.cos(a)] self.drawArrow() if self.showLabel == 1: self.printLabel() if self.opPanel: self.opPanel.valInput.set(self.labelFormat % self.value) def setLabel(self, labCfg): self.labCfg = labCfg text = labCfg.get('text', None) if text is None or text == '': return d = {} for k, w in self.labCfg.items(): if k == 'side': continue else: d[k] = w if not 'side' in self.labCfg.keys(): self.labCfg['side'] = 'left' if not self.lab: self.lab = Tkinter.Label(self, d) self.lab.pack(side=self.labCfg['side']) self.lab.bind("<Button-3>", self.toggleOptPanel) else: self.lab.configure(text) ##################################################################### # the 'configure' methods: ##################################################################### def configure(self, **kw): for key, value in kw.items(): # the 'set' parameter callbacks if key == 'labCfg': self.setLabel(value) elif key == 'type': self.setType(value) elif key == 'min': self.setMin(value) elif key == 'max': self.setMax(value) elif key == 'increment': self.setIncrement(value) elif key == 'precision': self.setPrecision(value) elif key == 'showLabel': self.setShowLabel(value) elif key == 'continuous': self.setContinuous(value) elif key == 'oneTurn': self.setOneTurn(value) # the 'lock' entries callbacks elif key == 'lockType': self.lockTypeCB(value) elif key == 'lockMin': self.lockMinCB(value) elif key == 'lockBMin': self.lockBMinCB(value) elif key == 'lockMax': self.lockMaxCB(value) elif key == 'lockBMax': self.lockBMaxCB(value) elif key == 'lockIncrement': self.lockIncrementCB(value) elif key == 'lockBIncrement': self.lockBIncrementCB(value) elif key == 'lockPrecision': self.lockPrecisionCB(value) elif key == 'lockShowLabel': self.lockShowLabelCB(value) elif key == 'lockValue': self.lockValueCB(value) elif key == 'lockContinuous': self.lockContinuousCB(value) elif key == 'lockOneTurn': self.lockOneTurnCB(value) def setType(self, Type): assert type(Type) in [types.StringType, types.TypeType],\ "Illegal type for datatype. Expected %s or %s, got %s"%( type('a'), type(type), type(Type) ) if type(Type) == type(""): # type str assert Type in ('int','float'),\ "Illegal type descriptor. Expected 'int' or 'float', got '%s'"%Type self.type = eval(Type) else: self.type = Type if self.type == int: self.labelFormat = "%d" self.int_value = self.value else: self.labelFormat = "%." + str(self.precision) + "f" if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togIntFloat']['widget'] if self.type == int: w.setvalue('int') elif self.type == 'float': w.setvalue('float') if self.opPanel: self.opPanel.updateDisplay() # and update the printed label if self.canvas and self.showLabel == 1: self.printLabel() def setMin(self, min): if min is not None: assert type(min) in [types.IntType, types.FloatType],\ "Illegal type for minimum. Expected type %s or %s, got %s"%( type(0), type(0.0), type(min) ) if self.max and min > self.max: min = self.max self.min = self.type(min) if self.showLabel == 1: self.printLabel() if self.value < self.min: self.set(self.min) if hasattr(self.opPanel, 'optionsForm'): self.opPanel.minInput.set(self.labelFormat % self.min) self.opPanel.toggleMin.set(1) self.opPanel.min_entry.configure(state='normal', fg='gray0') self.minOld = self.min else: self.min = None if hasattr(self.opPanel, 'optionsForm'): self.opPanel.toggleMin.set(0) self.opPanel.min_entry.configure(state='disabled', fg='gray40') def setMax(self, max): if max is not None: assert type(max) in [types.IntType, types.FloatType],\ "Illegal type for maximum. Expected type %s or %s, got %s"%( type(0), type(0.0), type(max) ) if self.min and max < self.min: max = self.min self.max = self.type(max) if self.showLabel == 1: self.printLabel() if self.value > self.max: self.set(self.max) if hasattr(self.opPanel, 'optionsForm'): self.opPanel.maxInput.set(self.labelFormat % self.max) self.opPanel.toggleMax.set(1) self.opPanel.max_entry.configure(state='normal', fg='gray0') self.maxOld = self.max else: self.max = None if hasattr(self.opPanel, 'optionsForm'): self.opPanel.toggleMax.set(0) self.opPanel.max_entry.configure(state='disabled', fg='gray40') def setIncrement(self, incr): if incr is not None: assert type(incr) in [types.IntType, types.FloatType],\ "Illegal type for increment. Expected type %s or %s, got %s"%( type(0), type(0.0), type(incr) ) self.increment = self.type(incr) self.offsetValue = self.value self.incrementOld = self.increment if hasattr(self.opPanel, 'optionsForm'): self.opPanel.incrInput.set(self.labelFormat % self.increment) self.opPanel.toggleIncr.set(1) self.opPanel.incr_entry.configure(state='normal', fg='gray0') else: self.increment = self.type(0) if hasattr(self.opPanel, 'optionsForm'): self.opPanel.toggleIncr.set(0) self.opPanel.incrInput.set(self.labelFormat % 0) self.opPanel.incr_entry.configure(state='disabled', fg='gray40') def setPrecision(self, val): assert type(val) in [types.IntType, types.FloatType],\ "Illegal type for precision. Expected type %s or %s, got %s"%( type(0), type(0.0), type(val) ) val = int(val) if val > 10: val = 10 if val < 1: val = 1 self.precision = val if self.type == float: self.labelFormat = "%." + str(self.precision) + "f" else: self.labelFormat = "%d" if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['selPrec']['widget'] w.setvalue(val) if self.opPanel: self.opPanel.updateDisplay() # and update the printed label if self.canvas and self.showLabel == 1: self.printLabel() def setContinuous(self, cont): """ cont can be None, 0 or 1 """ assert cont in [None, 0, 1],\ "Illegal value for continuous: expected None, 0 or 1, got %s"%cont if cont != 1: cont = None self.continuous = cont if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togCont']['widget'] if cont: w.setvalue('on') #i=1 else: w.setvalue('off') #i=0 if self.opPanel: self.opPanel.updateDisplay() def setShowLabel(self, val): """Show label can be 0, 1 or 2 0: no label 1: label is always shown 2: show label only when value changes""" assert val in [0,1,2],\ "Illegal value for showLabel. Expected 0, 1 or 2, got %s"%val if val != 0 and val != 1 and val != 2: print "Illegal value. Must be 0, 1 or 2" return self.showLabel = val self.toggleWidgetLabel(val) if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togLabel']['widget'] if self.showLabel == 0: label = 'never' elif self.showLabel == 1: label = 'always' elif self.showLabel == 2: label = 'move' w.setvalue(label) if self.opPanel: self.opPanel.updateDisplay() def setOneTurn(self, oneTurn): assert type(oneTurn) in [types.IntType, types.FloatType],\ "Illegal type for oneTurn. Expected %s or %s, got %s"%( type(0), type(0.0), type(oneTurn) ) self.oneTurn = oneTurn self.threeSixtyOver1turn = 360. / oneTurn self.piOver1turn = math.pi / oneTurn self.oneTurnOver2pi = oneTurn / (2 * math.pi) if self.opPanel: self.opPanel.updateDisplay() ##################################################################### # the 'lock' methods: ##################################################################### def lockTypeCB(self, mode): if mode != 0: mode = 1 self.lockType = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockMinCB(self, mode): #min entry field if mode != 0: mode = 1 self.lockMin = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockBMinCB(self, mode): # min checkbutton if mode != 0: mode = 1 self.lockBMin = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockMaxCB(self, mode): # max entry field if mode != 0: mode = 1 self.lockMax = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockBMaxCB(self, mode): # max checkbutton if mode != 0: mode = 1 self.lockBMax = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockIncrementCB(self, mode): # increment entry field if mode != 0: mode = 1 self.lockIncrement = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockBIncrementCB(self, mode): # increment checkbutton if mode != 0: mode = 1 self.lockBIncrement = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockPrecisionCB(self, mode): if mode != 0: mode = 1 self.lockPrecision = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockShowLabelCB(self, mode): if mode != 0: mode = 1 self.lockShowLabel = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockValueCB(self, mode): if mode != 0: mode = 1 self.lockValue = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockContinuousCB(self, mode): if mode != 0: mode = 1 self.lockContinuous = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockOneTurnCB(self, mode): if mode != 0: mode = 1 self.lockOneTurn = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay()
class xyzGUI(Tkinter.Frame): """ """ def __init__(self, master=None, name='XYZ',callback=None, callbackX=None, widthX=100, heightX=26, wheelPadX=4, labcfgX={'text':None}, widthY=100, heightY=26, wheelPadY=4, labcfgY={'text':None}, callbackY=None, widthZ=100, heightZ=26, wheelPadZ=4, callbackZ=None, labcfgZ={'text':None}, **kw): self.callback = callback # user specified callback self.name=name # title inside canvas self.widthX=widthX self.heightX=heightX self.wheelPadX=wheelPadX self.labcfgX=labcfgX self.widthY=widthY self.heightY=heightY self.wheelPadY=wheelPadY self.labcfgY=labcfgY self.widthZ=widthZ self.heightZ=heightZ self.wheelPadZ=wheelPadZ self.labcfgZ=labcfgZ Tkinter.Frame.__init__(self, master) Tkinter.Pack.config(self) self.callbacks = CallbackManager() # object to manage callback # functions. They get called with the # current value as an argument self.frame = Tkinter.Frame(self, relief = 'sunken', borderwidth=5) self.frame.pack(expand=1, fill='x') self.createEntries(self.frame) if self.callback: self.callbacks.AddCallback(self.callback) def createEntries(self, master): self.f = Tkinter.Frame(master) self.f.grid(column=1, rowspan=3) self.thumbx = ThumbWheel(master=self.f, width=self.widthX, height=self.heightX, labcfg=self.labcfgX, wheelPad=self.wheelPadX) self.thumbx.callbacks.AddCallback(self.thumbx_cb) self.thumbx.grid(row=0, column=0) self.thumby = ThumbWheel(master=self.f, width=self.widthY, height=self.heightY, labcfg=self.labcfgY, wheelPad=self.wheelPadY) self.thumby.callbacks.AddCallback(self.thumby_cb) self.thumby.grid(row=1, column=0) self.thumbz = ThumbWheel(master=self.f, width=self.widthZ, height=self.heightZ, labcfg=self.labcfgZ, wheelPad=self.wheelPadZ) self.thumbz.callbacks.AddCallback(self.thumbz_cb) self.thumbz.grid(row=2, column=0) self.f.pack(side='top', expand=1) def thumbx_cb(self, events=None): self.callbacks.CallCallbacks(self.thumbx.value) def thumby_cb(self, events=None): self.callbacks.CallCallbacks(self.thumby.value) def thumbz_cb(self, events=None): self.callbacks.CallCallbacks(self.thumbz.value) def set(self, x, y, z): # called from outside self.thumbx.setValue(x) self.thumby.setValue(y) self.thumbz.setValue(z) ##################################################################### # the 'configure' methods: ##################################################################### def configure(self, **kw): for key,value in kw.items(): # the 'set parameter' callbacks if key=='labcfgX': self.setLabel(value,'x') elif key=='labcfgY': self.setLabel(value,'y') elif key=='labcfgZ': self.setLabel(value,'z') elif key=='continuousX': self.setContinuous(value,'x') elif key=='continuousY': self.setContinuous(value,'y') elif key=='continuousZ': self.setContinuous(value,'z') elif key=='precisionX': self.setPrecision(value,'x') elif key=='precisionY': self.setPrecision(value,'y') elif key=='precisionZ': self.setPrecision(value,'z') elif key=='typeX': self.setType(value,'x') elif key=='typeY': self.setType(value,'y') elif key=='typeZ': self.setType(value,'z') elif key=='minX': self.setMin(value,'x') elif key=='minY': self.setMin(value,'y') elif key=='minZ': self.setMin(value,'z') elif key=='maxX': self.setMax(value,'x') elif key=='maxY': self.setMax(value,'y') elif key=='maxZ': self.setMax(value,'z') elif key=='oneTurnX': self.setOneTurn(value,'x') elif key=='oneTurnY': self.setOneTurn(value,'y') elif key=='oneTurnZ': self.setOneTurn(value,'z') elif key=='showLabelX': self.setShowLabel(value,'x') elif key=='showLabelY': self.setShowLabel(value,'y') elif key=='showLabelZ': self.setShowLabel(value,'z') elif key=='incrementX': self.setIncrement(value,'x') elif key=='incrementY': self.setIncrement(value,'y') elif key=='incrementZ': self.setIncrement(value,'z') #################################################### elif key=='lockTypeX': self.lockType(value,'x') elif key=='lockTypeY': self.lockType(value,'y') elif key=='lockTypeZ': self.lockType(value,'z') elif key=='lockMinX': self.lockMin(value,'x') elif key=='lockMinY': self.lockMin(value,'y') elif key=='lockMinZ': self.lockMin(value,'z') elif key=='lockBMinX': self.lockBMin(value,'x') elif key=='lockBMinY': self.lockBMin(value,'y') elif key=='lockBMinZ': self.lockBMin(value,'z') elif key=='lockMaxX': self.lockMax(value,'x') elif key=='lockMaxY': self.lockMax(value,'y') elif key=='lockMaxZ': self.lockMax(value,'z') elif key=='lockBMaxX': self.lockBMax(value,'x') elif key=='lockBMaxY': self.lockBMax(value,'y') elif key=='lockBMaxZ': self.lockBMax(value,'z') elif key=='lockIncrementX': self.lockIncrement(value,'x') elif key=='lockIncrementY': self.lockIncrement(value,'y') elif key=='lockIncrementZ': self.lockIncrement(value,'z') elif key=='lockBIncrementX': self.lockBIncrement(value,'x') elif key=='lockBIncrementY': self.lockBIncrement(value,'y') elif key=='lockBIncrementZ': self.lockBIncrement(value,'z') elif key=='lockPrecisionX': self.lockPrecision(value,'x') elif key=='lockPrecisionY': self.lockPrecision(value,'y') elif key=='lockPrecisionZ': self.lockPrecision(value,'z') elif key=='lockShowLabelX': self.lockShowLabel(value,'x') elif key=='lockShowLabelY': self.lockShowLabel(value,'y') elif key=='lockShowLabelZ': self.lockShowLabel(value,'z') elif key=='lockValueX': self.lockValue(value,'x') elif key=='lockValueY': self.lockValue(value,'y') elif key=='lockValueZ': self.lockValue(value,'z') elif key=='lockContinuousX': self.lockContinuous(value,'x') elif key=='lockContinuousY': self.lockContinuous(value,'y') elif key=='lockContinuousZ': self.lockContinuous(value,'z') elif key=='lockOneTurnX': self.lockOneTurn(value,'x') elif key=='lockOneTurnY': self.lockOneTurn(value,'y') elif key=='lockOneTurnZ': self.lockOneTurn(value,'z') def setLabel(self, label, mode): if mode == 'x': self.thumbx.setLabel(label) elif mode == 'y': self.thumby.setLabel(label) elif mode == 'z': self.thumbz.setLabel(label) def setContinuous(self, value, mode): if mode == 'x': self.thumbx.setContinuous(value) elif mode == 'y': self.thumby.setContinuous(value) elif mode == 'z': self.thumbz.setContinuous(value) def setPrecision(self, value, mode): if mode == 'x': self.thumbx.setPrecision(value) elif mode == 'y': self.thumby.setPrecision(value) elif mode == 'z': self.thumbz.setPrecision(value) def setType(self, type, mode): if type == 'int': type = int else: type = float if mode == 'x': self.thumbx.setType(type) elif mode == 'y': self.thumby.setType(type) elif mode == 'z': self.thumbz.setType(type) def setMin(self, value, mode): if mode == 'x': self.thumbx.setMin(value) elif mode == 'y': self.thumby.setMin(value) elif mode == 'z': self.thumbz.setMin(value) def setMax(self, value, mode): if mode == 'x': self.thumbx.setMax(value) elif mode == 'y': self.thumby.setMax(value) elif mode == 'z': self.thumbz.setMax(value) def setOneTurn(self, value, mode): if mode == 'x': self.thumbx.setOneTurn(value) elif mode == 'y': self.thumby.setOneTurn(value) elif mode == 'z': self.thumbz.setOneTurn(value) def setShowLabel(self, value, mode): if mode == 'x': self.thumbx.setShowLabel(value) if mode == 'y': self.thumby.setShowLabel(value) if mode == 'z': self.thumbz.setShowLabel(value) def setIncrement(self, value, mode): if mode == 'x': self.thumbx.setIncrement(value) if mode == 'y': self.thumby.setIncrement(value) if mode == 'z': self.thumbz.setIncrement(value) ##################################################################### # the 'lock' methods: ##################################################################### def lockType(self, value, mode): if mode == 'x': self.thumbx.lockTypeCB(value) if mode == 'y': self.thumby.lockTypeCB(value) if mode == 'z': self.thumbz.lockTypeCB(value) def lockMin(self, value, mode): if mode == 'x': self.thumbx.lockMinCB(value) if mode == 'y': self.thumby.lockMinCB(value) if mode == 'z': self.thumbz.lockMinCB(value) def lockBMin(self, value, mode): if mode == 'x': self.thumbx.lockBMinCB(value) if mode == 'y': self.thumby.lockBMinCB(value) if mode == 'z': self.thumbz.lockBMinCB(value) def lockMax(self, value, mode): if mode == 'x': self.thumbx.lockMaxCB(value) if mode == 'y': self.thumby.lockMaxCB(value) if mode == 'z': self.thumbz.lockMaxCB(value) def lockBMax(self, value, mode): if mode == 'x': self.thumbx.lockBMaxCB(value) if mode == 'y': self.thumby.lockBMaxCB(value) if mode == 'z': self.thumbz.lockBMaxCB(value) def lockIncrement(self, value, mode): if mode == 'x': self.thumbx.lockIncrementCB(value) if mode == 'y': self.thumby.lockIncrementCB(value) if mode == 'z': self.thumbz.lockIncrementCB(value) def lockBIncrement(self, value, mode): if mode == 'x': self.thumbx.lockBIncrementCB(value) if mode == 'y': self.thumby.lockBIncrementCB(value) if mode == 'z': self.thumbz.lockBIncrementCB(value) def lockPrecision(self, value, mode): if mode == 'x': self.thumbx.lockPrecisionCB(value) if mode == 'y': self.thumby.lockPrecisionCB(value) if mode == 'z': self.thumbz.lockPrecisionCB(value) def lockShowLabel(self, value, mode): if mode == 'x': self.thumbx.lockShowLabelCB(value) if mode == 'y': self.thumby.lockShowLabelCB(value) if mode == 'z': self.thumbz.lockShowLabelCB(value) def lockValue(self, value, mode): if mode == 'x': self.thumbx.lockValueCB(value) if mode == 'y': self.thumby.lockValueCB(value) if mode == 'z': self.thumbz.lockValueCB(value) def lockContinuous(self, value, mode): if mode == 'x': self.thumbx.lockContinuousCB(value) if mode == 'y': self.thumby.lockContinuousCB(value) if mode == 'z': self.thumbz.lockContinuousCB(value) def lockOneTurn(self, value, mode): if mode == 'x': self.thumbx.lockOneTurnCB(value) if mode == 'y': self.thumby.lockOneTurnCB(value) if mode == 'z': self.thumbz.lockOneTurnCB(value)
class CoefCanvas: def __init__(self,master,title=None,width=150,height=50, callback=None): #tkCol is a list of list where each elemnt is a color # tkCol[0][0] = a Tk color # size = size of the sqare representing the Tkcolor on the canvas # border = border size # if not master: master = Tkinter.Toplevel() if title is not None: master.title(title) f = self.frame = Tkinter.Frame(master) self.coeff = 0.0 self.value = "%.1f"%self.coeff+'%' self.width = width self.height = height self.cwcanvas = Tkinter.Canvas(f,width=self.width, height=self.height) self.wrect = self.cwcanvas.create_rectangle(0,0,100,10, outline='black', fill='white') self.rrect = self.cwcanvas.create_rectangle(0,0,self.coeff,10, outline='black', fill='red') self.labelvalue = self.cwcanvas.create_text(120,10,text=self.value) self.cwcanvas.pack(side='top') #self.frame.pack() # CallBack Manager self.cbManager = CallbackManager() if callback: if type(callback) in [types.ListType, types.TupleType]: map(self.cbManager.AddCallback, callback) else: self.cbManager.AddCallback(callback) def get(self): pass def set(self,coeff,color): self.cwcanvas.coords(self.rrect,0,0,coeff,10) self.cwcanvas.itemconfigure(self.rrect,fill=color) self.cwcanvas.itemconfigure(self.labelvalue,text="%.1f"%coeff+'%') def pack(self,*args, **kw): apply(self.frame.pack, args, kw) def pack_forget(self,*args, **kw): apply(self.frame.pack_forget, args, kw) def grid(self,*args, **kw): apply(self.frame.grid, args, kw) def grid_forget(self,*args, **kw): apply(self.frame.grid_forget, args, kw) def destroy(self,*args, **kw): apply(self.frame.destroy, args, kw)
class PatternCanvas: def __init__(self,master,title=None,callback=None, size=5,border=1,tkCol=None,immediate=1): #tkCol is a list of list where each elemnt is a color # tkCol[0][0] = a Tk color # size = size of the sqare representing the Tkcolor on the canvas # border = border size # if not master: master = Tkinter.Toplevel() if title is not None: master.title(title) f = self.frame = Tkinter.Frame(master) if tkCol: self.Xelemt = len(tkCol) self.Yelemt = len(tkCol[0]) self.size = size self.border = border self.width =(self.Xelemt*self.size+2*self.border) self.height =(self.Yelemt*self.size+2*self.border) self.cwcanvas = Tkinter.Canvas(f,width=self.width, height=self.height, borderwidth=3 ) xo = self.border for i in range(self.Xelemt): xe = xo + self.size yo = self.border for j in range(self.Yelemt): ye = yo + self.size self.cwcanvas.create_rectangle(xo, yo, xe, ye, fill=tkCol[i][j]) yo = ye xo = xe self.cwcanvas.pack() #self.frame.pack() # CallBack Manager self.cbManager = CallbackManager() if callback: if type(callback) in [types.ListType, types.TupleType]: map(self.cbManager.AddCallback, callback) else: self.cbManager.AddCallback(callback) def get(self): pass def set(self): pass def pack(self,*args, **kw): apply(self.frame.pack, args, kw) def pack_forget(self,*args, **kw): apply(self.frame.pack_forget, args, kw) def grid(self,*args, **kw): apply(self.frame.grid, args, kw) def grid_forget(self,*args, **kw): apply(self.frame.grid_forget, args, kw) def destroy(self,*args, **kw): apply(self.frame.destroy, args, kw)
class vectorGUI(KeyboardModifierMonitor): """ This class implements a vector widget. The widget has a vector which can be moved within a sphere to generate a 3D vector. Values are normalized and stored in self.vector In addition, the vector can be rotated with 3 thumbwheels. Values can be entered directly by typing them into the 3 entry forms. Then, the 'normalize and set' button has to be pressed in order to normalize and set the new vector. The widget has a configure() method: vector, mode, precision and continuous can be set this way. vector is a list of 3 floating values, e.g. [0., 0., 1.] mode describes the axis movement (rotation around an axis): is type string and can be either 'X', 'Y' or 'Z'. Free movement (standard value) is 'XY'. continuous can be either 0 (or None) or 1. Default is 0 precision is type int and ranges from 1 - 10 master, name and size can be passed only to the constructor. a lock() method is used to disable the various gui components of the options panel. Usage: <instance>.lock(<component>=<value>) component is continuous, precision or mode. value is 0 or 1. 1 disables, 0 enables. """ def pack(self, *args, **kw): self.master.pack(args, **kw) def grid(self, *args, **kw): self.master.grid(args, **kw) def bind(self, *args, **kw): self.master.bind(args, **kw) def unbind(self, *args, **kw): self.master.unbind(args, **kw) def grid_forget(self): self.master.grid_forget() def __init__(self, master=None, name='vector', size=200, continuous=1, vector=[0.0, 0.0, 1.0], mode='XY', precision=5, lockContinuous=0, lockPrecision=0, lockMode=0, callback=None, labelSide='top'): KeyboardModifierMonitor.__init__(self) self.callback = callback # user specified callback self.name = name # title inside canvas self.labelSide = labelSide # where title gets packed self.mode = mode # axe mode: can be 'XY', 'X', 'Y' or 'Z' self.precision = precision # floating number digits self.continuous = continuous # can be 1 or 0 self.vector = vector # initial vector value self.size = size # size of vector widget self.lockContinuous = lockContinuous # set to 1 to lock menus in # option panel self.lockPrecision = lockPrecision self.lockMode = lockMode self.r = self.size / 2 self.r2 = self.r * self.r self.drawShadowX = 0 self.drawShadowY = 1 self.drawShadowZ = 0 self.fillShadowPlanes = 1 self.master = master = tkinter.Frame(master) #Tkinter.Frame.__init__(self, master) #Tkinter.Pack.config(self) self.callbacks = CallbackManager() # object to manage callback # functions. They get called with the # current value as an argument self.zeros = numpy.array((0, 0, 0), numpy.int16) self.viewingMatInv = numpy.array( [[0.96770716, -0.03229283, -0.25, 0.], [0.03229283, -0.96770716, 0.25, 0.], [0.25, 0.25, 0.93541437, 0.], [0., 0., 0., 1.]], 'f') self.viewingMat = numpy.transpose(self.viewingMatInv) self.createCanvas(master, size) self.createEntries(self.frame) tkinter.Widget.bind(self.canvas, "<ButtonPress-1>", self.mouseDown) tkinter.Widget.bind(self.canvas, "<ButtonRelease-1>", self.mouseUp) tkinter.Widget.bind(self.canvas, "<B1-Motion>", self.mouseMove) self.setEntries() self.opPanel = VectorOptionsPanel(master=self, title="Vector GUI Options") tkinter.Widget.bind(self.canvas, "<Button-3>", self.toggleOptPanel) if self.callback: self.callbacks.AddCallback(self.callback) def toggleOptPanel(self, event=None): # opens and closes options panel by right clicking on widget if self.opPanel.flag: self.opPanel.Dismiss_cb() else: if not hasattr(self.opPanel, 'optionsForm'): self.opPanel.displayPanel(create=1) else: self.opPanel.displayPanel(create=0) def mouseUp(self, event): if not self.continuous: self.callbacks.CallCallbacks(self.vector) def mouseDown(self, event): # remember where the mouse went down xc = event.x - self.xm yc = self.ym - event.y # compute the intersection point between z2 = self.r2 - (xc * xc) - (yc * yc) if z2 >= 0: # we picked inside the sphere. going for a XY rotation self.lastPt3D = (xc, yc, math.sqrt(z2)) else: # going for a Z rotation pass def mouseMove(self, event): # simple trackball, only works inside cirle # creates an XY rotation defined by pts intersecting the spheres xc = event.x - self.xm yc = self.ym - event.y # compute the intersection point between xc2 = xc * xc yc2 = yc * yc z2 = self.r2 - xc2 - yc2 if z2 < 0: lInvMag = 1. / math.sqrt(xc2 + yc2) xc *= lInvMag * (self.r) yc *= lInvMag * (self.r) z2 = 0 # compute rotation angle a = self.lastPt3D b = (xc, yc, math.sqrt(z2)) ang = math.acos((a[0] * b[0] + a[1] * b[1] + a[2] * b[2]) / self.r2) if self.mode == 'XY': #compute rotation axis rotaxis = numpy.array( (a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]), 'f') elif self.mode == 'X': rotaxis = numpy.array((1., 0., 0.), 'f') elif self.mode == 'Y': rotaxis = numpy.array((0., 1., 0.), 'f') elif self.mode == 'Z': rotaxis = numpy.array((0., 0., 1.), 'f') mat = rotax(self.zeros, rotaxis, ang) self.lastPt3D = b self.updateVector(mat) def updateVector(self, mat): mat = numpy.reshape(mat, (4, 4)) newPts = self.vector + [1] newPts = numpy.dot([newPts], mat)[0] self.vector = [newPts[0], newPts[1], newPts[2]] self.setEntries() self.drawVector() if self.continuous: self.callbacks.CallCallbacks(self.vector) def drawVector(self): coords3D = self.vector + [1] # apply viewing transformation to vector newPtsWithView = numpy.dot([coords3D], self.viewingMat)[0] # compute 2D projection of vector (broken on 2 segments for # depth cueing x1 = self.xm + int(newPtsWithView[0] * (self.xm)) y1 = self.ym + int(newPtsWithView[1] * (self.ym)) # change vector's segments coordinates self.canvas.coords(self.lId1, self.xm, self.ym, x1, y1) # update vector shadows # Y=0 plane if self.drawShadowY: pt = [coords3D[0], 0, coords3D[2], 1.] newPtsWithView = numpy.dot([pt], self.viewingMat)[0] xm = self.xm + int(newPtsWithView[0] * (self.xm)) ym = self.ym + int(newPtsWithView[1] * (self.ym)) if self.fillShadowPlanes: self.canvas.coords(self.shadowPY, self.xm, self.ym, xm, ym, x1, y1) self.canvas.coords(self.shadowY, self.xm, self.ym, xm, ym, x1, y1) # X=0 plane if self.drawShadowX: pt = [0, coords3D[1], coords3D[2], 1.] newPtsWithView = numpy.dot([pt], self.viewingMat)[0] xm = self.xm + int(newPtsWithView[0] * (self.xm)) ym = self.ym + int(newPtsWithView[1] * (self.ym)) if self.fillShadowPlanes: self.canvas.coords(self.shadowPX, self.xm, self.ym, xm, ym, x1, y1) self.canvas.coords(self.shadowX, self.xm, self.ym, xm, ym, x1, y1) # Z=0 plane if self.drawShadowZ: pt = [coords3D[0], coords3D[1], 0, 1.] newPtsWithView = numpy.dot([pt], self.viewingMat)[0] xm = self.xm + int(newPtsWithView[0] * (self.xm)) ym = self.ym + int(newPtsWithView[1] * (self.ym)) if self.fillShadowPlanes: self.canvas.coords(self.shadowPZ, self.xm, self.ym, xm, ym, x1, y1) self.canvas.coords(self.shadowZ, self.xm, self.ym, xm, ym, x1, y1) if self.vector[0] < 0.0: self.canvas.tag_raise('verticalCircle', 'moving') else: self.canvas.tag_lower('verticalCircle', 'moving') if self.vector[1] < 0.0: self.canvas.tag_raise('horizontalCircle', 'moving') else: self.canvas.tag_lower('horizontalCircle', 'moving') if self.vector[2] < 0.0 or self.vector[1] < 0.0: self.canvas.tag_raise('axis', 'moving') else: self.canvas.tag_lower('axis', 'moving') def thumbx_cb(self, events=None): val = self.thumbx.value ## valX=self.thumbx.value ## valY=self.thumby.value ## valZ=self.thumbz.value ## n = math.sqrt(valX*valX+valY*valY+valZ*valZ) ## if n == 0.0: v = [0.0, 0.0, 1.0] ## else: v = [valX/n, valY/n, valZ/n] ## val = v[0] rot = numpy.zeros((4, 4), 'f') rot[0][0] = 1.0 rot[1][1] = math.cos(val) rot[1][2] = -math.sin(val) rot[2][1] = math.sin(val) rot[2][2] = math.cos(val) self.updateVector(rot) def thumby_cb(self, events=None): val = self.thumby.value rot = numpy.zeros((4, 4), 'f') rot[0][0] = math.cos(val) rot[0][2] = -math.sin(val) rot[1][1] = 1.0 rot[2][0] = math.sin(val) rot[2][2] = math.cos(val) self.updateVector(rot) def thumbz_cb(self, events=None): val = self.thumbz.value rot = numpy.zeros((4, 4), 'f') rot[0][0] = math.cos(val) rot[0][1] = -math.sin(val) rot[1][0] = math.sin(val) rot[1][1] = math.cos(val) rot[2][2] = 1.0 self.updateVector(rot) def entryX_cb(self, event=None): val = self.entryXTk.get() if len(val) == 0: val = self.vector[0] try: val = float(val) self.entryXTk.set(self.thumbx.labelFormat % val) except ValueError: # put back original value if someone types garbage self.entryXTk.set(self.thumbx.labelFormat % self.vector[0]) def entryY_cb(self, event=None): val = self.entryYTk.get() if len(val) == 0: val = self.vector[1] try: val = float(val) self.entryYTk.set(self.thumby.labelFormat % val) except ValueError: # put back original value if someone types garbage self.entryYTk.set(self.thumby.labelFormat % self.vector[1]) def entryZ_cb(self, event=None): val = self.entryZTk.get() if len(val) == 0: val = self.vector[2] try: val = float(val) self.entryZTk.set(self.thumbz.labelFormat % val) except ValueError: # put back original value if someone types garbage self.entryZTk.set(self.thumbz.labelFormat % self.vector[2]) def entryV_cb(self, event=None): v = self.entryVTk.get() try: val = string.split(v) except: self.setEntries() return if val is None or len(val) != 3: self.setEntries() return try: valX = float(val[0]) valY = float(val[1]) valZ = float(val[2]) except: self.setEntries() return # compute normalized vector n = math.sqrt(valX * valX + valY * valY + valZ * valZ) if n == 0.0: v = [0.0, 0.0, 1.0] else: v = [valX / n, valY / n, valZ / n] self.vector = v self.setEntries() self.drawVector() if self.continuous: self.callbacks.CallCallbacks(self.vector) def setButton_cb(self, event=None): valX = float(self.entryXTk.get()) valY = float(self.entryYTk.get()) valZ = float(self.entryZTk.get()) # compute normalized vector n = math.sqrt(valX * valX + valY * valY + valZ * valZ) if n == 0.0: v = [0.0, 0.0, 1.0] else: v = [valX / n, valY / n, valZ / n] self.vector = v self.setEntries() self.drawVector() if self.continuous: self.callbacks.CallCallbacks(self.vector) def createEntries(self, master): self.f = tkinter.Frame(master) #self.f.grid(column=3, rowspan=3) def fX(): self.vector = [1., 0., 0.] self.setEntries() self.callbacks.CallCallbacks(self.vector) def fY(): self.vector = [0., 1., 0.] self.setEntries() self.callbacks.CallCallbacks(self.vector) def fZ(): self.vector = [0., 0., 1.] self.setEntries() self.callbacks.CallCallbacks(self.vector) f1 = tkinter.Frame(master) f2 = tkinter.Frame(master) f3 = tkinter.Frame(master) f1.pack(side='top') f2.pack(side='top') f3.pack(side='top') lX = tkinter.Button(master=f1, text='x', command=fX) lX.pack(side='left') lY = tkinter.Button(master=f2, text='y', command=fY) lY.pack(side='left') lZ = tkinter.Button(master=f3, text='z', command=fZ) lZ.pack(side='left') #lX = Tkinter.Button(master=self.f, text='x', command=fX) #lY = Tkinter.Button(master=self.f, text='y', command=fY) #lZ = Tkinter.Button(master=self.f, text='z', command=fZ) #lX.grid(row=0, column=0) #lY.grid(row=1, column=0) #lZ.grid(row=2, column=0) #self.thumbx = ThumbWheel(master=self.f, width=50, self.thumbx = ThumbWheel(master=f1, width=50, height=20, labcfg={ 'text': 'X:', 'side': 'left' }, wheelPad=2, oneTurn=.1, min=-1, max=1, showLabel=0, precision=5, type=float) self.thumbx.callbacks.AddCallback(self.thumbx_cb) self.thumbx.unbind("<Button-3>") self.thumbx.canvas.unbind("<Button-3>") #self.thumbx.grid(row=0, column=1) self.thumbx.pack(side='left') #self.thumby = ThumbWheel(master=self.f, width=50, self.thumby = ThumbWheel(master=f2, width=50, height=20, labcfg={ 'text': 'Y:', 'side': 'left' }, wheelPad=2, oneTurn=.1, min=-1, max=1, showLabel=0, precision=5, type=float) self.thumby.callbacks.AddCallback(self.thumby_cb) self.thumby.unbind("<Button-3>") self.thumby.canvas.unbind("<Button-3>") #self.thumby.grid(row=1, column=1) self.thumby.pack(side='left') #self.thumbz = ThumbWheel(master=self.f, width=50, self.thumbz = ThumbWheel(master=f3, width=50, height=20, labcfg={ 'text': 'Z:', 'side': 'left' }, wheelPad=2, oneTurn=.1, min=-1, max=1, showLabel=0, precision=5, type=float) self.thumbz.callbacks.AddCallback(self.thumbz_cb) self.thumbz.unbind("<Button-3>") self.thumbz.canvas.unbind("<Button-3>") #self.thumbz.grid(row=2, column=1) self.thumbz.pack(side='left') self.entryXTk = tkinter.StringVar() #self.entryX = Tkinter.Entry(master=self.f, textvariable=self.entryXTk, self.entryX = tkinter.Entry(master=f1, textvariable=self.entryXTk, width=8) self.entryX.bind('<Return>', self.entryX_cb) #self.entryX.grid(row=0, column=2) self.entryX.pack(side='left') self.entryYTk = tkinter.StringVar() #self.entryY = Tkinter.Entry(master=self.f, textvariable=self.entryYTk, self.entryY = tkinter.Entry(master=f2, textvariable=self.entryYTk, width=8) self.entryY.bind('<Return>', self.entryY_cb) #self.entryY.grid(row=1, column=2) self.entryY.pack(side='left') self.entryZTk = tkinter.StringVar() #self.entryZ = Tkinter.Entry(master=self.f, textvariable=self.entryZTk, self.entryZ = tkinter.Entry(master=f3, textvariable=self.entryZTk, width=8) self.entryZ.bind('<Return>', self.entryZ_cb) #self.entryZ.grid(row=2, column=2) self.entryZ.pack(side='left') self.entryVTk = tkinter.StringVar() self.entryV = tkinter.Entry(master, textvariable=self.entryVTk, width=18) self.entryV.bind('<Return>', self.entryV_cb) self.f.pack(side='top', expand=1) self.entryV.pack() self.setButton = tkinter.Button(master, text='normalize and set', command=self.setButton_cb) self.setButton.pack(side='bottom') def setEntries(self): self.entryXTk.set(self.thumbx.labelFormat % self.vector[0]) self.entryYTk.set(self.thumby.labelFormat % self.vector[1]) self.entryZTk.set(self.thumbz.labelFormat % self.vector[2]) lf = '%.3f' self.entryVTk.set(lf%self.vector[0]+' '+lf%self.vector[1]+' '\ +lf%self.vector[2]) self.drawVector() def createCanvas(self, master, size=200): self.frame = tkinter.Frame(self.master, relief='sunken', borderwidth=5) if self.name is not None: self.title = tkinter.Label(self.frame, text=self.name) self.title.pack(side=self.labelSide) self.canvas = tkinter.Canvas(self.frame, width=size, height=size) # set the focus so that we get keyboard events, and add callbacks self.canvas.bind('<KeyPress>', self.modifierDown) self.canvas.bind("<KeyRelease>", self.modifierUp) xm = self.xm = ym = self.ym = self.r self.canvas.create_oval(0, 0, size, size) self.canvas.create_oval(xm - (xm / 4), 0, xm + (xm / 4), size, tags='verticalCircle') self.canvas.create_oval(0, ym - (ym / 4), size, ym + (ym / 4), tags='horizontalCircle') # apply viewing transformation to vector XaxisWithView = numpy.dot([(1., 0., 0., 1.)], self.viewingMat)[0] x1 = self.xm + int(XaxisWithView[0] * (self.xm)) y1 = self.ym + int(XaxisWithView[1] * (self.ym)) self.canvas.create_line(xm, ym, x1, y1, fill='red', tags='axis') XaxisWithView = numpy.dot([(0., 1., 0., 1.)], self.viewingMat)[0] x2 = self.xm + int(XaxisWithView[0] * (self.xm)) y2 = self.ym + int(XaxisWithView[1] * (self.ym)) self.canvas.create_line(xm, ym, x2, y2, fill='green', tags='axis') XaxisWithView = numpy.dot([(0., 0., 1., 1.)], self.viewingMat)[0] x3 = self.xm + int(XaxisWithView[0] * (self.xm)) y3 = self.ym + int(XaxisWithView[1] * (self.ym)) self.canvas.create_line(xm, ym, x3, y3, fill='blue', tags='axis') self.textId = self.canvas.create_text(0, size, anchor='sw', text="XY") # shadow line in X=0 plane self.shadowPX = self.canvas.create_polygon(0, 0, 0, 0, 0, 0, fill='red', tag='moving') self.shadowPY = self.canvas.create_polygon(0, 0, 0, 0, 0, 0, fill='green', tag='moving') self.shadowPZ = self.canvas.create_polygon(0, 0, 0, 0, 0, 0, fill='blue', tag='moving') self.shadowX = self.canvas.create_line(0, 0, 0, 0, fill='black', tag='moving') self.shadowY = self.canvas.create_line(0, 0, 0, 0, fill='black', tag='moving') self.shadowZ = self.canvas.create_line(0, 0, 0, 0, fill='black', tag='moving') self.lId1 = self.canvas.create_line(0, 0, 0, 0, fill='black', width=3, arrow='last') self.canvas.pack(side='top') self.frame.pack(expand=1, fill='x') self.xm = self.ym = self.r self.drawVector() def setVector(self, value): #setVector does not call a callback! v = value # compute normalized vector n = math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]) if n == 0.0: v = [0.0, 0.0, 1.0] else: v = [v[0] / n, v[1] / n, v[2] / n] self.vector = v self.setEntries() self.drawVector() ##################################################################### # the 'configure' methods: ##################################################################### def configure(self, **kw): for key, value in list(kw.items()): # the 'set parameter' callbacks if key == 'continuous': self.setContinuous(value) elif key == 'mode': self.setMode(value) elif key == 'precision': self.setPrecision(value) # the 'lock entries' callbacks elif key == 'lockContinuous': self.lockContinuousCB(value) elif key == 'lockMode': self.lockModeCB(value) elif key == 'lockPrecision': self.lockPrecisionCB(value) def setContinuous(self, cont): """ cont can be None, 0 or 1 """ if cont != 1: cont = None self.continuous = cont if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togCont']['widget'] if cont: w.setvalue('on') else: w.setvalue('off') def setMode(self, mode): if mode != 'XY' and mode != 'X' and mode != 'Y' and mode != 'Z': mode = 'XY' self.canvas.itemconfigure(self.textId, text=mode) self.mode = mode if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togAxes']['widget'] w.setvalue(mode) def setPrecision(self, val): val = int(val) if val > 10: val = 10 if val < 1: val = 1 self.thumbx.configure(precision=val) self.thumby.configure(precision=val) self.thumbz.configure(precision=val) self.entryXTk.set(self.thumbx.labelFormat % self.vector[0]) self.entryYTk.set(self.thumby.labelFormat % self.vector[1]) self.entryZTk.set(self.thumbz.labelFormat % self.vector[2]) if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['selPrec']['widget'] w.setvalue(val) if self.opPanel: self.opPanel.updateDisplay() ##################################################################### # the 'lock' methods: ##################################################################### def lockContinuousCB(self, mode): if mode != 0: mode = 1 self.lockContinuous = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockPrecisionCB(self, mode): if mode != 0: mode = 1 self.lockPrecision = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockModeCB(self, mode): if mode != 0: mode = 1 self.lockMode = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay()
class ContourSpectrum(Tkinter.Frame): """This class implements a ContourSpectrum widget. it is fully a copy/paste from Dial """ def __init__(self, master=None, type='float', labCfg={ 'fg': 'black', 'side': 'left', 'text': None }, min=None, max=None, showLabel=1, value=0.0, continuous=1, precision=2, callback=None, lockMin=0, lockBMin=0, lockMax=0, lockBMax=0, lockPrecision=0, lockShowLabel=0, lockValue=0, lockType=0, lockContinuous=0, signatures=None, **kw): Tkinter.Frame.__init__(self, master) Tkinter.Pack.config(self) self.callbacks = CallbackManager() # object to manage callback # functions. They get called with the # current value as an argument # initialize various attributes with default values self.height = 100 # widget height self.width = 256 # widget height self.widthMinusOne = self.width - 1 self.min = 0 # minimum value self.max = 1 # maximum value self.range = self.max - self.min self.precision = 2 # decimal places self.minOld = 0. # used to store old values self.maxOld = 0. self.size = 50 # defines widget size self.offsetValue = 0. # used to set increment correctly self.lab = None # label self.callback = None # user specified callback self.opPanel = None # option panel widget self.value = 0.0 # current value of widget self.oldValue = 0.0 # old value of widget self.showLabel = 1 # turn on to display label on self.continuous = 1 # set to 1 to call callbacks at # each value change, else gets called # on button release event self.labCfg = labCfg # Tkinter Label options self.labelFont = (ensureFontCase('helvetica'), 14, 'bold' ) # label font self.labelColor = 'yellow' # label color self.canvas = None # the canvas to create the widget in self.lockMin = lockMin # lock<X> vars are used in self.lock() self.lockMax = lockMax # to lock/unlock entries in optionpanel self.lockBMin = lockBMin self.lockBMax = lockBMax self.lockPrecision = 0 self.lockShowLabel = lockShowLabel self.lockValue = lockValue self.lockType = lockType self.lockContinuous = lockContinuous # configure with user-defined values self.setCallback(callback) self.setContinuous(continuous) self.setType(type) self.setPrecision(precision) self.setMin(min) self.setMax(max) self.setShowLabel(showLabel) self.setValue(value) self.setLabel(self.labCfg) if master is None: master = Tkinter.Toplevel() self.master = master # widget master self.createCanvas(master) Tkinter.Widget.bind(self.canvas, "<ButtonPress-1>", self.mouseDown) Tkinter.Widget.bind(self.canvas, "<B1-Motion>", self.mouseMove) Tkinter.Widget.bind(self.canvas, "<ButtonRelease-1>", self.mouseUp) # create cursor self.cursorTk = self.canvas.create_line(0, 0, 0, 0, tags=['cursor']) self.increment = 0.0 self.incrementOld = 0. self.lockIncrement = 0 self.lockBIncrement = 0 self.oneTurn = 360. self.lockOneTurn = 0 self.opPanel = OptionsPanel(master=self, title="Slider graph Options") self.signatures = None # Signature objects fro isocontouring lib self.sigData = [] # list of (x,y) values arrays for each signature self.maxFun = [] # max Y value in each signature self.minFun = [] # min Y value in each signature self.yratios = [] # normalization factors self.colors = ['red', 'green', 'blue', 'orange'] self.tkLines = [] # list of Tkids for lines if signatures: self.setSignatures(signatures) def setCallback(self, cb): """Set widget callback. Must be callable function. Callback is called every time the widget value is set/modified""" assert cb is None or callable(cb) or type(cb) is types.ListType,\ "Illegal callback: must be either None or callable, or list. Got %s"%cb if cb is None: return elif type(cb) is types.ListType: for func in cb: assert callable( func), "Illegal callback must be callable. Got %s" % func self.callbacks.AddCallback(func) else: self.callbacks.AddCallback(cb) self.callback = cb def toggleOptPanel(self, event=None): if self.opPanel.flag: self.opPanel.Dismiss_cb() else: if not hasattr(self.opPanel, 'optionsForm'): self.opPanel.displayPanel(create=1) else: self.opPanel.displayPanel(create=0) def mouseDown(self, event): # remember where the mouse went down #self.lastx = event.x #self.lasty = event.y self.setXcursor(event.x) def mouseUp(self, event): # call callbacks if not in continuous mode if not self.continuous: self.callbacks.CallCallbacks(self.opPanel.valInput.get()) if self.showLabel == 2: # no widget labels on mouse release self.canvas.itemconfigure(self.labelId2, text='') self.canvas.itemconfigure(self.labelId, text='') def mouseMove(self, event): # move the cursor self.setXcursor(event.x) #self.lastx = event.x def printLabel(self): if self.canvas is None: return self.canvas.itemconfigure(self.labelId2, text=self.labelFormat % self.value) #newVal) self.canvas.itemconfigure(self.labelId, text=self.labelFormat % self.value) #newVal) def drawCursor(self, x): if self.canvas: self.canvas.coords(self.cursorTk, x, 0, x, self.height) def get(self): return self.type(self.value) def setXcursor(self, x, update=1, force=0): """ x is a cursor position in pixel between 1 and self.width """ if x < 1: x = 1 if x > self.width: x = self.width self.drawCursor(x) # the mouse return position from 1 to self.width (x=0 is not drawn) # we need cursor position from 0 (so last x is self.width-1) x = x - 1 if self.range is not None: self.value = self.min + x * self.range / float(self.widthMinusOne) newVal = self.get() if self.continuous or force: if update and self.oldValue != newVal or force: self.oldValue = newVal self.callbacks.CallCallbacks(newVal) if self.showLabel == 2: self.printLabel() else: if self.showLabel == 2: self.printLabel() if self.showLabel == 1: self.printLabel() if self.opPanel: self.opPanel.valInput.set(self.labelFormat % newVal) def set(self, x, update=1, force=0): """ x is a value between self.min and self.max """ #print "set ContourSpectrum" if self.range is not None: xcursor = (x - self.min) * float(self.widthMinusOne) / self.range xcursor = xcursor + 1 self.drawCursor(xcursor) self.setXcursor(xcursor, update, force) def createCanvas(self, master): self.frame = Tkinter.Frame(self, borderwidth=3, relief='sunken') self.canvas = Tkinter.Canvas(self.frame, width=self.width, height=self.height) self.xm = 25 self.ym = 25 self.labelId2 = self.canvas.create_text(self.xm + 2, self.ym + 2, fill='black', justify='center', text='', font=self.labelFont) self.labelId = self.canvas.create_text(self.xm, self.ym, fill=self.labelColor, justify='center', text='', font=self.labelFont) # pack em up self.canvas.pack(side=Tkinter.TOP) self.frame.pack(expand=1, fill='x') self.toggleWidgetLabel(self.showLabel) def toggleWidgetLabel(self, val): if val == 0: # no widget labels self.showLabel = 0 self.canvas.itemconfigure(self.labelId2, text='') self.canvas.itemconfigure(self.labelId, text='') if val == 1: # show always widget labels self.showLabel = 1 self.printLabel() if val == 2: # show widget labels only when mouse moves self.showLabel = 2 self.canvas.itemconfigure(self.labelId2, text='') self.canvas.itemconfigure(self.labelId, text='') def setValue(self, val): #print "setValue" assert type(val) in [types.IntType, types.FloatType],\ "Illegal type for value: expected %s or %s, got %s"%( type(1), type(1.0), type(val) ) # setValue does NOT call a callback! if self.min is not None and val < self.min: val = self.min if self.max is not None and val > self.max: val = self.max self.value = self.type(val) self.offsetValue = self.value self.oldValue = self.value #print "setValue ContourSpectrum" if self.range is not None: xcursor = (val - self.min) * float(self.widthMinusOne) / self.range xcursor = xcursor + 1 self.drawCursor(xcursor) if self.showLabel == 1: self.printLabel() if self.opPanel: self.opPanel.valInput.set(self.labelFormat % self.value) def setLabel(self, labCfg): self.labCfg = labCfg text = labCfg.get('text', None) if text is None or text == '': return d = {} for k, w in self.labCfg.items(): if k == 'side': continue else: d[k] = w if not 'side' in self.labCfg.keys(): self.labCfg['side'] = 'left' if not self.lab: self.lab = Tkinter.Label(self, d) self.lab.pack(side=self.labCfg['side']) self.lab.bind("<Button-3>", self.toggleOptPanel) else: self.lab.configure(text) ##################################################################### # the 'configure' methods: ##################################################################### def configure(self, **kw): for key, value in kw.items(): # the 'set' parameter callbacks if key == 'labCfg': self.setLabel(value) elif key == 'type': self.setType(value) elif key == 'min': self.setMin(value) elif key == 'max': self.setMax(value) elif key == 'precision': self.setPrecision(value) elif key == 'showLabel': self.setShowLabel(value) elif key == 'continuous': self.setContinuous(value) # the 'lock' entries callbacks elif key == 'lockType': self.lockTypeCB(value) elif key == 'lockMin': self.lockMinCB(value) elif key == 'lockBMin': self.lockBMinCB(value) elif key == 'lockMax': self.lockMaxCB(value) elif key == 'lockBMax': self.lockBMaxCB(value) elif key == 'lockPrecision': self.lockPrecisionCB(value) elif key == 'lockShowLabel': self.lockShowLabelCB(value) elif key == 'lockValue': self.lockValueCB(value) elif key == 'lockContinuous': self.lockContinuousCB(value) def setType(self, Type): assert type(Type) in [types.StringType, types.TypeType],\ "Illegal type for datatype. Expected %s or %s, got %s"%( type('a'), type(type), type(Type) ) if type(Type) == type(""): # type str assert Type in ('int','float'),\ "Illegal type descriptor. Expected 'int' or 'float', got '%s'"%Type self.type = eval(Type) else: self.type = Type if self.type == int: self.labelFormat = "%d" self.int_value = self.value else: self.labelFormat = "%." + str(self.precision) + "f" if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togIntFloat']['widget'] if self.type == int: w.setvalue('int') elif self.type == 'float': w.setvalue('float') if self.opPanel: self.opPanel.updateDisplay() # and update the printed label if self.canvas and self.showLabel == 1: self.printLabel() def setMin(self, min): if min is not None: assert type(min) in [types.IntType, types.FloatType, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.int64, numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64, numpy.float, numpy.float32, numpy.float64],\ "Illegal type for minimum. Expected type %s or %s, got %s"%( type(0), type(0.0), type(min) ) if self.max and min > self.max: min = self.max self.min = self.type(min) if self.showLabel == 1: self.printLabel() if self.value < self.min: self.set(self.min) if hasattr(self.opPanel, 'optionsForm'): self.opPanel.minInput.set(self.labelFormat % self.min) self.opPanel.toggleMin.set(1) self.opPanel.min_entry.configure(state='normal', fg='gray0') self.minOld = self.min else: self.min = None if hasattr(self.opPanel, 'optionsForm'): self.opPanel.toggleMin.set(0) self.opPanel.min_entry.configure(state='disabled', fg='gray40') if self.min is not None and self.max is not None: self.range = float(self.max - self.min) else: self.range = None def setMax(self, max): if max is not None: assert type(max) in [types.IntType, types.FloatType, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.int64, numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64, numpy.float, numpy.float32, numpy.float64],\ "Illegal type for maximum. Expected type %s or %s, got %s"%( type(0), type(0.0), type(max) ) if self.min and max < self.min: max = self.min self.max = self.type(max) if self.showLabel == 1: self.printLabel() if self.value > self.max: self.set(self.max) if hasattr(self.opPanel, 'optionsForm'): self.opPanel.maxInput.set(self.labelFormat % self.max) self.opPanel.toggleMax.set(1) self.opPanel.max_entry.configure(state='normal', fg='gray0') self.maxOld = self.max else: self.max = None if hasattr(self.opPanel, 'optionsForm'): self.opPanel.toggleMax.set(0) self.opPanel.max_entry.configure(state='disabled', fg='gray40') if self.min is not None and self.max is not None: self.range = float(self.max - self.min) else: self.range = None def setPrecision(self, val): assert type(val) in [types.IntType, types.FloatType, numpy.int, numpy.float32],\ "Illegal type for precision. Expected type %s or %s, got %s"%( type(0), type(0.0), type(val) ) val = int(val) if val > 10: val = 10 if val < 1: val = 1 self.precision = val if self.type == float: self.labelFormat = "%." + str(self.precision) + "f" else: self.labelFormat = "%d" if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['selPrec']['widget'] w.setvalue(val) if self.opPanel: self.opPanel.updateDisplay() # and update the printed label if self.canvas and self.showLabel == 1: self.printLabel() def setContinuous(self, cont): """ cont can be None, 0 or 1 """ assert cont in [None, 0, 1],\ "Illegal value for continuous: expected None, 0 or 1, got %s"%cont if cont != 1: cont = None self.continuous = cont if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togCont']['widget'] if cont: w.setvalue('on') #i=1 else: w.setvalue('off') #i=0 if self.opPanel: self.opPanel.updateDisplay() def setShowLabel(self, val): """Show label can be 0, 1 or 2 0: no label 1: label is always shown 2: show label only when value changes""" assert val in [0,1,2],\ "Illegal value for showLabel. Expected 0, 1 or 2, got %s"%val if val != 0 and val != 1 and val != 2: print "Illegal value. Must be 0, 1 or 2" return self.showLabel = val self.toggleWidgetLabel(val) if hasattr(self.opPanel, 'optionsForm'): w = self.opPanel.idf.entryByName['togLabel']['widget'] if self.showLabel == 0: label = 'never' elif self.showLabel == 1: label = 'always' elif self.showLabel == 2: label = 'move' w.setvalue(label) if self.opPanel: self.opPanel.updateDisplay() ##################################################################### # the 'lock' methods: ##################################################################### def lockTypeCB(self, mode): if mode != 0: mode = 1 self.lockType = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockMinCB(self, mode): #min entry field if mode != 0: mode = 1 self.lockMin = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockBMinCB(self, mode): # min checkbutton if mode != 0: mode = 1 self.lockBMin = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockMaxCB(self, mode): # max entry field if mode != 0: mode = 1 self.lockMax = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockBMaxCB(self, mode): # max checkbutton if mode != 0: mode = 1 self.lockBMax = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockPrecisionCB(self, mode): if mode != 0: mode = 1 self.lockPrecision = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockShowLabelCB(self, mode): if mode != 0: mode = 1 self.lockShowLabel = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockValueCB(self, mode): if mode != 0: mode = 1 self.lockValue = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def lockContinuousCB(self, mode): if mode != 0: mode = 1 self.lockContinuous = mode if hasattr(self.opPanel, 'optionsForm'): self.opPanel.lockUnlockDisplay() def setSignatures(self, signatures): self.signatures = signatures self.sigData = [] # get the values self.maxFun = [] self.minFun = [] for s in self.signatures: x = Numeric.zeros((s.nval, ), 'f') s.getFx(x) self.minix = mini = min(x) if (isinstance(mini, Numeric.ArrayType)) and (mini.shape == ()): mini = mini[0] maxi = max(x) if (isinstance(maxi, Numeric.ArrayType)) and (maxi.shape == ()): maxi = maxi[0] self.rangex = range = maxi - mini if range != 0: x = (((x - mini) / range) * self.widthMinusOne).astype('i') y = Numeric.zeros((s.nval, ), 'f') s.getFy(y) self.sigData.append((x, y)) self.maxFun.append(max(y)) self.minFun.append(min(y)) self.setMin(mini) self.setMax(maxi) # iso value with hightest value in first function if len(self.sigData): ind = list(self.sigData[0][1]).index(max(self.sigData[0][1])) self.setXcursor(ind) else: self.setXcursor(0.0) self.drawSignatures() def drawSignatures(self): # compute normalization factors self.yratios = [] maxi = max(self.maxFun) for i in range(4): h = self.height - 1. if maxi != 0 and self.maxFun[i] != 0: self.yratios.append((h / maxi) * (maxi / self.maxFun[i])) else: self.yratios.append(0) for l in self.tkLines: self.canvas.delete(l) for i, f in enumerate(self.sigData): coords = [] for x, y in zip(f[0], f[1]): coords.append(x) coords.append(self.height - y * self.yratios[i]) self.tkLines.append( apply(self.canvas.create_line, coords, {'fill': self.colors[i]}))