Right way to Use Events in a Class

Hi All,

I am trying to control some equipment from panel. However with my “simple version” I get an error when trying to update the code. The issue is that I am used to using “self” to refer to other functions defined in the class. However the events functionality seems not to work that way at least naively.


from qcodes.instrument_drivers.stanford_research.SG384 import SRS_SG384 as Instr

 class mChild( type(Instr),type(param.Parameterized)):
     pass
    
 class Instr_Panel(Instr,param.Parameterized,metaclass = mChild):

    def __init__(self, name, address, reset=False, **kwargs):
        print('something')
        '''Inherit  the SRS class parameters'''
        super().__init__(name, address, **kwargs)
    
    
    def update_frequency(self,event):
        '''Convert the Text to a float'''
        freq = float(event.new)
        '''Call the frequency .set() function'''
        self.frequency.set(freq)
    
    freq_panel = pn.widgets.TextInput(name='SRS Frequency', value='5000000')
    freq_panel.param.watch(update_frequency, 'value')
    
    col = pn.Column(freq_panel)
    col.servable()

SRS =  SRS_SG384_Panel(name='SRS384', address='TCPIP::192.168.1.121',timeout=.01)

When running (and updating the GUI) this I get the error:
TypeError: update_frequency() missing 1 required positional argument: ‘event’


Ultimately it would be nice to write a wrapper between panel’s parameter class and the qcodes parameters class https://qcodes.github.io/Qcodes/examples/Parameters/Parameters.html.

However, this requires modifying the panel parameters class and I’m not sure I understand the syntax of panel just yet…

So I’m a little confused by the way you’re using classes here so this doesn’t appear to be a Panel issue.

Here:

    freq_panel = pn.widgets.TextInput(name='SRS Frequency', value='5000000')

You’re creating class attribute in the form of a TextInput widget which means that all instances of this class will now share this object. This also explains why the callback you are defining doesn’t work:

    freq_panel.param.watch(update_frequency, 'value')

This is assigning the unbound method as a callback but it’s being used as an instance method, so Python does not provide the self. The way I would structure this is either as:

 class Instr_Panel(Instr,param.Parameterized,metaclass = mChild):

    def __init__(self, name, address, reset=False, **kwargs):
        print('something')
        '''Inherit  the SRS class parameters'''
        super().__init__(name, address, **kwargs)
        self.freq_panel = pn.widgets.TextInput(name='SRS Frequency', value='5000000')
        self.freq_panel.param.watch(self.update_frequency, 'value')
    
    def update_frequency(self,event):
        '''Convert the Text to a float'''
        freq = float(event.new)
        '''Call the frequency .set() function'''
        self.frequency.set(freq)

SRS =  Instr_Panel(name='SRS384', address='TCPIP::192.168.1.121',timeout=.01)
col = pn.Column(SRS.freq_panel)
col.servable()

If you wanted to use a Parameterized pattern you could also do:

class Instr_Panel(Instr,param.Parameterized,metaclass = mChild):

    freq = param.String()

    def __init__(self, name, address, reset=False, **kwargs):
        super().__init__(name, address, **kwargs)

    @pn.depends('freq', watch=True)
    def update_frequency(self):
        '''Convert the Text to a float'''
        freq = float(event.new)
        '''Call the frequency .set() function'''
        self.frequency.set(self.freq)

SRS =  Instr_Panel(name='SRS384', address='TCPIP::192.168.1.121',timeout=.01)
pn.Column(SRS.param).servable()

But maybe I’m totally missing what you’re trying to do.

Hi Philipp,

Super helpful reply. You are getting what I am trying to do, I.e. build a parameterized instrument gui that can be used to build larger apps. Your second example is very close to what I want. However, I don’t think that update_frequency is getting updated when I modify the text input.

Note I am running the code with bokeh serve someapp.py. I added a print statement to verify it’s working operation which I am monitoring on a powershell prompt. It never prints the updated frequency and the SRS isn’t changing it’s frequency

I tried your version and then made some modifications trying to get it to work.

class SRS_SG384_Panel(SRS_SG384,param.Parameterized,metaclass = mChild):
    freq = param.String('232')
    
    def __init__(self, name, address, reset=False, **kwargs):
        print('something')
        super().__init__(name, address, **kwargs)
        
        '''Update the Widget Box to the current value of the instrument'''
        self.freq = str(self.frequency.get())
    
    
    @pn.depends('freq', watch=True)
    def update_frequency(self):
        '''Convert the Text to a float'''
        freq = float(self.freq)
        print(freq)
        '''Call the frequency .set() function'''
        self.frequency.set(freq)
        



    
SRS =  SRS_SG384_Panel(name='SRS384', address='TCPIP::192.168.1.121',timeout=.01)
    
pn.Column(SRS.param).servable()  

Note when I call self.freq = str(self.frequency.get()), I have the suspicion that I am overwriting the param’s class. But this is what I am told to do from https://panel.holoviz.org/user_guide/Param.html
when it says we can update them with Example.int_list = [1, 7].

So there are two issues:

1.) The update is not in fact updating.
2.) The value in the init method is updating the SRS.freq parameter but it is not updating the widget. Which is why I have the suspicion that I am overwriting the param class.

Thanks for your help.

To Follow Up.

I wrote a simplified version of what I am trying to do and still cannot get panel to behave correctly. Any help would be appreciated.

Note this set of code is not printing out anything whatsoever when the text value is updated. Nor is the init updating the string from ‘232’ to ‘343’ like I would expect when launched.

class APanel(param.Parameterized):
    some_str = param.String('232')
    
    def __init__(self, **kwargs):
        print('something')
        self.some_str = '343'
        print('got the a new value')
        print(self.some_str)


    @pn.depends('some_str', watch=True)
    def update(self):
        '''Convert the Text to a float'''
        some_strs = float(self.some_str)
        print(some_strs)

AP = APanel()        
pn.Column(AP.param).servable()

if you add super(APanel, self).init(**params) it works.

import panel as pn, param
pn.extension()
class APanel(param.Parameterized):
    some_str = param.String('232')
    
    def __init__(self, **params):
        super(APanel, self).__init__(**params)
        print('something')
        self.some_str = '343'
        print('got the a new value')
        print(self.some_str)


    @pn.depends('some_str', watch=True)
    def update(self):

        some_strs = float(self.some_str)
        print(some_strs)

AP = APanel()        
pn.Column(AP.param)

perfect!

1 Like