Managing graph-updates in a user-defined function

Protra
Protra's picture
Posts: 6
Joined: 2014-10-02
Location: Switzerland

Hello everybody,

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

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


chozo
Posts: 258
Joined: 2010-05-14
Location: Japan

What you are looking for is a Window Hook, i.e., a link to an user function which is called every time something is changed in a (graph or other) window. You create link to a function which selects the action to take depending on the modification which occurred (for example, call UpdateBackgroundFit() after a Cursor has been moved). From there you can chain all desired events (subtraction, saving) together as needed.

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.


tony
Posts: 162
Joined: 2007-08-28
Location: Canada

An alternative to using cursors is to use the graph marquee to select regions to add or subtract from a maskwave. The fit can be updated from the marquee menu, no need for a hook function. That's what I do in the baselines package: http://www.igorexchange.com/project/Baselines


Protra
Protra's picture
Posts: 6
Joined: 2014-10-02
Location: Switzerland

tony wrote:
An alternative to using cursors is to use the graph marquee to select regions to add or subtract from a maskwave. The fit can be updated from the marquee menu, no need for a hook function. That's what I do in the baselines package: http://www.igorexchange.com/project/Baselines

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


tony
Posts: 162
Joined: 2007-08-28
Location: Canada

Protra wrote:
do you give me the permission to adapt the code to my needs?

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.


Back to top