All pastes #329474 Raw Edit

Unnamed

public python v1 · immutable
#329474 ·published 2007-01-26 19:56 UTC
rendered paste body
#    This is a component of AXIS, a front-end for emc#    Copyright 2007 Anders Wallin <anders.wallin@helsinki.fi>##    This program is free software; you can redistribute it and/or modify#    it under the terms of the GNU General Public License as published by#    the Free Software Foundation; either version 2 of the License, or#    (at your option) any later version.##    This program is distributed in the hope that it will be useful,#    but WITHOUT ANY WARRANTY; without even the implied warranty of#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the#    GNU General Public License for more details.##    You should have received a copy of the GNU General Public License#    along with this program; if not, write to the Free Software#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA""" A widget library for pyVCP         The layout and composition of a Python Virtual Control Panel is specified    with an XML file. The file must begin with <pyvcp>, and end with </pyvcp>    In the documentation for each widget, optional tags are shown bracketed:    [ <option>Something</option> ]    such a tag is not required for pyVCP to work, but may add functionality or    modify the behaviour of a widget.    Example XML file:    <pyvcp>        <led>            <size>40</size>            <halpin>"my-led"</halpin>        </led>    </pyvcp>    This will create a VCP with a single LED widget which indicates the value     of HAL pin compname.my-led """#TJP 2007 01 25 some checkbtns with IOpins will wnat to set when pressed, others will reset,#  so a param was added 'downVal' to specify what the down value meant,  allowing True or False#TJP 20070125 widgets work visually, now...#  clean up checkbutton btnType "momentary" real bad name, it's original checkbutton#  add a cb for external stimulus to checkbutton btnType 'toggle'#   an external signal must be able to reset the btn thru the halpin, the halpin is type IO##TJP 20070124 commented calls to tooltip, rt not closing well, errs when restarted #TJP 20070122   added tooltios yesterday, seemed ok, #TJP   now i see that there's a bug, the 1ts time i use it... ok, the 2nd time ... i get#         tomp@cncbox:~/emc2.1-alpha0$ bin/pyvcp -c fred jnk5.xml#         HAL: ERROR: duplicate component name 'fred'#         ULAPI: WARNING: module 'HAL_fred' failed to delete shmem 01#         Error: Multiple components with the same name.#  and i 'fix' by 'scripts/realtime stop; scripts/realtime start'#  i commented the tooltip code out & it's fine for multiple reps# why?#  the bug in the tooltip code, cuz if i use xml w/o tooltip, then mux reps are ok#  and err occurs when xml has tooltip, but tooltips not used, so bug in creation, not use of tooltip#  confirmed... just commented the creat_tooltip & is ok mux timesfrom Tkinter import *from hal import *import math# this makes only functions named here be included in the pydoc__all__=["pyvcp_label"]# FIXME: this is ugly, a list of valid widgets in this module should be # created somehow automagically# or another mechanism for vcpparse.py to know which elements are valid should# be constructedelements =["pyvcp","led","vbox","hbox","vbox" \            ,"button","scale","checkbutton","bar" \            ,"label","number","spinbox","radiobutton","jogwheel"\            ,"meter","dial"]# FIXME: this is ugly, each widget should know what parameters are valid# for itself, and vcpparse.py should check validity for each widget individually# TJP most parms listed at tkinter's site dont work in pyvcp#   and not becasue they're not in the list below, i tried addingthem, #   and then the name-value pirs appeared on the vcpars's w_command list, but still didnt work#   so, if it's crippled, i dont want extra bloat in here... #     i removed the 'should be' and left the 'live with it' elements##TJP 20070122 added tooltips parm ttiptxt#   can they be active ? like be the dro or the scale, something that changes?##TJP 20070123 added btnType token for Button widget#   if btnType = Mom, (momentary) then btn halpin is O(utput) (needs no info about halpin's prev state)#   if btnType = Togl (toggle) then halpin is IO(in and output) (btn needs to know pin state so btn can change it)#    seems like it should just ask the pin, not tell the pin !! the pin should be an object with it's own data, and methods#TJP added compass param for labelsparameters = ["size","text","orient","halpin","format" \            ,"font","min_","max_","resolution" \            ,"choices","cpr","fillcolor","bgcolor" \            ,"bd","relief","init", \            "ttiptxt", "btnType","compass"]from Tkinter import *import math# -------------------------------------------class pyvcp_dial(Canvas):    # Dial widget by tomp    """ A dial that outputs a HAL_FLOAT         reacts to both mouse-wheel and mouse dragging        <dial>            [ <size>376</size> ]            [ <cpr>100</cpr> ]    number of changes per rev, is # of dial tick marks, beware hi values)                        [ <min_>-33.123456</min_> ]            [ <max_>3.3</max_> ]            [ <text>"Gallons per Hour"</text> ]            (knob label)            [ <init>123</init> ]           (initial value a whole number must end in '.')            [ <resolution>.001</resolution> ]          (scale value a whole number must end in '.')            [ <halpin>"anaout"</halpin> ]            [ <ttiptxt>"dont touch that dial!"</ttiptxt> ]			(tooltip that occurs when cursor is over widget)        </dial>                key bindings:                    <Button-4>              untested no wheel mouse                    <Button-5>              untested no wheel mouse                    <Button1-Motion>      used internally during drag                    <ButtonPress>          used internally to record beginning of drag                    <ButtonRelease>          used internally at end of drag                    <Double-1> divides scale by 10                    <Double-2> resets scale to original value                    <Double-3> multiplies scale by 10                    <Shift-1>   shift-click resets original analog value                 features:                    text autoscales    """    # FIXME:    # -jogging should be enabled only when the circle has focus    #   TJP nocando:   only widgets have events, not thier 'items', the circle is an item    # -circle should maintain focus when mouse over dot    #   TJP nocando:   ditto, the circle is an item, so focus & event are not aligned to it    # -jogging by dragging with the mouse could work better    # -add a scaled output, scale changes when alt/ctrl/shift is held down    #   TJP dblLeftClick divides scale by 10 , dblRightClcik muxs by 10    n=0    #TJP TODO: let some artists look at it, butt ugly!    #TJP cpr is overloaded, now it means "chgs per rev" not "counts per rev"    #TJP the tik marks could get very fine, avoid high cpr to size ratios (easily seen)    #TJP awallin chgd text text2 text3 to text init resolution, better names :)     #TJP 20070122 add var for tooltip text    def __init__(self,root,pycomp,halpin=None,size=200,cpr=40, \            min_=None,max_=None, \            text=None,init=0,resolution=0.1, \            ttiptxt=None, \            **kw):        pad=10        self.out=init             # float output   out        self.origValue=init       # in case user wants to reset the pot/valve/thingy        Canvas.__init__(self,root,width=size,height=size)        self.circle=self.create_oval(pad,pad,size-pad,size-pad)        self.itemconfig(self.circle)        self.mid=size/2        self.r=(size-2*pad)/2        self.alfa=0        self.d_alfa=2*math.pi/cpr        self.size=size        self.funit=resolution        self.origFunit=self.funit        # allow restoration        self.mymin=min_        self.mymax=max_        self.dot = self.create_oval(self.dot_coords())        self.itemconfig(self.dot,fill="yellow",activefill="green")        #TJP items get rendered in order of creation, so the knob will be behind these texts        #TJP the font can be described with pixel size by using negative value        self.txtroom=size/6        # a title, if the user has supplied one        if text!=None:            self.title=self.create_text([self.mid,self.mid-self.txtroom],                        text=text,font=('Arial',-self.txtroom))        # the output        self.dro=self.create_text([self.mid,self.mid],                        text=str(self.out),font=('Arial',-self.txtroom))        # the scale        self.delta=self.create_text([self.mid,self.mid+self.txtroom],                         text=str(self.funit),font=('Arial',-self.txtroom))        self.bind('<Button-4>',self.wheel_up)            # untested no wheel mouse        self.bind('<Button-5>',self.wheel_down)          # untested no wheel mouse        self.bind('<Button1-Motion>',self.motion)        #during drag        self.bind('<ButtonPress>',self.bdown)                #begin of drag        self.bind('<ButtonRelease>',self.bup)                #end of drag         self.bind('<Double-1>',self.chgScaleDn)            # doubleclick scales down        self.bind('<Double-2>',self.resetScale)         # doubleclick resets scale        self.bind('<Double-3>',self.chgScaleUp)         # doubleclick scales up        self.bind('<Shift-1>',self.resetValue)          # shift resets value        self.draw_ticks(cpr)        self.dragstartx=0        self.dragstarty=0        self.dragstart=0        # create the hal pin        if halpin == None:            halpin = "dial."+str(pyvcp_dial.n)+".out"        pyvcp_dial.n += 1        pycomp.newpin(halpin, HAL_FLOAT, HAL_OUT)        self.halpin=halpin        #TJP 20070122  tooltip if the user passes a ttiptext        #TJP if i want to chg the text, i need a handle...  self.ttip=createToolTip(self,ttiptxt)                #TJP 20070124 struck till i find out cause of odd rt errs, not fully shutdown after scripts/realtime stop        #self.ttt=ttiptxt        #if self.ttt != None:        #  #self.ttip=createToolTip(self, self.ttt)        #  print "no ttip 2dA"                self.pycomp=pycomp    def chgScaleDn(self,event):        # reduces the scale by 10x        self.funit=self.funit/10.0        self.update_scale()    def chgScaleUp(self,event):        # increases the scale by 10x        self.funit=self.funit*10.0        self.update_scale()    def resetScale(self,event):        # reset scale to original value        self.funit=self.origFunit        self.update_scale()    def resetValue(self,event):        # reset output to orifinal value        self.out=self.origValue        self.update_dro()    def dot_coords(self):        # calculate the coordinates for the dot        DOTR=0.08*self.size        DOTPOS=0.75        midx = self.mid+DOTPOS*self.r*math.cos(self.alfa)        midy = self.mid+DOTPOS*self.r*math.sin(self.alfa)        return midx-DOTR, midy-DOTR,midx+DOTR,midy+DOTR    def bdown(self,event):        self.dragstartx=event.x        self.dragstarty=event.y        self.dragstart=math.atan2((event.y-self.mid),(event.x-self.mid))        self.itemconfig(self.dot,fill="green",activefill="green")    def bup(self,event):        self.itemconfig(self.dot,fill="yellow")    def motion(self,event):        dragstop = math.atan2((event.y-self.mid),(event.x-self.mid))        delta = dragstop - self.dragstart        if delta>=self.d_alfa:            self.up()            self.dragstart=math.atan2((event.y-self.mid),(event.x-self.mid))        elif delta<=-self.d_alfa:            self.down()            self.dragstart=math.atan2((event.y-self.mid),(event.x-self.mid))        self.itemconfig(self.dot,fill="green",activefill="green")    def wheel_up(self,event):        self.up()    def wheel_down(self,event):        self.down()    def down(self):        self.alfa-=self.d_alfa        self.out-=self.funit        #TJP clip down side        if self.mymin != None:            if self.out<self.mymin:                self.out=self.mymin        self.update_dot()        self.update_dro()    def up(self):        self.alfa+=self.d_alfa        self.out+=self.funit        #TJP clip up side        if self.mymax != None:            if self.out>self.mymax:                self.out=self.mymax        self.update_dot()        self.update_dro()    def update_dot(self):        self.coords(self.dot, self.dot_coords() )    def update_dro(self):        valtext = str(self.out)        self.itemconfig(self.dro,text=valtext)    def update_scale(self):        valtext = str(self.funit)        valtext = 'x ' + valtext        self.itemconfig(self.delta,text=valtext)        ##TJP 20070122 BEGIN scratch code used in effort to get changable tootips, doesnt work yet 20070122        #TJP 20070124 no ref, no calls to tooltip till rt errs fixed        #self.ttip.text=valtext        #self.itemconfig(self.ttip, text=valtext)        ##works, is right value , doesnt affect tooltip tho       print "<>", self.ttip.text, "<>"        ##works, is hex jibberish, of no use                      print ">",self.ttip,"<"        ##TJP 20070122 END   scratch code used in effort to get changable tootips, doesnt work yet 20070122    def draw_ticks(self,cpr):        for n in range(0,cpr):            startx=self.mid+self.r*math.cos(n*self.d_alfa)            starty=self.mid+self.r*math.sin(n*self.d_alfa)            stopx=self.mid+1.15*self.r*math.cos(n*self.d_alfa)            stopy=self.mid+1.15*self.r*math.sin(n*self.d_alfa)            self.create_line([startx,starty,stopx,stopy])    def update(self,pycomp):        self.pycomp[self.halpin] = self.out# -------------------------------------------class pyvcp_meter(Canvas):    """ Meter - shows the value of a FLOAT with an analog meter        <meter>            [ <size>300</size> ]            [ <halpin>"mymeter"</halpin> ]            [ <text>"My Voltage"</text> ]            [ <min_>-22</min_> ]            [ <max_>123</max_> ]        </meter>    """    # FIXME: logarithmic scale option    n=0    def __init__(self,root,pycomp,halpin=None,                        size=200,text=None,min_=0,max_=100,**kw):        pad=10        Canvas.__init__(self,root,width=size,height=size)        self.halpin=halpin        self.min_=min_        self.max_=max_        range_=2.5        self.min_alfa=-math.pi/2-range_        self.max_alfa=-math.pi/2+range_        self.circle=self.create_oval(pad,pad,size-pad,size-pad)        self.itemconfig(self.circle,fill="white")        self.mid=size/2        self.r=(size-2*pad)/2        self.alfa=0        self.line = self.create_line([self.mid,self.mid, \                            self.mid+0.8*self.r*math.cos(self.alfa), \                            self.mid+0.8*self.r*math.sin(self.alfa)],fill="red")        self.itemconfig(self.line,width=3)        if text!=None:            t=self.create_text([self.mid,self.mid-20])            self.itemconfig(t,text=text)            self.itemconfig(t,font=('Arial',20))        self.draw_ticks()        # create the hal pin        if halpin == None:            self.halpin = "meter."+str(pyvcp_meter.n)+".value"        pyvcp_meter.n += 1        pycomp.newpin(self.halpin, HAL_FLOAT, HAL_IN)        self.value = pycomp[self.halpin]    def update(self,pycomp):        self.value = pycomp[self.halpin]        scale=(self.max_-self.min_)/(self.max_alfa-self.min_alfa)        self.alfa=self.min_alfa + (self.value-self.min_)/scale        if self.alfa > self.max_alfa:            self.alfa = self.max_alfa        elif self.alfa < self.min_alfa:            self.alfa = self.min_alfa        self.coords(self.line, self.mid,self.mid, \                            self.mid+0.8*self.r*math.cos(self.alfa), \                            self.mid+0.8*self.r*math.sin(self.alfa))       def draw_ticks(self):        d_alfa = float((self.max_alfa-self.min_alfa))/10        d_value = float((self.max_-self.min_))/10        for n in range(0,11):            startx=self.mid+self.r*math.cos(self.min_alfa + n*d_alfa)            starty=self.mid+self.r*math.sin(self.min_alfa + n*d_alfa)            stopx=self.mid+0.85*self.r*math.cos(self.min_alfa + n*d_alfa)            stopy=self.mid+0.85*self.r*math.sin(self.min_alfa + n*d_alfa)            textx=stopx - 0.1*self.r*math.cos(self.min_alfa + n*d_alfa)            texty=stopy - 0.1*self.r*math.sin(self.min_alfa + n*d_alfa)            self.create_line([startx,starty,stopx,stopy])            t=self.create_text([textx,texty])            self.itemconfig(t,text=str(self.min_+d_value*n))# -------------------------------------------class pyvcp_jogwheel(Canvas):    """" A jogwheel that outputs a HAL_FLOAT count        reacts to both mouse-wheel and mouse dragging        <jogwheel>            [ <cpr>33</cpr> ]                       (counts per revolution)            [ <halpin>"myjogwheel"</halpin> ]            [ <size>300</size> ]        </jogwheel>    """    # FIXME:    # -jogging should be enabled only when the circle has focus    # -circle should maintain focus when mouse over dot    # -jogging by dragging with the mouse could work better    # -add a scaled output, scale changes when alt/ctrl/shift is held down    n=0    def __init__(self,root,pycomp,halpin=None,size=200,cpr=40,**kw):        pad=10        self.count=0        Canvas.__init__(self,root,width=size,height=size)        self.circle=self.create_oval(pad,pad,size-pad,size-pad)        self.itemconfig(self.circle,fill="lightgrey",activefill="darkgrey")        self.mid=size/2        self.r=(size-2*pad)/2        self.alfa=0        self.d_alfa=2*math.pi/cpr        self.size=size                        self.dot = self.create_oval(self.dot_coords())        self.itemconfig(self.dot,fill="black")        #self.itemconfig(self.line,arrow="last")        #self.itemconfig(self.line,width=3)        self.bind('<Button-4>',self.wheel_up)        self.bind('<Button-5>',self.wheel_down)        self.bind('<Button1-Motion>',self.motion)        self.bind('<ButtonPress>',self.bdown)        self.draw_ticks(cpr)        self.dragstartx=0        self.dragstarty=0        self.dragstart=0        # create the hal pin        if halpin == None:            halpin = "jogwheel."+str(pyvcp_jogwheel.n)+".count"        pyvcp_jogwheel.n += 1        pycomp.newpin(halpin, HAL_FLOAT, HAL_OUT)        self.halpin=halpin        pycomp[self.halpin] = self.count        self.pycomp=pycomp    def dot_coords(self):        DOTR=0.08*self.size        DOTPOS=0.75        midx = self.mid+DOTPOS*self.r*math.cos(self.alfa)        midy = self.mid+DOTPOS*self.r*math.sin(self.alfa)        return midx-DOTR, midy-DOTR,midx+DOTR,midy+DOTR        def bdown(self,event):        self.dragstartx=event.x        self.dragstarty=event.y        self.dragstart=math.atan2((event.y-self.mid),(event.x-self.mid))    def motion(self,event):        dragstop = math.atan2((event.y-self.mid),(event.x-self.mid))        delta = dragstop - self.dragstart        if delta>=self.d_alfa:            self.up()            self.dragstart=math.atan2((event.y-self.mid),(event.x-self.mid))        elif delta<=-self.d_alfa:            self.down()            self.dragstart=math.atan2((event.y-self.mid),(event.x-self.mid))        def wheel_up(self,event):        self.up()            def wheel_down(self,event):        self.down()    def down(self):        self.alfa-=self.d_alfa        self.count-=1        self.pycomp[self.halpin] = self.count        self.update_dot()               def up(self):        self.alfa+=self.d_alfa        self.count+=1        self.pycomp[self.halpin] = self.count        self.update_dot()      def update_dot(self):        self.coords(self.dot, self.dot_coords() )          def draw_ticks(self,cpr):        for n in range(0,cpr):            startx=self.mid+self.r*math.cos(n*self.d_alfa)            starty=self.mid+self.r*math.sin(n*self.d_alfa)            stopx=self.mid+1.15*self.r*math.cos(n*self.d_alfa)            stopy=self.mid+1.15*self.r*math.sin(n*self.d_alfa)            self.create_line([startx,starty,stopx,stopy])    def update(self,pycomp):        # this is stupid, but required for updating pin        # when first connected to a signal        self.pycomp[self.halpin] = self.count        # -------------------------------------------class pyvcp_radiobutton(Frame):    n=0    def __init__(self,master,pycomp,halpin=None,choices=[],**kw):        f=Frame.__init__(self,master,bd=2,relief=GROOVE)        self.v = IntVar()        self.v.set(1)        self.choices=choices        if halpin == None:            halpin = "radiobutton."+str(pyvcp_radiobutton.n)        pyvcp_radiobutton.n += 1                self.halpins=[]        n=0        for c in choices:            b=Radiobutton(self,f, text=str(c)                        ,variable=self.v, value=pow(2,n))            b.pack()            if n==0:                b.select()            c_halpin=halpin+"."+str(c)            pycomp.newpin(c_halpin, HAL_BIT, HAL_OUT)            self.halpins.append(c_halpin)            n+=1    # FIXME    # this is a fairly stupid way of updating the pins    # since the calculation is done every 100ms wether a change    # has happened or not. see below.       def update(self,pycomp):        index=math.log(self.v.get(),2)        index=int(index)        for pin in self.halpins:            pycomp[pin]=0;        pycomp[self.halpins[index]]=1;    # FIXME    # this would be a much better way of updating the    # pins, but at the moment I can't get it to work    # this is never called even if I set command=self.update()    # in the call to Radiobutton above    def changed(self):        index=math.log(self.v.get(),2)        index=int(index)        print "active:",self.halpins[index]# -------------------------------------------class pyvcp_label(Label):    """ Static text label         <label>            <text>"My Label:"</text>            <compass>"ne"</compass>       [ where text goes into label ]        </label>    """    #TJP default location is center    def __init__(self,master,pycomp,compass=None,**kw):        if compass==None:          ankr=CENTER        else:          #print "compass dir passed ",compass          ankr=compass        Label.__init__(self,master,**kw)        #TJP you have to fill the space for anchors like center to work         #   else what appears to be 'left' is really centered in a tiny box smooshed up to left wall !        self.pack(fill=X, expand=1, anchor=ankr)            def update(self,pycomp):        pass# -------------------------------------------class pyvcp_vbox(Frame):    """ Box in which widgets are packed vertically        <vbox>            <relief>GROOVE</relief>         (FLAT, SUNKEN, RAISED, GROOVE, RIDGE)            <bd>3</bd>                      (border width)            place widgets here        </vbox>    """    def __init__(self,master,pycomp,bd=0,relief=FLAT):        Frame.__init__(self,master,bd=bd,relief=relief)        self.pack(expand=1,fill=BOTH)    def update(self,pycomp):         pass    def packtype(self):        return "top"# -------------------------------------------class pyvcp_hbox(Frame):    """ Box in which widgets are packed horizontally        <vbox>            <relief>GROOVE</relief>         (FLAT, SUNKEN, RAISED, GROOVE, RIDGE)            <bd>3</bd>                      (border width)            place widgets here        </vbox>            """    def __init__(self,master,pycomp,bd=0,relief=FLAT):        Frame.__init__(self,master,bd=bd,relief=relief)        self.pack(expand=1,fill=BOTH)    def update(self,pycomp):         pass    def packtype(self):        return "left"# -------------------------------------------class pyvcp_spinbox(Spinbox):    """ (control) controls a float, also shown as text         reacts to the mouse wheel         <spinbox>            [ <halpin>"my-spinbox"</halpin> ]            [ <min_>55</min_> ]   sets the minimum value to 55            [ <max_>123</max_> ]  sets the maximum value to 123        </spinbox>    """    # FIXME: scale resolution when shift/ctrl/alt is held down?    #TJP spinbox has a default width of 20 chars.... a bit much for 2^32, try 12 ( i allow 5.4f format    n=0    def __init__(self,master,pycomp,halpin=None,                    min_=0,max_=100,resolution=1,format="2.1f",**kw):        self.v = DoubleVar()        if 'increment' not in kw: kw['increment'] = resolution        if 'from' not in kw: kw['from'] = min_        if 'to' not in kw: kw['to'] = max_        if 'format' not in kw: kw['format'] = "%" + format        kw['command'] = self.command        Spinbox.__init__(self,master,textvariable=self.v,width=12,borderwidth=5,**kw)        if halpin == None:            halpin = "spinbox."+str(pyvcp_spinbox.n)        pyvcp_spinbox.n += 1        self.halpin=halpin        self.value=min_        self.oldvalue=min_        self.format = "%(b)"+format        self.max_=max_        self.min_=min_        self.resolution=resolution        self.v.set( str( self.format  % {'b':self.value} ) )        pycomp.newpin(halpin, HAL_FLOAT, HAL_OUT)        self.bind('<Button-4>',self.wheel_up)        self.bind('<Button-5>',self.wheel_down)    def command(self):        self.value = self.v.get()    def update(self,pycomp):          pycomp[self.halpin] = self.value         if self.value != self.oldvalue:            self.v.set( str( self.format  % {'b':self.value} ) )             self.oldvalue=self.value              def wheel_up(self,event):        self.value += self.resolution        if self.value > self.max_:            self.value = self.max_                   def wheel_down(self,event):        self.value -= self.resolution        if self.value < self.min_:            self.value = self.min_          # -------------------------------------------class pyvcp_number(Label):    """ (indicator) shows a float as text """    n=0    def __init__(self,master,pycomp,halpin=None,format="2.1f",**kw):        self.v = StringVar()        self.format=format        Label.__init__(self,master,textvariable=self.v,padx=4,pady=4,**kw)        if halpin == None:            halpin = "number."+str(pyvcp_number.n)        pyvcp_number.n += 1        self.halpin=halpin        self.value=0.0        dummy = "%(b)"+self.format        self.v.set( str( dummy  % {'b':self.value} ) )        pycomp.newpin(halpin, HAL_FLOAT, HAL_IN)    def update(self,pycomp):            newvalue = pycomp[self.halpin]        if newvalue != self.value:            self.value=newvalue            dummy = "%(b)"+self.format            self.v.set( str( dummy  % {'b':newvalue} ) )  # -------------------------------------------class pyvcp_bar(Canvas):    """ (indicator) a bar-indicator for a float"""    n=0    # FIXME logarithmic scale?    # FIXME specifying negative startval or endval does not work    def __init__(self,master,pycomp,              fillcolor="green",bgcolor="grey",               halpin=None,min_=0.0,max_=100.0,**kw):            self.cw=200    # canvas width        self.ch=50     # canvas height        self.bh=30     # bar height        self.bw=150    # bar width        self.pad=((self.cw-self.bw)/2)        Canvas.__init__(self,master,width=self.cw,height=self.ch)        if halpin == None:            halpin = "bar."+str(pyvcp_bar.n)        pyvcp_bar.n += 1        self.halpin=halpin        self.endval=max_        self.startval=min_        pycomp.newpin(halpin, HAL_FLOAT, HAL_IN)        # the border        border=self.create_rectangle(self.pad,1,self.pad+self.bw,self.bh)        self.itemconfig(border,fill=bgcolor)        # the bar        self.bar=self.create_rectangle(self.pad+1,2,self.pad+1,self.bh-1)        self.itemconfig(self.bar,fill=fillcolor)        self.value=0.0 # some dummy value to start with                       # start text        start_text=self.create_text(self.pad,self.bh+10,text=str(self.startval) )        #end text        end_text=self.create_text(self.pad+self.bw,self.bh+10,text=str(self.endval) )        # value text        self.val_text=self.create_text(self.pad+self.bw/2,                                   self.bh/2,text=str(self.value) )              def update(self,pycomp):        # update value        newvalue=pycomp[self.halpin]        if newvalue != self.value:            self.value = newvalue            percent = self.value/(self.endval-self.startval)            if percent < 0.0:                percent = 0            elif percent > 1.0:                percent = 1.0              # set value text            valtext = str( "%(b)3.1f" % {'b':self.value} )            self.itemconfig(self.val_text,text=valtext)            # set bar size            self.coords(self.bar, self.pad+1, 2,                         self.pad+self.bw*percent, self.bh-1)# -------------------------------------------class pyvcp_led(Canvas):    """ (indicator) a LED         color is on_color when halpin is 1, off_color when halpin is 0         <led>          <halpin>            "MyOptionalLedName"   (opt, default  is 'led' )          </halpin>          <on_color>            "barney-purple"       (opt, default green)          </on_color>          <off_color>            "puce"                (opt, default red)          </off_color>          <size>            30                    (opt, default is 20 pxls)          </size>        </led>    """    n=0    def __init__(self,master,pycomp, halpin=None,                          off_color="red",on_color="green",size=20,**kw):        Canvas.__init__(self,master,width=size,height=size,bd=0)        self.off_color=off_color        self.on_color=on_color        self.oh=self.create_oval(1,1,size,size)        self.state=0        self.itemconfig(self.oh,fill=off_color)        if halpin == None:            halpin = "led."+str(pyvcp_led.n)        self.halpin=halpin        pycomp.newpin(halpin, HAL_BIT, HAL_IN)        pyvcp_led.n+=1    def update(self,pycomp):        newstate = pycomp[self.halpin]        if newstate != self.state:            if newstate == 1:                self.itemconfig(self.oh,fill=self.on_color)                self.state=1            else:                self.itemconfig(self.oh,fill=self.off_color)                 self.state=0# -------------------------------------------class pyvcp_checkbutton(Checkbutton):    """ (control) a checkbutton         halpin is 1 when checkbutton-momentary pressed, 0 otherwise            halpin is O (the btn sees it as write only)        halpin changes state when checkbutton-toggle pressed, unchanged otherwise            halpin is IO (the btn can read it and write it  [set it the opp state] )        20070123          added parameter 'btnType' to allowed token list         allow values "Opin" & "IOpin"        <checkbutton>          <halpin>"mybtn"</halpin>          <btnType>"O"</btnType>       halpin is ouput only         </checkbutton>        <checkbutton>          <halpin>"mybtn"</halpin>     optional  default is created from "checkbutton" & a counter          <btnType>"IO"</btnType>      optional  default it O           <text>"Flood"</init>         optional  default is from a counter          <init>True</init>            optional  default is True        </checkbutton>     """    #TJP 20070125   only chg the halpin when pressed, not when release, so: dont cnx halpin to self.v!!!    n=0    def __init__(self,master,pycomp,halpin=None,btnType=None,text=None,init=None, **kw):        if btnType==None:          btnType="Opin"        if ((btnType!="Opin")&(btnType!="IOpin")):          return        if init==None:          init=True        if ((init!=True)&(init!=False)):          return        if text==None:          btntxt=str(pyvcp_checkbutton.n)        else:          btntxt = text        self.v = BooleanVar(master)       #for original 'Opin' type checkbutton        self.downVal=init                 # the logic value to emit when pressed        self.btntype=btnType              # "Opin" or "IOpin"        #i dont see why this is necc, else update gives        #       AttributeError: pyvcp_checkbutton instance has no attribute 'newstate'        self.newstate=self.oldstate=None        if btnType=="Opin":          Checkbutton.__init__(self,master, \              variable=self.v, \              text=None, indicatoron=0, width=14, \              onvalue=1, offvalue=0, \              **kw)        else:          Checkbutton.__init__(self,master, \              text=None, indicatoron=0, width=14, \              **kw)        if halpin == None:          halpin = "checkbutton."+str(pyvcp_checkbutton.n)        self.halpin=halpin        if btnType=="Opin":          pycomp.newpin(halpin, HAL_BIT, HAL_OUT)          self.configure(text=btntxt)        else:            pycomp.newpin(halpin, HAL_BIT, HAL_IO)            self.configure(text=btntxt)            #show the color according to <init>, set the pin & history to opposite of presed state ( becuz not pressed yet )            if self.downVal==True:              colr="red"              self.configure(activebackground=colr)              self.configure(bg=colr)              pycomp[self.halpin]=self.oldstate=self.newstate=False       # if init was True, then begin UP, Red, and oldstate = False            else:              colr="green"              self.configure(activebackground=colr)              self.configure(selectcolor=colr)              self.configure(bg=colr)              pycomp[self.halpin]=self.oldstate=self.newstate=True        # if init was False, then begin UP, Green & oldstate = True            self.bind('<ButtonPress>',self.bdown)               # only bind the IOpin vrsn of cbtn   chg output state when pressed        pyvcp_checkbutton.n += 1        self.mirror=None    # this func requests a change in state    def bdown(self,event):      if self.btntype=="IOpin":        if self.downVal==True:          self.newstate=True        else:          self.newstate=False        self.configure(state=DISABLED)    #def bup(self)    #   there is no bup, only external srcs can release the btn, not this widget    def update(self,pycomp):      if self.btntype=="Opin":        pycomp[self.halpin]=self.v.get()      else:                         # btnType = "IO"        dirty = False                                 # no need to change appearance till we know theres been a change        if self.newstate!=self.oldstate:          # detect a change caused by btn press ( an internal change )          pycomp[self.halpin]=self.newstate          dirty=True                                  # need to chg appearance          self.oldstate=self.newstate        elif pycomp[self.halpin]!=self.oldstate:  # detect a change caused by by external src          dirty=True                                  # need to chg appearance          self.oldstate=self.newstate=pycomp[self.halpin]        if dirty==True:          tmp=pycomp[self.halpin]          if self.downVal==True:          #    downVal is True              if tmp==True:               #       halpin is True                self.select()             #             btn looks down    looks decided by downVal & halpin                colr="green"              #             colr is green     color decided by halpin              else:                       #       halpin is False                self.deselect()           #             btn looks up                colr="red"                #             colr is red          else:                           #    downVal is False              if tmp==True:               #       halpin is True                self.deselect()           #             btn looks up                colr="green"              #             colr is green              else:                       #       halpin is False                self.select()             #             btn looks down                colr="red"                #             colr is red          self.configure(selectcolor=colr)          self.configure(activebackground=colr)          self.configure(state=NORMAL)          dirty=False# -------------------------------------------class pyvcp_button(Button):    """ (control) a button         halpin is 1 when button pressed, 0 otherwise     """    n=0    def __init__(self,master,pycomp,halpin=None,**kw):        Button.__init__(self,master,**kw)        if halpin == None:            halpin = "button."+str(pyvcp_button.n)        self.halpin=halpin         pycomp.newpin(halpin, HAL_BIT, HAL_OUT)        self.state=0;        self.bind("<ButtonPress>", self.pressed)        self.bind("<ButtonRelease>", self.released)         self.configure(height=1)        pyvcp_button.n += 1        def pressed(self,event):        # "the button was pressed"        self.state=1         def released(self,event):        # the button was released        self.state=0    def update(self,pycomp):        pycomp[self.halpin]=self.state# -------------------------------------------class pyvcp_scale(Scale):    """ (control) a slider         halpin-i is integer output         halpin-f is float output     """    # FIXME scale resolution when ctrl/alt/shift is held down?    # FIXME allow user to specify size    n=0    def __init__(self,master,pycomp,                    resolution=1,halpin=None,min_=0,max_=10,**kw):        self.resolution=resolution        Scale.__init__(self,master,resolution=self.resolution,                         from_=min_,to=max_,**kw)        if halpin == None:            halpin = "scale."+str(pyvcp_scale.n)        self.halpin=halpin                pycomp.newpin(halpin+"-i", HAL_S32, HAL_OUT)        pycomp.newpin(halpin+"-f", HAL_FLOAT, HAL_OUT)        self.bind('<Button-4>',self.wheel_up)        self.bind('<Button-5>',self.wheel_down)        pyvcp_scale.n += 1    def update(self,pycomp):        pycomp[self.halpin+"-f"]=self.get()        pycomp[self.halpin+"-i"]=int(self.get())    def wheel_up(self,event):        self.set(self.get()+self.resolution)    def wheel_down(self,event):        self.set(self.get()-self.resolution)#----------------------------------------begin tooltips -------------------------------------#TJP 20070122 tooltip adopted from code in IDLE   see CallTipWindow.pyclass ToolTip(object):    def __init__(self, somewdgt):        self.somewdgt = somewdgt        self.tipwindow = None        self.id = None        self.x = self.y = 0    def showtip(self, text):        "Display text in tooltip window"        self.txt = text        if self.tipwindow or not self.txt:            return        #orig x, y, cx, cy = self.somewdgt.bbox("insert")        #orig x = x + self.somewdgt.winfo_rootx() + 27        #orig y = y + cy + self.somewdgt.winfo_rooty() +27        ##TJP dont bother, alwys rtns 4 0's    mytupl=self.somewdgt.bbox(self.text)        #orig  x = x + self.somewdgt.winfo_rootx() + 27        #TJP the posn is set to the widget's topleft corner , but right by 5 and down by 5 pxls        x = self.somewdgt.winfo_rootx() + 5        #orig  y = y + cy + self.somewdgt.winfo_rooty() +27        y = self.somewdgt.winfo_rooty() + 5        #TJP the tooltip is a TopLevel type window ( on the top level, so its visible ... a hidden tooltip is useless :)        self.tipwindow = tw = Toplevel(self.somewdgt)        tw.wm_overrideredirect(1)        tw.wm_geometry("+%d+%d" % (x, y))        #TJP chg from    label=Label(...   to    self.lbl=Label(...        self.lbl = Label(tw, text=self.txt, justify=LEFT,                      background="#ffffe0", relief=SOLID, borderwidth=1,                      font=("arial", "8", "normal"))        self.lbl.pack(ipadx=1)        #the rendering doesnt really happen till this func is exited,        #   eg:   a sleep inside here delays the window&label from appearing        #         >not< a delay before hidingitself        #time.sleep(1)        #print "hello"    def hidetip(self):        tw = self.tipwindow        self.tipwindow = None        if tw:            tw.destroy()    #TJP will the std vpcparse update handle this?	no, vcparse.updater only updates a comp, so maybe use a comp to store the data?    #works, called by every widget created with a tool tip    #def dummy(self):    #    print "derfhello"#TJP 20070122 this indentation and sequence is neccesarydef createToolTip(somewdgt, text):    #somewdgt is the widget the tooltip belongs to    #toolTip is the tooltip object created by nxt line... a toolTip is NOT a widget    toolTip = ToolTip(somewdgt)    toolTip.text=text    #call funcs internal to tooltip like this...  toolTip.dummy()    #somewdgt.after is allowed, but is after widget created, not aftet ttip seen    def enter(event):        toolTip.showtip(text)    def leave(event):        toolTip.hidetip()    somewdgt.bind('<Enter>', enter)    somewdgt.bind('<Leave>', leave)    return toolTip#----------------------------------------end tooltips-----------------------------------------if __name__ == '__main__':    print "You can't run pyvcp_widgets.py by itself..."