Need some help on learning GUI object handling

Hi guys, I'm learning how to handle with GUI objects, in particular the toggle buttons, but there's something I'm missing.
I've studied the manual, the tutorial and some forum posts without finding my answers.

The idea I'm working on is really simple: I would like to have 2 toggles, one to plot the MA20 and one to plot the MA100 of the close prices. My questions are:

  1. If I open 2 or more scripts at the same time, then they became linked due the fact their function point to the same static vars. What is in this case the best way to handle multiple contemporary instances of the same interface?

  2. To learn how to handle queued events I've added a fake procedure that's time demanding. I'm expecting that it slowes down the script only when an event occurs, but I'm experiencing that it alwasy slow down my code: for example when I move the bar line indicator without changing the price window. Why this beahavior?

This is the test code:

//	GUI_test_2.afl
delay = Param("Time intensive function's iterations", 0, 0, 20, 1);

PlotOHLC(O,H,L,C, "", colorBlack, styleBar);
SetChartOptions(0, chartShowDates);

idToggle1 = 1;
idToggle2 = 2;

procedure createGUI()
{
	GuiToggle("TGGL 1", idToggle1, 20, 35, 100, 50, notifyClicked);
	GuiToggle("TGGL 2", idToggle2, 120, 35, 100, 50, notifyClicked);
}

procedure initGUI()
{
	if (StaticVarGet("toggle1Status") == 1)
		GuiSetCheck(idToggle1, 1);
	if (StaticVarGet("toggle1Status") == 0)
		GuiSetCheck(idToggle1, 0);
	if (StaticVarGet("toggle2Status") == 1)
		GuiSetCheck(idToggle2, 1);
	if (StaticVarGet("toggle2Status") == 0)
		GuiSetCheck(idToggle2, 0);
}

procedure handleEvents()
{
	newvalue = 0;
	for ( n = 0; id = GuiGetEvent(n, 0); n++) // il 2° parametro = 0 => restituisce l'ID del controllo.
	{
		code = GuiGetEvent(n, 1); // il 2° parametro = 1 => restituisce il notification code.
		switch (id)
		{			
			case idToggle1:
			{
			value = StaticVarGet("toggle1Status", True);
			if (value == 0) 
				newvalue = 1;
			if (value == 1) 
				newvalue = 0;
			
			StaticVarSet("toggle1Status", newvalue, False, False);
			text = "Toggle 1 value = " + NumToStr(newvalue) + "    Delay = " + NumToStr(delay, 1.0);
			PopupWindow(text, "TEST TGGL1", 1,-1, -1, -1, -1, True);
			break;
			}
			
			case idToggle2:
			{
			value = StaticVarGet("toggle2Status", True);
			if (value == 0) 
				newvalue = 1;
			if (value == 1) 
				newvalue = 0;
			
			StaticVarSet("toggle2Status", newvalue, False, False);
			text = "Toggle 2 value = " + NumToStr(newvalue) + "    Delay = " + NumToStr(delay, 1.0);
			PopupWindow(text, "TEST TGGL2", 1,-1, -1, -1, -1, True);
			break;
			}
			
			default:
				break;
		}
	}
}

procedure timeIntensiveFakeProcedure(iterations)
{
	for (i = 0; i <= iterations; i++)
	{
		for (bi = 0; bi <= BarCount-1; bi++)
		{
			handle = fopen("test.csv", "w", False);
			fputs(NumToStr(C[bi], 1.3), handle);
			fclose(handle);
		}
	}
}


createGUI();
initGUI();
handleEvents();

//	Toggle 1
tggl1Val = StaticVarGet("toggle1Status");
if (tggl1Val)
{
	Plot(MA(C,20), "MA20", colorRed, styleLine|styleThick);
	timeIntensiveFakeProcedure(delay);
}
//	Toggle 2
tggl2Val = StaticVarGet("toggle2Status");
if (tggl2Val)
{
	Plot(MA(C,100), "MA100", colorBlue, styleLine|styleThick);
	timeIntensiveFakeProcedure(delay);
}

Thank you in advance for any insight !

If all you're after is plotting the 2 MAs and being able to turn it on/off, why not use ParamToggle instead?

showMA20 = ParamToggle("Show 20 MA", "No|Yes", 0);
showMA100 = ParamToggle("Show 100 MA", "No|Yes", 0);

if (showMA20) Plot(MA(C,20), "MA20", colorRed, styleLine|styleThick);
if (showMA100) Plot(MA(C,100), "MA100", colorBlue, styleLine|styleThick);

To do it with the GUI buttons, you've got to learn about StaticVarGet/Set and how that works. Then also learn about GUI events and how they are not really events but just an array that is read when you poll it. It can be done, but requires a lot more effort to setup as you're finding out.

1 Like

Hi mordrax, thank you for your reply and your coding.

My scope is not to toggle 2 MA which, as you posted, can be done pretty straightforwardly using the ParamToggle function;

the code I've posted is a kind of work bench instead, that should help me to understand how to efficiently implement GUIs. In other words my aim is to learn how to implement GUIs.

The way that I do this is to read the script name that is executing the function.

tggl1Val = StaticVarGet("toggle1Status");
scriptName = getFilename(GetFormulaPath()); // getFileName() is a function that gets your script name

if (scriptName == "myScript.afl" && tggl1Val) { ... }

This way, I can control which script will run based on the static var. You can also use this to control which script will show the GUI.

In your code, whenever tggl1Val or tggl2Val is true, your time intensive function runs.
When you have a chart visible in amibroker, it will periodically run with an action of actionIndicator to refresh your chart. ( see actionEx AFL Function Reference - STATUS )
Each time this runs, if either of your toggle is true, then it'll hit timeIntensiveFakeProcedure for each true toggle.

To see this, try putting this somewhere _TRACE("Running scripts again...") and use window's debug view to see your formula run over and over again: DebugView - Windows Sysinternals | Microsoft Learn
This is why when either of your buttons is true, your whole amibroker app will slow down.

2 Likes

@ 2DD, if you apply the formula to different charts, add a suffix to all your static variables using the GetChartID() function.
This way, each graph will have its own "independent" static variables.
Look for examples in the forum.

1 Like

Thank you mordrax, I didn't understand that Amibroker periodically refresh my chart without any new data or input. I indeed have to study the status and actionEx.
Your workaround to read the script I think that forces me to have different scripts for different instances which could be quite messy.
Thank you for the suggestion of using DebugView, I didn't used it until now due the fact using AFL internal debug tool and exploration I always get out my issues, but I'm feeling that now I have to make that step forward.

Thank you Beppe, great suggestion I missed that function. I think this solves a great part of my issues related to the instances. Going to study now...

Yeah, I used the step through debugger for a week or so when I started ( I'm still just a newbie ) but soon went all out on logging, it's a much much faster way of coding and debugging.

One of the things I'm doing is setting a global constant on each of the script that I run and so for each pane/window ( I only use 1 pane per window to keep things simple ), it gets it's own custom set of code.

This allows me to say for instance, only show the UI on one window or only show my trades on the lower t/fs etc... Your UI code will run on all windows as well if you don't stop it from doing this, it's not a massive issue but just be aware each chart window executes fairly often, esp if you have IB or some plugin connected that updates your chart.

image

G'luck!!!

1 Like

Thank you mordrax this is very interesting and gives me some good ideas on how to arrange workflow. Nice