SetVariable with string

Hi,
With the Prep4AnalyseGBC function, my aim is to allow the user to create the definition for a string variable (sID) which is then passed to a series of other routines that I have not shown. My problem is with the SetVariable operation. When I enter text into the control panel, my expectation is that this will become the definition for sID. The information that is available in the igor manual (page III-377) about using SetVariable in this way implies to me that the text entered to the control panel should be passed to the parameter varStr in my action procedure, Prep4AnalyseGBC_svWaveID.

However, this is not happening, because the procedure fails at run time and sID comes out as a null string. Can anyone elaborate a little on how SetVariable is supposed to work in this case or point me to a few pages in the manual where more information is available?


Structure strAnalyseGBC
   
    String sID

EndStructure

Function Prep4AnalyseGBC()//(vVoltageAmplitude4TPParameters,  sFirstDF, sLastDF)

    //Variable vVoltageAmplitude4TPParameters
    //String sFirstDF, sLastDF
   
   
    NewDataFolder/O root:tmp_PauseforPrepDF
    String/G root:tmp_PauseforPrepDF:sID = ""
   
    //STRUCT strAnalyseGBC Prep4
   
    DoWindow/K tmp_PauseforPrep
    NewPanel/K=2 /W=(90,300,420,460)/N=tmp_PauseforPrep as "Enter wave info"   
   
    SetVariable svWaveID, title="Wave ID", fsize=14, pos={50,25}, size={150,50}, value=_STR:"", proc=Prep4AnalyseGBC_svWaveID
   
    PauseForUser tmp_PauseforPrep
   
    SVAR sID_G = root:tmp_PauseforPrep:sID
//sID receives an alternative designation as a global string variable.
    String sID = sID_G
//sID as a global variable is copied to a local string.

    KillDataFolder root:tmp_PauseforPrep

    //Print Prep4.sID
    Print sID
End

Function Prep4AnalyseGBC_svWaveID (svWaveID, varNum, varStr, varName) : SetVariableControl
   
    String svWaveID, varStr, varName
    Variable varNum
   
    //STRUCT strAnalyseGBC Prep4_svWaveID
   
    String/G root:tmp_PauseforPrepDF:sID = varStr
   
    //Prep4_svWaveID.sID = varStr
   
    DoWindow/K tmp_PauseforPrep
End
You are using value=_STR in the SetVariable command. This means "store the value entered by the user variable in Igor internal storage". You can read that value using the ControlInfo operation when PauseForUser returns.

If you want to store the value in a global variable you need to write:
value=root:tmp_PauseforPrep:sID

However using the internal storage is better because it avoids the need to create a global variable.
I haven't looked too closely into your code, but here are two comments:
- You don't need an action procedure just to pass the value to some global string. You should tell SetVariable which string to use upon its creation and the variable control will always automatically update. That's what the value parameter is for.
- With the newer Igor versions it became even more unfavorable to use global variables for this. Better not hardcode these ugly things into your code and clutter your experiment. You can use value=_STR:stuff to attach this stuff directly to the particular control (i.e., its window- and even control-specific). Extract the value using ControlInfo only when needed (opposed to having the stuff lying around all the time.

Edit: Ooops, too slow this time ... OK, here's a bonus comment:
It seems that you are working with folders (a lot?). You might want to look into data folder references (DFREFs) before its too late to clean up all the clutter.
Thanks for your replies. I attempted to implement the the ControlInfo function in my routine, but the code posted below fails at runtime because s_value comes out as a null variable. As I understand it, S_value should take on the name of whatever sequence of alphabetical keyboard strokes follow _STR:

i.e. whatever is entered into the text box in the panel (tmp=PauseforPrep) that appears.

Structure strAnalyseGBC
   
    String sID

EndStructure

Function Prep4AnalyseGBC()//(vVoltageAmplitude4TPParameters,  sFirstDF, sLastDF)

    //Variable vVoltageAmplitude4TPParameters
    //String sFirstDF, sLastDF
   
   
    NewDataFolder/O root:tmp_PauseforPrepDF
    //String/G root:tmp_PauseforPrepDF:sID
   
    String sID
   
    //STRUCT strAnalyseGBC Prep4
   
    DoWindow/K tmp_PauseforPrep
    NewPanel/K=2 /W=(90,300,420,460)/N=tmp_PauseforPrep as "Enter wave info"   
   
    SetVariable svWaveID, title="Wave ID", fsize=14, pos={50,25}, size={150,50}, value=_STR:"", proc=Prep4AnalyseGBC_svWaveID
   
    PauseForUser tmp_PauseforPrep
   
    ControlInfo svWaveID
   
    sID = s_value
   
    //SVAR sID_G = root:tmp_PauseforPrep:sID
//sID receives an alternative designation as a global string variable.
    //String sID = sID_G
//sID as a global variable is copied to a local string.

    KillDataFolder root:tmp_PauseforPrepDF
   
    //Print Prep4.sID
    Print sID
End

Function Prep4AnalyseGBC_svWaveID (svWaveID, varNum, varStr, varName) : SetVariableControl
   
    String svWaveID, varStr, varName
    Variable varNum
   
    //STRUCT strAnalyseGBC Prep4_svWaveID
       
    //ControlInfo svWavelD
    //print s_value
   
    //Prep4_svWaveID.sID = s_value
    //varStr = s_value
    //print varStr
    //String/G root:tmp_PauseforPrepDF:sID = varStr
    //print sID
   
    DoWindow/K tmp_PauseforPrep
End
Quote:
but the code posted below fails at runtime because s_value comes out as a null variable.


That's because you are killing the SetVariable control, by killing the panel, before you call ControlInfo.

Here is a way to achieve what you want.

Function ContinueButtonProc(ba) : ButtonControl
    STRUCT WMButtonAction &ba

    switch(ba.eventCode)
        case 2:                 // mouse up
            ControlInfo/W=tmp_PauseforPrep svWaveID
            String/G root:gPauseForUserOutput = S_value   // Create global variable to store output
            KillWindow tmp_PauseforPrep
            break
    endswitch

    return 0
End

Function CreatePanel()
    NewPanel /N=tmp_PauseforPrep /K=2 /W=(90,300,420,460) as "Enter wave info"
    SetVariable svWaveID, title="Wave ID", fsize=14, pos={50,25}, size={150,50}, value=_STR:""
    Button ContinueButton,pos={120,121},size={70,20},proc=ContinueButtonProc,title="Continue"
EndMacro

Function Demo()
    CreatePanel()
    PauseForUser tmp_PauseforPrep
    SVAR output = root:gPauseForUserOutput    // Access global variable to get output
    Print output
End

Thanks for the code. It certainly works! One remaining issue is that I do not really understand what the 'ba' (in the action procedure) variable represents. There are numerous examples in the manual of code with a format similar to the one you posted, but I could not find an explanation for what the variable (sometimes denoted: 'ctrlName') means. Please can you explain?
It's the name of a parameter that Igor passes to a control action procedure. The type of the parameter is "reference to WMButtonAction structure". For details read the Details section of the documentation for Button.

There are two forms for the action procedure. The form that takes a ctrlName parameter is antiquated. The form that takes a reference to a structure is recommended.
Thanks for the hint. The form which takes the in-built structure WMButtonAction seems neater. I modified this code to do something else. It works, except that for some reason igor skips the printf function at the bottom. I had expected V_Flag = 1 if the button was clicked and V_Flag = 0 if the button was not clicked. Because this is not the case I used the two conditional statements in the two action procedures. Is this really how you would do it? Or is there a neater way?

#pragma rtGlobals=3     // Use modern global access method and strict wave access.

Function Readjust_4Area(ba1) : ButtonControl
    STRUCT WMButtonAction &ba1
   
        switch(ba1.eventCode)
        case 2:                 // mouse up

            ControlInfo/W=PauseforReadjustment vMeasureArea
            if (V_Flag == 0)
                Variable/G root:gPauseForUserReadjust1 = 1
                Variable/G root:gPauseForUserReadjust2 = 0
            endif
            KillWindow PauseforReadjustment
            break
    endswitch
 
    //return 0
End
 
 Function Readjust_4NoArea(ba2) : ButtonControl
    STRUCT WMButtonAction &ba2

        switch(ba2.eventCode)
        case 2:                 // mouse up
       
            ControlInfo/W=PauseforReadjustment vDoNotMeasureArea
            if (V_Flag == 0)
                Variable/G root:gPauseForUserReadjust2 = 2
                Variable/G root:gPauseForUserReadjust1 = 0
            endif
            KillWindow PauseforReadjustment
            break
    endswitch
 
    //return 0
End
 
Function CreateReadjustPanel(sGraphName)

    String sGraphName
   
    NewPanel/N=PauseforReadjustment/K=2 /W=(90,300,420,460) as "Cursor B readjustment panel"
    DoWindow/C PauseforReadjustment
    AutoPositionWindow/E/M=1/R=$sGraphName
   
    DrawText 21,20,"Adjust cursor B for best area measurement"

   
    Button button1,pos={65,35},size={220,10}, proc=Readjust_4Area,title= "Cursor B ready"
    Button button2,pos={65,60},size={220,10},proc= Readjust_4NoArea, title= "Don't measure area"
   
EndMacro
 
Function Readjust_vReturn2BaseLoc (sGraphName)
   
    String sGraphName
   
    CreateReadjustPanel(sGraphName)
    PauseForUser PauseforReadjustment, $sGraphName 

    NVAR vMeasureArea = root:gPauseForUserReadjust1
    NVAR vDoNotMeasureArea = root:gPauseForUserReadjust2
   
    if (vMeasureArea == 1)
        return vMeasureArea
       
    elseif (vDoNotMeasureArea == 2)
        return vDoNotMeasureArea
   
    endif
   
    Printf "Results = %g\r and %g\r", vMeasureArea, vDoNotMeasureArea
   
End
<\igor>
Quote:
It works, except that for some reason igor skips the printf function at the bottom.


That's because your function returns before the Printf statement.


This statement makes no sense because you have no control named vDoNotMeasureArea:
ControlInfo/W=PauseforReadjustment vDoNotMeasureArea

Same for vMeasureArea.

You don't need to call ControlInfo for a button. The mere fact that your button action procedure was called means that the button was clicked.
Ah yes, I had forgotten that the return statement stops execution of the code. I had thought the modifications I made below would work but, with this code, if I click on the window sGraphName, then I can no longer click on the PauseForReadjustment panel. I didn't seem to have this problem before. Any ideas what's gone wrong?

Function Readjust_4Area(ba1) : ButtonControl
    STRUCT WMButtonAction &ba1
   
        switch(ba1.eventCode)
        case 2:                 // mouse up

            Variable/G root:gPauseForUserReadjust1 = 1
            Variable/G root:gPauseForUserReadjust2 = 0
           
            KillWindow PauseforReadjustment
            break
    endswitch
 
    return 0
End
 
 Function Readjust_4NoArea(ba2) : ButtonControl
    STRUCT WMButtonAction &ba2

        switch(ba2.eventCode)
        case 2:                 // mouse up
       
       
            Variable/G root:gPauseForUserReadjust2 = 2
            Variable/G root:gPauseForUserReadjust1 = 0
           
            KillWindow PauseforReadjustment
            break
    endswitch
 
    return 0
End
 
Function CreateReadjustPanel(sGraphName)

    String sGraphName
   
    NewPanel/N=PauseforReadjustment/K=2 /W=(90,300,420,460) as "Cursor B readjustment panel"
    DoWindow/C PauseforReadjustment
    AutoPositionWindow/E/M=1/R=$sGraphName
   
    DrawText 21,20,"Adjust cursor B for best area measurement"

   
    Button button1,pos={65,35},size={220,10}, proc=Readjust_4Area,title= "Cursor B ready"
    Button button2,pos={65,60},size={220,10},proc= Readjust_4NoArea, title= "Don't measure area"
   
EndMacro
 
Function Readjust_vReturn2BaseLoc (sGraphName)
   
    String sGraphName
   
    CreateReadjustPanel(sGraphName)
    PauseForUser PauseforReadjustment, $sGraphName 

    NVAR vMeasureArea = root:gPauseForUserReadjust1
    NVAR vDoNotMeasureArea = root:gPauseForUserReadjust2
   
    //Printf "Results = %g\r and %g\r", vMeasureArea, vDoNotMeasureArea
   
End
<\igor>
That's most probably because of your PauseForUser statement. You are using sGraphName as the target, and to cite the manual
Quote:

During execution of PauseForUser, only mouse and keyboard activity directed toward either mainWindowName or targetWindowName is allowed.
chozo wrote:
That's most probably because of your PauseForUser statement. You are using sGraphName as the target, and to cite the manual
Quote:

During execution of PauseForUser, only mouse and keyboard activity directed toward either mainWindowName or targetWindowName is allowed.


But I should be able to click between the two windows without a problem - that is what is implied by the statement you cite from the manual. What happens now is that I can click on either window once execution pauses, but as soon as I click on the 'target window' I get stuck on it and cannot click on the panel (which seems to be locked in the background).
Problem solved! Not having a return statement for vMeasureArea and vDoNotMeasureArea seemed to be causing the problem. I needed this anyway, of course, but I did not spot it immediately because I thought something else must be causing the problem I was experiencing.