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.
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.
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