Accessing user-defined structures from controls (buttons, listbox...)

Hello,

I'd like to access the members of a structure which is selected from a ListBox (or a popup menu).
The challenge is that the event (e.g. Cell Selected) calls a procedure which can have only one parameter : the Control name, whereas I'd like to pass also a structure reference.
First, I tried to "encapsulate" the control into the structure, so that each instance of the structure has its own button (this button is aimed at changing the values of the structures data members) :

Structure class_VFA
    string str_Analyte
    variable  RT
    variable  Vc_Mono
    variable  Vc_Dimer

    variable  Sdev_RT
    variable  Sdev_Vc_Mono
    variable  Sdev_Vc_Dimer

    variable  RTMin
    variable  RTMax
   
    variable  VcMin_Mono
    variable  VcMax_Mono
   
    variable  VcMin_Dimer
    variable  VcMax_Dimer
   
    STRUCT WMButtonAction Button_RedefineCoords
EndStructure
//-----------------------------------------------------------------------------
        //Fonction d'Initialisation
        //------------------------------
Function Init_Analyte(str_Analyte,instance_locale,RT,Vc_Mono,Vc_Dimer,Sdev_RT,Sdev_Vc_Mono,Sdev_Vc_Dimer,RTMin,RTMax,VcMin_Mono,VcMax_Mono,VcMin_Dimer,VcMax_Dimer)
   
    string str_Analyte
    STRUCT class_VFA &instance_locale
    variable    RT,Vc_Mono,Vc_Dimer,Sdev_RT,Sdev_Vc_Mono,Sdev_Vc_Dimer,RTMin,RTMax,VcMin_Mono,VcMax_Mono,VcMin_Dimer,VcMax_Dimer
   
    instance_locale.str_Analyte = str_Analyte
    instance_locale.RT = RT
    instance_locale.Vc_Mono = Vc_Mono
    instance_locale.Vc_Dimer = Vc_Dimer
    instance_locale.Sdev_RT = Sdev_RT
    instance_locale.Sdev_Vc_Mono = Sdev_Vc_Mono
    instance_locale.Sdev_Vc_Dimer = Sdev_Vc_Dimer
    instance_locale.RTMin = RTMin
    instance_locale.RTMax = RTMax
    instance_locale.VcMin_Mono = VcMin_Mono
    instance_locale.VcMax_Mono = VcMax_Mono
    instance_locale.VcMin_Dimer = VcMin_Dimer
    instance_locale.VcMax_Dimer = VcMax_Dimer
   
    instance_locale.Button_RedefineCoords.ctrlName = "Button_Coords_"+str_Analyte
//------------------------------------------------------------------------------------------------------------------
Function Afficher_BtnRedefineCoords(instance_locale)

    STRUCT class_VFA &instance_locale

    Button $instance_locale.Button_RedefineCoords.ctrlName,pos={650,200},size={24,18},proc=ButtonProc_RedefineCoords,title="°",font="Wingdings",fSize=16,fColor=(0xFFFF,0xFFFF,0xFFFF)

End


It didn't work since i don't know how to access the members of the structure containing the button instance.

Then I decided to suppress the buttons, and to create a listbox or a popup menu (actually I don't know which solution (1 button per instance, 1 listbox, 1 popup menu...) is the cleanest from a software-engineering point of view)

The main issue is still there : how to refer to a structure from a control (button in the 1st case, item number for the listbox)

I hope I've been clear, I would understand if the description of my problem was confusing. If so, please do not hesitate to ask me.

Thanks in advance!

Best Regards.

Nasser
I don't understand what you are asking or what you are trying to do. It might be helpful if you posted enough example code that I could paste the code into Igor and run it in the same way you are doing so (probably you'd need to include code for an example panel, etc.).

The only way you can get a filled in structure for a control in Igor is if you set the control's action procedure to a structure based action procedure for that control type. Otherwise, you can use one of the predefined control structures in your code (eg. WMButtonAction), but your code has to fill out all of the values in the structure that are necessary to get your code to work. You could use ControlInfo to get some of the information about the control and put those values into the structure.

Igor doesn't allow static instances of structures- they can only exist within the context of executing function code. But you can store a "freeze-dried" copy of a structure as a string or byte wave. To do that, use the StructPut operation (note that you must use the /S flag to store into a string, which I find to be the most convenient).

To get back a copy of the structure, use GetStruct (again with /S if you are using a string).

The reason a string is most convenient is that it can be stored inside a control using the the control's userdata keyword. In the action procedure, you would use something like
String userdata = GetUserData(s.win, s.ctrlName, "myUserData")
STRUCT class_VFA myVFA
StuctGet/S myVFA, userdata

(code not tested, I may have made some stupid mistake...)

Note that there are restrictions on what kind of members you can have in a structure that you store via StructPut. Your structure contains a String member, which is not allowed. You can use a char array, and it will have almost the same functionality. The Variable members are OK.

The STRUCT WMButtonAction member will not be useful, however, as you have found.


The Scatter Plot Matrix package uses StructPut/StructGet to store information into the generated graph window. See File->Example Experiments->Graphing Techniques->Scatter Plot Matrix Demo.

Another example of the use of StructPut/StructGet is the WaveSelectorWidget module. See File->Example Experiments->Programming->WaveSelectorWidgetExample.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
Hello aclight,

aclight wrote:
I don't understand what you are asking or what you are trying to do. It might be helpful if you posted enough example code that I could paste the code into Igor and run it in the same way you are doing so (probably you'd need to include code for an example panel, etc.).


First, I have created a structure, which consists of a string (the name of the chemical), and coordinates (in order to locate the peak corresponding to that chemical, on a spectrum image). The reason why I have defined a structure is that there will be less arguments to be passed in functions, for instance, to quantify the peak. This is working.

Then, my aim is to access the fields of a structure from a control (most likely an item in a listbox).
Here is how I'd like to do it :
> I'd like to re-define the coordinqtes of a peak.
> I choose it from a listbox (I select its name, corresponding to the field "str_Analyte" in the user-defined structure)
> I use the GetMarquee function in order to get the 4 new coordinates values (top left, top right, bottom left, bottom right)
> I affect these values to the Vcmin, VcMax, RTmin, RTmax fields, in the structure instance corresponding to the peak I've choosen in the ListBox.
> I'm happy

Do you know what I mean ?

Best Regards
Hello John,

johnweeks wrote:
Igor doesn't allow static instances of structures- they can only exist within the context of executing function code. But you can store a "freeze-dried" copy of a structure as a string or byte wave. To do that, use the StructPut operation (note that you must use the /S flag to store into a string, which I find to be the most convenient).


I don't wish these structure instances to be static. I'd like to be able to redefine the coordinates (for instance).
To be honest, I don't realy know what you mean by "freeze-dried".

johnweeks wrote:
Note that there are restrictions on what kind of members you can have in a structure that you store via StructPut. Your structure contains a String member, which is not allowed. You can use a char array, and it will have almost the same functionality. The Variable members are OK.

Thanks for the info. In the documentation it's written "The structure to be exported must contain only numeric data", fortunately char arrays are supported !

johnweeks wrote:
The STRUCT WMButtonAction member will not be useful, however, as you have found.

The aim was to create one button per structure instance, so as to directly access the "parent structure" instance fields (e.g. the coordinates of the chemical's peak). Now I'm trying to do the same using an item choosen in a ListBox : How to retrieve the structure instance corresponding to the selected item, in order to access its fields ?


johnweeks wrote:
The Scatter Plot Matrix package uses StructPut/StructGet to store information into the generated graph window. See File->Example Experiments->Graphing Techniques->Scatter Plot Matrix Demo.

Another example of the use of StructPut/StructGet is the WaveSelectorWidget module. See File->Example Experiments->Programming->WaveSelectorWidgetExample.


I'll have a look to these examples, to check if their purpose answer my question.

Thank you

Best Regards
Nasser wrote:
I don't wish these structure instances to be static. I'd like to be able to redefine the coordinates (for instance).

I don't mean static in the sense that they can't change, but static in the programmer's sense that they are not local variables that disappear when the function finishes.
Quote:
To be honest, I don't realy know what you mean by "freeze-dried".

Sorry- that's very colloquial American English :) It refers to the process by which food can be dried and made very lightweight for use by hikers or astronauts. In this case, it is intended to mean that the structures are stored away in a special encoding that allows you to re-make the structure later. Just like the hiker or astronaut can re-introduce water to freeze-dried food during cooking to make an edible meal.
Quote:
The aim was to create one button per structure instance, so as to directly access the "parent structure" instance fields (e.g. the coordinates of the chemical's peak). Now I'm trying to do the same using an item choosen in a ListBox : How to retrieve the structure instance corresponding to the selected item, in order to access its fields ?

And you're trying to coerce Igor's language to look more object-oriented, which you will most likely achieve with only limited success. In the case of a structure for each button, you clearly can use StructPut to store one structure in the control. But with a structure for each row of a ListBox, you have only one control and a number of structures. You might be able to store a structure with an array of structures, but I think that will get cumbersome. Another possibility would be to use multiple named userdata strings to store multiple structures. The structure for row 1 might be stored using userdata(MyStruct1)=... and recovered via GetUserData(panelname, listboxname, "MyStruct"+num2str(rownum)).

Another technique I have used for storing per-row data in a listbox is to use a three-dimensional matrix for the listbox selwave. I then use the extra layers of the selwave (or the listwave- both work) to store private data relating to the rows and columns in the list.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
Hi John,

Thank you for your prompt answer and all those explanations.
johnweeks wrote:
And you're trying to coerce Igor's language to look more object-oriented, which you will most likely achieve with only limited success

You're right, I'm focusing on an Object-oriented programming :)
--> http://www.igorexchange.com/node/1965

I thought the code would be easier to maintain using an Object-Oriented approach.

Regarding your suggestions, I'm going to use the userData variables, that I don't know yet. The most important is that we can access a structure's members from a list's item.

Thank you very much.

Kind regards

Thank you very much, it's working :

Function Afficher_BtnRedefineCoords(instance_locale,y)

    STRUCT class_VFA &instance_locale
    variable y
   
    string str_Button_RedefineCoords
    string strStruct4UserData
       
    str_Button_RedefineCoords = "Button_Coords_" + instance_locale.str_Analyte

    StructPut /S instance_locale, strStruct4UserData //on sauvegarde l'instance de structure VOC dans une chaine de caract.
   
    Button $str_Button_RedefineCoords,pos={612,460+20*y},size={24,18},proc=ButtonProc_RedefineCoords,title="°",font="Wingdings",fSize=16,fColor=(0xFFFF,0xFFFF,0xFFFF), userdata=strStruct4UserData
       
End

//------------------------------------------------------------------------------------------------

Function ButtonProc_RedefineCoords(ba) : ButtonControl
    STRUCT WMButtonAction &ba

    STRUCT class_VFA myVFA
   
        switch( ba.eventCode )
            case 2: // mouse up
                StructGet/S myVFA, ba.userdata  // written out to control
                RedefineCoords(myVFA)               // click code here
            break
        endswitch

        return 0
End


Best Regards