Managing graph-updates in a user-defined function
fernandes_edgar
This time I am stuck with the way to program in order to have an instantaneous update of a graph after a user-modification.
Let me explain the situation: My scanning tunnelling spectroscopy (STS) data has some trend (which is not define, can be quadratic, linear, exponential) that I want to remove. For that I developed a function that removes this trend by fitting regions of the data defined by the cursors. The idea is that as far as the cursors are moved, the fit should update and then by clicking on a "save" button I remove the fit to the raw data.
Presently I update the graph by using an infinite loop "do() while(true)". This is not satisfying for two reasons; First, this is silly, and second, this avoids me to update the script by using sliders to select the ROI instead of the graph cursors.
Here is the code I use now. In comment are what I would use with sliders.
Function RemoveLinContribution(srcWave, traceName, graphName, PolyNumber)
//This function is intended to remove the linear contribution in the measured spectras.
//If the contribution is flat, there is no use to remove the constant contribution
//We do a new window, in order to accept the graph
Wave srcWave
String traceName, graphName
Variable PolyNumber
//Wave maskWave
Variable/G saving = 0
String crs1="", crs2=""
ShowInfo/CP={0,1,2,3,4}/W=$graphName
Button SaveButton, title="Save", proc = SaveRmLinContribution
Cursor/A=1/W=$graphName A $traceName leftx(srcWave)
Cursor/A=1/P/W=$graphName B $traceName numpnts(srcWave)-1
Duplicate/O srcWave rmLinContrib
Duplicate/O srcWave LineFit//The wave wich will have the original one withouth the slope
Duplicate/O srcWave maskWave//The mask we will use to fit the line
Make/O/N=10 crsValues
AppendToGraph/W=$graphName/C=(0,0,0) LineFit
//We will construct the panel with the sliders
//NewPanel /W=(950,161,1749,519) as "Sliders for the trend removal"
// ShowTools/A
// SetDrawLayer UserBack
// DrawText 28,23,"A"; DrawText 34,47,"B"; DrawText 25,67,"C"; DrawText 30,93,"D";
// DrawText 29,128,"E"; DrawText 38,148,"F"; DrawText 23,173,"G"; DrawText 34,204,"H"
// DrawText 23,233,"I"; DrawText 23,240,"I";
// Slider slA,pos={105.00,14.00},size={621.00,17.00},proc=SliderProc
// Slider slA,limits={0,200,1},value= crsValues[0],vert= 0,ticks= 0
// Slider slB,pos={107.00,43.00},size={621.00,17.00},proc=SliderProc
// Slider slB,limits={0,200,1},value= crsValues[1],vert= 0,ticks= 0
// Slider slC,pos={109.00,73.00},size={621.00,17.00},proc=SliderProc
// Slider slC,limits={0,200,1},value= crsValues[2],vert= 0,ticks= 0
// Slider slD,pos={110.00,105.00},size={621.00,17.00},proc=SliderProc
// Slider slD,limits={0,200,1},value= crsValues[3],vert= 0,ticks= 0
// Slider slE,pos={113.00,130.00},size={621.00,17.00},proc=SliderProc
// Slider slE,limits={0,200,1},value= crsValues[4],vert= 0,ticks= 0
// Slider slF,pos={109.00,162.00},size={621.00,17.00},proc=SliderProc
// Slider slF,limits={0,200,1},value= crsValues[5],vert= 0,ticks= 0
// Slider slG,pos={108.00,197.00},size={621.00,17.00},proc=SliderProc
// Slider slG,limits={0,200,1},value= crsValues[6],vert= 0,ticks= 0
// Slider slH,pos={111.00,229.00},size={621.00,17.00},proc=SliderProc
// Slider slH,limits={0,200,1},value= crsValues[7],vert= 0,ticks= 0
// Slider slI,pos={112.00,258.00},size={621.00,17.00},proc=SliderProc
// Slider slI,limits={0,200,1},value= crsValues[8],vert= 0,ticks= 0
// Slider slJ,pos={111.00,285.00},size={621.00,17.00},proc=SliderProc
// Slider slJ,limits={0,200,1},value= crsValues[9],vert= 0,ticks= 0
//
// if(PolyNumber==3)
// Variable y0=0,A=0,invT=0
// Prompt y0, "Enter y0 value: " // Set prompt for x param
// Prompt A, "Enter A value: " // Set prompt for y param
// Prompt invT, "Enter invT value:"
// DoPrompt "Enter Values", y0, A, invT
// if (V_Flag)
// return -1 // User canceled
// endif
// K0 = y0;K1 = A;K2 = -invT;
// endif
//
do
// Cursor/A=1/W=$graphName A $traceName crsValues[0]
// Cursor/A=1/W=$graphName B $traceName crsValues[1]
// Cursor/A=1/W=$graphName C $traceName crsValues[2]
// Cursor/A=1/W=$graphName D $traceName crsValues[3]
// Cursor/A=1/W=$graphName E $traceName crsValues[4]
// Cursor/A=1/W=$graphName F $traceName crsValues[5]
// Cursor/A=1/W=$graphName G $traceName crsValues[6]
// Cursor/A=1/W=$graphName H $traceName crsValues[7]
// Cursor/A=1/W=$graphName I $traceName crsValues[8]
// Cursor/A=1/W=$graphName J $traceName crsValues[9]
// DoUpdate
maskWave = 0
//We will put '1' in the maskWave for the data points between the cursors
crs1 = CsrInfo(A); crs2 = CsrInfo(B)
//If the two cursors A and B are on the graph
if(cmpstr(crs1, "", 2) && cmpstr(crs2, "", 2))
maskWave[pcsr(A),pcsr(B)] = 1
endif
crs1 = CsrInfo(C); crs2 = CsrInfo(D)
//If the two cursors C and D are on the graph
if(cmpstr(crs1, "", 2) && cmpstr(crs2, "", 2))
maskWave[pcsr(C),pcsr(D)] = 1
endif
crs1 = CsrInfo(E); crs2 = CsrInfo(F)
//If the two cursors E and F are on the graph
if(cmpstr(crs1, "", 2) && cmpstr(crs2, "", 2))
maskWave[pcsr(E),pcsr(F)] = 1
endif
crs1 = CsrInfo(G); crs2 = CsrInfo(H)
//If the two cursors G and H are on the graph
if(cmpstr(crs1, "", 2) && cmpstr(crs2, "", 2))
maskWave[pcsr(G),pcsr(H)] = 1
endif
crs1 = CsrInfo(I); crs2 = CsrInfo(J)
//If the two cursors I and J are on the graph
if(cmpstr(crs1, "", 2) && cmpstr(crs2, "", 2))
maskWave[pcsr(I),pcsr(J)] = 1
endif
//We fit between the cursor position
//We fit in the regions of the mask = 1
if(Polynumber == 1)
CurveFit/Q line srcWave /M=maskWave /D=LineFit
elseif(Polynumber == 2)
CurveFit/Q poly 3, srcWave /M=maskWave /D=LineFit
endif
Wave W_Coef
if(PolyNumber == 1)
LineFit[] = W_Coef[0] + W_Coef[1]*pnt2x(LineFit, p)
elseif(Polynumber == 2)
LineFit[] = W_Coef[0] + W_Coef[1]*pnt2x(LineFit, p) + W_Coef[2]*pnt2x(LineFit, p)*pnt2x(LineFit, p)
endif
//sleep/B/S/C=2 1
DoUpdate
PauseForUser/C $graphName
while(saving==0)
if(saving)
DFREF srcDF= GetDataFolderDFR()
String SaveName = nameofwave(srcWave)+"_rmLin"
//We remove the linear contribution, centered around 0
//Then, at 0 the measured point does not change, only outside we remove the slope
Duplicate/O srcWave srcDF:$SaveName
Wave srcWave_rmLin = srcDF:$SaveName
if(Polynumber == 1)
srcWave_rmLin[] -= W_Coef[1]*pnt2x(srcWave_rmLin, p)
Note srcWave_rmLin "\rDrif-corrected with a degree 1 polynomial"
elseif(PolyNumber == 2)
srcWave_rmLin[] -= W_Coef[1]*pnt2x(srcWave_rmLin, p) + W_Coef[2]*pnt2x(srcWave_rmLin, p)*pnt2x(srcWave_rmLin, p)
Note srcWave_rmLin "\rDrif-corrected with a degree 2 polynomial"
endif
//We close the graph now
RemoveFromGraph/W=$graphName LineFit
KillControl SaveButton
Cursor/K/W=$graphName A
Cursor/K/W=$graphName B
Cursor/K/W=$graphName C
Cursor/K/W=$graphName D
Cursor/K/W=$graphName E
Cursor/K/W=$graphName F
Cursor/K/W=$graphName G
Cursor/K/W=$graphName H
Cursor/K/W=$graphName I
Cursor/K/W=$graphName J
HideInfo/W=$graphName
endif
KillWaves LineFit, rmLinContrib
return 0
End
Function SaveRmLinContribution(ctrlName) : ButtonControl
String ctrlName
NVAR saving
saving = 1
End
Function SliderProc(ctrlName,sliderValue,event) : SliderControl
String ctrlName
Variable sliderValue
Variable event // bit field: bit 0: value set, 1: mouse down, 2: mouse up, 3: mouse moved
Wave crsValues
if(event & 0x1) // bit 0, value set
if(!cmpstr(ctrlName, "slA"))
crsValues[0] = sliderValue
elseif(!cmpstr(ctrlName, "slB"))
crsValues[1] = sliderValue
elseif(!cmpstr(ctrlName, "slC"))
crsValues[2] = sliderValue
elseif(!cmpstr(ctrlName, "slD"))
crsValues[3] = sliderValue
elseif(!cmpstr(ctrlName, "slE"))
crsValues[4] = sliderValue
elseif(!cmpstr(ctrlName, "slF"))
crsValues[5] = sliderValue
elseif(!cmpstr(ctrlName, "slG"))
crsValues[6] = sliderValue
elseif(!cmpstr(ctrlName, "slH"))
crsValues[7] = sliderValue
elseif(!cmpstr(ctrlName, "slI"))
crsValues[8] = sliderValue
elseif(!cmpstr(ctrlName, "slJ"))
crsValues[9] = sliderValue
endif
endif
//This function is intended to remove the linear contribution in the measured spectras.
//If the contribution is flat, there is no use to remove the constant contribution
//We do a new window, in order to accept the graph
Wave srcWave
String traceName, graphName
Variable PolyNumber
//Wave maskWave
Variable/G saving = 0
String crs1="", crs2=""
ShowInfo/CP={0,1,2,3,4}/W=$graphName
Button SaveButton, title="Save", proc = SaveRmLinContribution
Cursor/A=1/W=$graphName A $traceName leftx(srcWave)
Cursor/A=1/P/W=$graphName B $traceName numpnts(srcWave)-1
Duplicate/O srcWave rmLinContrib
Duplicate/O srcWave LineFit//The wave wich will have the original one withouth the slope
Duplicate/O srcWave maskWave//The mask we will use to fit the line
Make/O/N=10 crsValues
AppendToGraph/W=$graphName/C=(0,0,0) LineFit
//We will construct the panel with the sliders
//NewPanel /W=(950,161,1749,519) as "Sliders for the trend removal"
// ShowTools/A
// SetDrawLayer UserBack
// DrawText 28,23,"A"; DrawText 34,47,"B"; DrawText 25,67,"C"; DrawText 30,93,"D";
// DrawText 29,128,"E"; DrawText 38,148,"F"; DrawText 23,173,"G"; DrawText 34,204,"H"
// DrawText 23,233,"I"; DrawText 23,240,"I";
// Slider slA,pos={105.00,14.00},size={621.00,17.00},proc=SliderProc
// Slider slA,limits={0,200,1},value= crsValues[0],vert= 0,ticks= 0
// Slider slB,pos={107.00,43.00},size={621.00,17.00},proc=SliderProc
// Slider slB,limits={0,200,1},value= crsValues[1],vert= 0,ticks= 0
// Slider slC,pos={109.00,73.00},size={621.00,17.00},proc=SliderProc
// Slider slC,limits={0,200,1},value= crsValues[2],vert= 0,ticks= 0
// Slider slD,pos={110.00,105.00},size={621.00,17.00},proc=SliderProc
// Slider slD,limits={0,200,1},value= crsValues[3],vert= 0,ticks= 0
// Slider slE,pos={113.00,130.00},size={621.00,17.00},proc=SliderProc
// Slider slE,limits={0,200,1},value= crsValues[4],vert= 0,ticks= 0
// Slider slF,pos={109.00,162.00},size={621.00,17.00},proc=SliderProc
// Slider slF,limits={0,200,1},value= crsValues[5],vert= 0,ticks= 0
// Slider slG,pos={108.00,197.00},size={621.00,17.00},proc=SliderProc
// Slider slG,limits={0,200,1},value= crsValues[6],vert= 0,ticks= 0
// Slider slH,pos={111.00,229.00},size={621.00,17.00},proc=SliderProc
// Slider slH,limits={0,200,1},value= crsValues[7],vert= 0,ticks= 0
// Slider slI,pos={112.00,258.00},size={621.00,17.00},proc=SliderProc
// Slider slI,limits={0,200,1},value= crsValues[8],vert= 0,ticks= 0
// Slider slJ,pos={111.00,285.00},size={621.00,17.00},proc=SliderProc
// Slider slJ,limits={0,200,1},value= crsValues[9],vert= 0,ticks= 0
//
// if(PolyNumber==3)
// Variable y0=0,A=0,invT=0
// Prompt y0, "Enter y0 value: " // Set prompt for x param
// Prompt A, "Enter A value: " // Set prompt for y param
// Prompt invT, "Enter invT value:"
// DoPrompt "Enter Values", y0, A, invT
// if (V_Flag)
// return -1 // User canceled
// endif
// K0 = y0;K1 = A;K2 = -invT;
// endif
//
do
// Cursor/A=1/W=$graphName A $traceName crsValues[0]
// Cursor/A=1/W=$graphName B $traceName crsValues[1]
// Cursor/A=1/W=$graphName C $traceName crsValues[2]
// Cursor/A=1/W=$graphName D $traceName crsValues[3]
// Cursor/A=1/W=$graphName E $traceName crsValues[4]
// Cursor/A=1/W=$graphName F $traceName crsValues[5]
// Cursor/A=1/W=$graphName G $traceName crsValues[6]
// Cursor/A=1/W=$graphName H $traceName crsValues[7]
// Cursor/A=1/W=$graphName I $traceName crsValues[8]
// Cursor/A=1/W=$graphName J $traceName crsValues[9]
// DoUpdate
maskWave = 0
//We will put '1' in the maskWave for the data points between the cursors
crs1 = CsrInfo(A); crs2 = CsrInfo(B)
//If the two cursors A and B are on the graph
if(cmpstr(crs1, "", 2) && cmpstr(crs2, "", 2))
maskWave[pcsr(A),pcsr(B)] = 1
endif
crs1 = CsrInfo(C); crs2 = CsrInfo(D)
//If the two cursors C and D are on the graph
if(cmpstr(crs1, "", 2) && cmpstr(crs2, "", 2))
maskWave[pcsr(C),pcsr(D)] = 1
endif
crs1 = CsrInfo(E); crs2 = CsrInfo(F)
//If the two cursors E and F are on the graph
if(cmpstr(crs1, "", 2) && cmpstr(crs2, "", 2))
maskWave[pcsr(E),pcsr(F)] = 1
endif
crs1 = CsrInfo(G); crs2 = CsrInfo(H)
//If the two cursors G and H are on the graph
if(cmpstr(crs1, "", 2) && cmpstr(crs2, "", 2))
maskWave[pcsr(G),pcsr(H)] = 1
endif
crs1 = CsrInfo(I); crs2 = CsrInfo(J)
//If the two cursors I and J are on the graph
if(cmpstr(crs1, "", 2) && cmpstr(crs2, "", 2))
maskWave[pcsr(I),pcsr(J)] = 1
endif
//We fit between the cursor position
//We fit in the regions of the mask = 1
if(Polynumber == 1)
CurveFit/Q line srcWave /M=maskWave /D=LineFit
elseif(Polynumber == 2)
CurveFit/Q poly 3, srcWave /M=maskWave /D=LineFit
endif
Wave W_Coef
if(PolyNumber == 1)
LineFit[] = W_Coef[0] + W_Coef[1]*pnt2x(LineFit, p)
elseif(Polynumber == 2)
LineFit[] = W_Coef[0] + W_Coef[1]*pnt2x(LineFit, p) + W_Coef[2]*pnt2x(LineFit, p)*pnt2x(LineFit, p)
endif
//sleep/B/S/C=2 1
DoUpdate
PauseForUser/C $graphName
while(saving==0)
if(saving)
DFREF srcDF= GetDataFolderDFR()
String SaveName = nameofwave(srcWave)+"_rmLin"
//We remove the linear contribution, centered around 0
//Then, at 0 the measured point does not change, only outside we remove the slope
Duplicate/O srcWave srcDF:$SaveName
Wave srcWave_rmLin = srcDF:$SaveName
if(Polynumber == 1)
srcWave_rmLin[] -= W_Coef[1]*pnt2x(srcWave_rmLin, p)
Note srcWave_rmLin "\rDrif-corrected with a degree 1 polynomial"
elseif(PolyNumber == 2)
srcWave_rmLin[] -= W_Coef[1]*pnt2x(srcWave_rmLin, p) + W_Coef[2]*pnt2x(srcWave_rmLin, p)*pnt2x(srcWave_rmLin, p)
Note srcWave_rmLin "\rDrif-corrected with a degree 2 polynomial"
endif
//We close the graph now
RemoveFromGraph/W=$graphName LineFit
KillControl SaveButton
Cursor/K/W=$graphName A
Cursor/K/W=$graphName B
Cursor/K/W=$graphName C
Cursor/K/W=$graphName D
Cursor/K/W=$graphName E
Cursor/K/W=$graphName F
Cursor/K/W=$graphName G
Cursor/K/W=$graphName H
Cursor/K/W=$graphName I
Cursor/K/W=$graphName J
HideInfo/W=$graphName
endif
KillWaves LineFit, rmLinContrib
return 0
End
Function SaveRmLinContribution(ctrlName) : ButtonControl
String ctrlName
NVAR saving
saving = 1
End
Function SliderProc(ctrlName,sliderValue,event) : SliderControl
String ctrlName
Variable sliderValue
Variable event // bit field: bit 0: value set, 1: mouse down, 2: mouse up, 3: mouse moved
Wave crsValues
if(event & 0x1) // bit 0, value set
if(!cmpstr(ctrlName, "slA"))
crsValues[0] = sliderValue
elseif(!cmpstr(ctrlName, "slB"))
crsValues[1] = sliderValue
elseif(!cmpstr(ctrlName, "slC"))
crsValues[2] = sliderValue
elseif(!cmpstr(ctrlName, "slD"))
crsValues[3] = sliderValue
elseif(!cmpstr(ctrlName, "slE"))
crsValues[4] = sliderValue
elseif(!cmpstr(ctrlName, "slF"))
crsValues[5] = sliderValue
elseif(!cmpstr(ctrlName, "slG"))
crsValues[6] = sliderValue
elseif(!cmpstr(ctrlName, "slH"))
crsValues[7] = sliderValue
elseif(!cmpstr(ctrlName, "slI"))
crsValues[8] = sliderValue
elseif(!cmpstr(ctrlName, "slJ"))
crsValues[9] = sliderValue
endif
endif
I tried to look to the benefits of the
Sleep
function, but actually this wasn't successful...Perhaps one of you have a better idea to achieve the desired result? I just want
1) To fit a line (or 2-degree polynomial) to a part of the data (selected by cursors, or sliders values)
2) Remove this fit from the raw data
3) Save the result in a new folder and wave name
Thank you very much for the kind advices you could provide.
Protra
You can read about this topic by calling:
DisplayHelpTopic "Window Hook Functions"
If you have questions or need specific examples, just keep asking here.
By the way, I would suggest you avoid putting everything in one function. Instead write specialized functions which prepare you working data (preferably in a separate folder), create the graph and its hook, clean-up stuff etc.
April 3, 2018 at 02:52 am - Permalink
April 3, 2018 at 09:38 am - Permalink
Oh Tony, this is just incredible. This is exactly what I need, I don't know how I did not find it before. Anyway, do you give me the permission to adapt the code to my needs? Mostly I will adapt it to execute your code in a loop where I loop on all the waves I have to remove the trend, and I do not need to remove the offset at point 0!
I will also take a look at the Window Hook, this is something I clearly never heard about.
Thank you both choso and tony!!
Protra
April 4, 2018 at 01:57 am - Permalink
of course, and I'm glad it's of use.
The baselines package has capability to loop over all the traces displayed in a graph window, so that may give you a start on the looping part.
April 4, 2018 at 08:54 am - Permalink