GUI with external parameters

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
tt_1111
Posts: 16
Joined: 15 Aug 2015, 11:25

GUI with external parameters

03 Aug 2020, 23:07

I'd like to construct a customizable message box with up to three buttons.

The text of these buttons should be passed over to the script as external parameters, equally the title of the form and a little explanatory text.

Makes up a total of up to 5 parameters.

The beginning of the form is easy an well documented — checking the number of parameters passed to the script:

Code: Select all

ParAnz := A_Args.Length()
MsgBox Parameteranzahl = %ParAnz%
if A_Args.Length() < 3
{
[indent=2]MsgBox % "The script needs at least 3 parameters, but only " A_Args.Length() " were delivered."
    ExitApp[/indent]}
if A_Args.Length() > 5
{
[indent=2]MsgBox % "The scripts needs a maximum of 5 parameters, but " A_Args.Length() " were delivered."
    ExitApp[/indent]}

;
; Explanatory text in red
Gui, Font, s12 cRed, Verdana
Gui, Add, Text, x25 y15, %2%
; Switching back to black text
Gui, Font, s10 cBlack, Verdana

However adding custom buttons (with custom actions attached) seems to be a bit more difficult.

The following construction does not work

Code: Select all

Gui, Add, Button, x25 y50 w85 g%3%, %3%
%3%:
Msgbox, You clicked '%3%'.
;  run, (CustomAction3)
  ExitApp
Return

Already the first line

Code: Select all

Gui, Add, Button, x25 y50 w85 [u]g%3%[/u], %3%
returns an error. Apparently the GoSub instruction 'g%n%' isn't possible this way.

The slightly mitigated following form returns the same error (%n% is the running variable within a loop):

Code: Select all

Gui, Add, Button, x25 y50 w85 gsub%n%, %3%

The mildest (and very clumsy to program) form finally gives the expected output:

Code: Select all

if if A_Args.Length() = 5
{
[indent=2]  Gui, Add, Button, x25 y50 w85 gsub1, %p3%
  Gui, Add, Button, x+20 y50 w85 gsub2, %p4%
  Gui, Add, Button, x+20 y50 w85 gsub3, %p5%
  Gui, Show, w380 h90, %p1%[/indent]}

All three buttons are shown.

But: Is there any chance to pass the parameters (text) to use in the form as external parameters within a little loop? Or does it really have to be

Thanks in advance for any hints!
Last edited by tt_1111 on 11 Aug 2020, 00:05, edited 1 time in total.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: GUI with external parameters

04 Aug 2020, 06:16

Code: Select all

; Code etwas verschlankt. Cosmetics.

MsgBox % "Parameteranzahl = " A_Args.Length()

msg :=	if (A_Args.Length() < 3) ? "The script needs at least 3 parameters, but only " A_Args.Length() " were delivered."
	:	if (A_Args.Length() > 5) ? "The scripts needs a maximum of 5 parameters, but " A_Args.Length() " were delivered."
	:	""
MsgBox % msg
ExitApp

; Explanatory text in red
Gui, Font, s12 cRed, Verdana
Gui, Add, Text, x25 y15,% 2
; Switching back to black text
Gui, Font, s10 cBlack, Verdana

Code: Select all

3 := "Test"

Gui, Add, Button, x25 y50 w85 g %3%,%3%		; legacy style
Gui, Add, Button,% "x25 y50 w85 g" %3%,% 3	;'forced expression'-style

%3%:
	Msgbox % "You clicked " 3 "."
	; run, (CustomAction3)
	Return

Code: Select all

if (A_Args.Length() = 5) {
Gui, Add, Button,% "x25		y50 w85 		g" %1%	,% 3
Gui, Add, Button,% "x+20	y50 w85 		g" %2%	,% 4
Gui, Add, Button,% "x+20	y50 w85 		g" %3%	,% 5
Gui, Show		,% "			w380	h90"		,% 1
}
HTH. Frohes Gelingen :)
A_AhkUser
Posts: 1147
Joined: 06 Mar 2017, 16:18
Location: France
Contact:

Re: GUI with external parameters

04 Aug 2020, 09:55

Hi,

Code: Select all

#NoEnv
#SingleInstance force

ParAnz := A_Args.Length()
; if (ParAnz < 3)
; {
	; MsgBox % "The script needs at least 3 parameters, but only " ParAnz " were delivered."
	; ExitApp
; } else if (ParAnz > 5) {
	; MsgBox % "The scripts needs a maximum of 5 parameters, but " ParAnz " were delivered."
	; ExitApp
; }

GUI, Margin, 10, 10
GUI, Add, Text, xm ym, My custom message box text.
Loop % ParAnz {
	GUI, Add, Button, xp+100 y100 w85 gmyLabel, % A_Args[ a_index ]
	; GUI, Add, Button, xp+100 y100 w85 gmyFunction, % A_Args[ a_index ]
}
; for each, argument in A_Args {
	; GUI, Add, Button, xp+100 y100 w85 gmyLabel, % argument
	; GUI, Add, Button, xp+100 y100 w85 gmyFunction, % argument
; }
GUI, Show, AutoSize
return

myLabel:
	Gui, +OwnDialogs
	Msgbox % "You clicked " . A_GuiControl . "."
return
; myFunction() {
	; Gui, +OwnDialogs
	; Msgbox % "You clicked " . A_GuiControl . "."
; }
or, more complicated:

Code: Select all

#NoEnv
#SingleInstance force


GUI, Margin, 10, 10
GUI, Add, Text, xm ym, My custom message box text.
for each, argument in A_Args {
	fn := Func("myFunction").bind(argument) ; https://www.autohotkey.com/docs/objects/Functor.htm#BoundFunc
	GUI, Add, Button, xp+100 y100 w85 vmyVar%a_index%, % argument
	GuiControl, +g, myVar%a_index%, % fn ; https://www.autohotkey.com/docs/commands/GuiControl.htm#options
}
GUI, Show, AutoSize
return

myFunction(argument, hControl, GUIEvent, EventInfo, ErrLevel:="") { ; https://www.autohotkey.com/docs/commands/Gui.htm#label
	Msgbox % "You clicked " . argument . " (var: " . A_GuiControl " | hwnd: " hControl . ")."
}
Hope this helps

A_AhkUser
my scripts
tt_1111
Posts: 16
Joined: 15 Aug 2015, 11:25

Re: GUI with external parameters

07 Aug 2020, 02:00

First and foremost: Thanks for your answers!

@ Bobo:

Is there any documentation about the PERL or PHP like 'code slimming'?
if (condition) ? do if yes : do if no


I don't find anything in the standard AHK documentation, and at least it doesn't seem to work when putting something more complicated into the 'if' or the 'else' branch — e.g. a button with or without default attribute?!

@ A_AhkUser:

Do I get it right that it is a bit difficult to determine the number of the button pressed?
At least I don't find an A_GUI... parameter containing that info.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: GUI with external parameters

07 Aug 2020, 03:50

Is there any documentation about the PERL or PHP like 'code slimming'?
if (condition) ? do if yes : do if no
:arrow: ternary operator

... and at least it doesn't seem to work when putting something more complicated into the 'if' or the 'else' branch — e.g. a button with or without default attribute?!
The ternary operator expects expression style. Eg, you can call full-blown functions if a condition has (not) been met ...

Code: Select all

MsgBox % msg := (var = true) ? funcA() : funcB()
Return

funcA() {
    SoundBeep
    Return "AutoHotkey rocks your sox!"
    }

funcB() {
    MsgBox % "The following message has been approved by BoBo himself ..."
    Return "Go out and vote on Nov 3rd, 2020!"
    }

No, it's not that difficult to identify a buttons specific "value". Simply assign a variable to it and request its content ...

Code: Select all

3 := "Test"

Gui, Add, Button, x25 y50 w85 vBtn gBtn, %3%
Gui, Show,,% chr(32)
Return

Btn:
    GuiControlGet, res,, Btn
    MsgBox % "You've pressed " res
    Return
    
tt_1111
Posts: 16
Joined: 15 Aug 2015, 11:25

Re: GUI with external parameters

09 Aug 2020, 20:55

Hmm — if I add buttons within a loop I can't assign the same variable to all buttons; this returns a direct error during script interpreting.

Whereas when putting them e.g. into an array the script doesn't know which element to ask for.

This also holds true when I don't use gsub but rather gfunc as suggested by A_AhkUser. I can't hand the parameter to the function within the 'GUI, Add Button' statement as this returns a 'Target label does not exist' during interpreting.

The only thing that comes into my mind right now is a kind of 'reverse lookup' of the input. Described e.g. in
https://www.autohotkey.com/boards/viewtopic.php?t=23286
[2nd post by jNizM, HasVal(haystack, needle)]

Not exactly eloquent. But at least working with buttons added within a loop and only one sub/function to define; thus definitely shorter and more elegant than my clumsy first approach.
just me
Posts: 9574
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: GUI with external parameters

10 Aug 2020, 03:49

A simple solution (not tested) using predefined button variables and labels:

Code: Select all

#NoEnv
ParAnz := A_Args.Length()
MsgBox Parameteranzahl = %ParAnz%
If (ParAnz < 3)
{
   MsgBox % "The script needs at least 3 parameters, but only " ParAnz " were delivered."
   ExitApp
}
If (ParAnz > 5)
{
   MsgBox % "The scripts needs a maximum of 5 parameters, but " ParAnz " were delivered."
   ExitApp
}
;
; Explanatory text in red
;
Gui, Font, s12 cRed, Verdana
Gui, Add, Text, x25 y15, % A_Args[2]
Gui, Font, s10 cBlack, Verdana ; switching back to black text
;
; Additional buttons
;
Loop, % (ParAnz - 2)
{
   X := (A_Index = 1) ? "x25" : "x+20"
   Gui, Add, Button, x%X% y50 w85 vBtn%A_Index% gSub%A_Index%, % A_Args[A_Index + 2]
}
;
; Show the GUI
;
Gui, Show, w380 h90, % A_Args[1]
Return

GuiClose:
ExitApp
;
; Button labels
;
Sub1:
   GuiControlGet, Caption, , %A_GuiControl%
   MsgBox, 0, %A_ThisLabel%, You clicked on button %A_GuiControl% with caption %Caption%!
Return
Sub2:
   GuiControlGet, Caption, , %A_GuiControl%
   MsgBox, 0, %A_ThisLabel%, You clicked on button %A_GuiControl% with caption %Caption%!
Return
Sub3:
   GuiControlGet, Caption, , %A_GuiControl%
   MsgBox, 0, %A_ThisLabel%, You clicked on button %A_GuiControl% with caption %Caption%!
Return
You may also use a shared label.
tt_1111
Posts: 16
Joined: 15 Aug 2015, 11:25

Re: GUI with external parameters

11 Aug 2020, 00:00

Yep, the last one should work.

Here is my final (so far) solution — a completely customizable message box with up to 12 buttons (arranged vertically if 4 or more). The buttons are added within a loop, and the scripts needs only one single subroutine to capture the button clicked. Results are written to the Windows registry so they can be easily queried by almost any scripting language (DOS batch, Powershell, VB(A), AHK, ...) and a bunch of other programs.

As the text of the buttons is freely choosable it makes no sense to use the numbering convention used for e.g. the standard AHK and VB message boxes.

Not implemented is the standard MsgBox capability to add a little icon (hand, question mark, exclamation mark, asterisk). Of course it could be easily added using an additional input parameter and an extra line to the box' header:

Code: Select all

GUI, Add, Picture, x5 y15 w15 h15, % p[5]
and some according adjustments (5 parameters instead of 4 before the actual buttons begin).

The script is tested and works as far as I can evaluate it.

A few MsgBox statements are commented out and serve as means of debug if required.

Good luck in using it ...

Code: Select all

#SingleInstance Force
#NoEnv
SetRegView 64

; Completely customizable MessageBox
; p1 = %1%	; Window title
; p2 = %2%	; Header
; p3 = %3%	; Standard button
; p4 = %4%	; Timeout
; p5 = %5%	; Text1
; p6 = %6%	; Text2
; p7 = %7%	; Text3
; ...
; 4 or more buttons will be arranged vertically


; № of parameters
NoPar := A_Args.Length()
; № of actual buttons
NoBut := Round(NoPar-4,0)

; Array for input parameters
p := []

count := 0
Loop, %0%	; For each parameter:
{
	count += 1
	param := %A_Index%	; Fetch the contents of the variable whose name is contained in A_Index.
	p[count] := param
	; MsgBox, ,Parameter, Parameter no %A_Index% is %param%.
}

; Precautionally convert no of standard button and timeout to numbers
p[3] += 0
p[4] += 0

; MsgBox % NoPar . "`n" . p[1] . "`n" . p[2] . "`n" . p[3] . "`n" . p[4] . "`n" . p[5] . "`n" . p[6] . "`n" . p[7] . "`n" . p[8] . "`n" . p[9] . "`n" . p[10] . "`n" . p[11] . "`n" . p[12] . "`n" . p[13] . "`n" . p[14] . "`n" . p[15] . "`n" . p[16] . "`n" . p[17] . "`n" . p[18] . "`n" . p[19] . "`n" . p[20]

if (NoPar < 5)
{
	MsgBox % "The script needs at least 5 parameters, but only " . NoPar . " are present."
	ExitApp
}
if (A_Args.Length() > 16)
{
	MsgBox % "More than 12 choices aren't envisaged. But " NoBut " are present!"
	ExitApp
}

; Header (button explanation) in red
Gui, Font, s12 cRed, Verdana Bold
Gui, Add, Text, x25 y15, % p[2]
Gui, Font, s10 cBlack, Verdana

; Buttons
if (NoPar < 8)
{
	; Max 3 buttons => horizontal arrangement
	subInd := 0
	xStart := 25
	Loop, % NoBut
	{
		x1 := xStart+(subInd*135)
		subInd += 1
		actInd := Round(subInd+4, 0)
		; Default Button
		dFlt := (subInd = p[3]) ? "Default" : ""
		; MsgBox % "subInd: " . subInd . "`nInd: " . actInd . "`nx1: " . x1
		GUI, Add, Button, % dflt " x" x1 " y50 w120 h40 gmySub", % p[actInd]
		GUI, Show, Autosize, % p[1]
	}
}
else if ((NoPar >= 8) && (NoPar < 17))
{
	; More than 3 and a maximum of 12 buttons => vertical arrangement
	subInd := 0
	yStart := 50
	Loop, % NoBut
	{
		y1 := yStart+(subInd*58)
		subInd += 1
		actInd := Round(subInd+4, 0)
		; Default Button
		dFlt := (subInd = p[3]) ? "Default" : ""
		; MsgBox % "subInd: " . subInd . "`nInd: " . actInd
		GUI, Add, Button, % dFlt " x25 y" y1 " w120 h40 gmySub", % p[actInd]
		GUI, Show, Autosize, % p[1]
	}
}

; Timeout
if (p[4] > 1)
{
	SetTimer, ExitAHK, % p[4]
}
return


;====================
; Subs and functions
;====================

mySub:
	myAction := A_GuiControl
	; Reverse look-up of the parameter № of the button clicked
	NoDirect := HasVal(p, myAction)
	; Calculating the № of the button (subtract 4 leading input parameters)
	NoCorr := NoDirect-4
	; MsgBox % "You clicked: " . myAction . ".`nIndex: " . NoCorr
	; Writing results to Windows registry
	RegWrite, Reg_SZ, HKEY_LOCAL_MACHINE, System\ScriptEngine, MsgOut, % NoCorr
	RegWrite, Reg_SZ, HKEY_LOCAL_MACHINE, System\ScriptEngine, Action, % myAction
	Gui, Destroy
return

ExitAHK:
	; GUI run into Timeout
	RegWrite, Reg_SZ, HKEY_LOCAL_MACHINE, System\ScriptEngine, MsgOut, 98
	RegWrite, Reg_SZ, HKEY_LOCAL_MACHINE, System\ScriptEngine, Action, Timeout
	ExitApp
Return


HasVal(haystack, needle)
{
	; Returns the index of the last found item ('needle') within in the array 'haystack' — 
	; or 0 if nothing was found, 'haystack' is no object or empty 
	if !(IsObject(haystack)) || (haystack.Length() = 0)
	{
		return 0
	}
	for index, value in haystack
	{
		if (value = needle)
		{
			return index
		}
	}
	return 0
}
Last edited by tt_1111 on 11 Aug 2020, 19:57, edited 2 times in total.
tt_1111
Posts: 16
Joined: 15 Aug 2015, 11:25

Re: GUI with external parameters

11 Aug 2020, 05:21

And here's another one — supercool graphical alternative for old DOS' choice this time.

It needs a little pix (usually the app's icon) for each choice and one for the background; preferably as *.png as they can hold a transparent background. The rest is quite equivalent to the button selection script in the post above. Like this it basically accepts any (even) number of parameters; lines are added within a loop and only one single sub (Label) section is sufficient to capture any input.

It's implemented as a variadic function (№ of parameters may vary) this time to be called by any other AHK script via an #Include statement.
I haven't put in much error catching; if the input params are wrong (e.g. no pix) you instantly see a strange result.

An example of the result is attached as well as the images used for it. Hope you don't mind the German realization of the example; the concept and realization are clearly visible. Have fun!


graphical-choice.jpg
graphical-choice.jpg (42.33 KiB) Viewed 2069 times

Code: Select all

#NoEnv
#SingleInstance force
SetRegView 64

global PixPath := "C:\Program Files\Tweaks\AutoHotkey\Pix"	; Path to used images
global count := 0									; Running variable in different loops
global p := []										; Array of function parameters

; Clear registry entries later used for stroing results
RegWrite, Reg_SZ, HKEY_LOCAL_MACHINE, System\ScriptEngine, MsgOut,
RegWrite, Reg_SZ, HKEY_LOCAL_MACHINE, System\ScriptEngine, Action,

; 
CustomMsgBox(Params*)
{
	; Parameters
	; global p1 := Fenstertitel
	; global p2 := Ueberschrift
	; global p3 := Text1
	; global p4 := Pix1
	; global p5 := Text2
	; global p6 := Pix2
	; global p7 := Text3
	; global p8 := Pix3
	; ...
	
	NoPar := Params.MaxIndex()
	; MsgBox % "NoPar: " . NoPar
	
	msg :=	if (NoPar < 4) ? "The script needs at least 4 parameters, but only " %NoPar% " are present."
		:	if (Mod(NoPar, 2) > 0 ) ? "The script needs an even number of parameters, but " %NoPar% " are present."
	if (msg != "")
	{
		MsgBox % msg
	}
	
	Htot := % NoPar * 40
	; MsgBox % "Htot: " . Htot
	count := 0
	
	for index,param in Params
	{
		count += 1
		p[index] := param
		; MsgBox % p[index] . " | " . param . " | " . Params[index]
	}

	; MsgBox % p[1] . "`n" . p[2] . "`n" . p[3] . "`n" . p[4] . "`n" . p[5] . "`n" . p[6] . "`n" .p[7] . "`n" . p[8] . "`n" . p[9] . "`n" . p[10] . "`n" . p[11] . "`n" . p[12]
	
	; Caption + header
	hPix := Htot-55
	GUI, Add, Picture, % "x0 y55 w425 h" hPix, %PixPath%\Background.png
	GUI, font, s23 cRed, Verdana Bold
	GUI, Add, Text, x20 y10 +BackgroundTrans, % p[2]
	
	; Buttons + pix
	GUI, font, s20 cWhite, Verdana Bold
	
	subInd := 0
	rowMax := Round((NoPar-2)/2, 0)	; 2 leading parameters defining caption and header +  each choice needs 2 parameters
	Loop, % rowMax
	{
		subInd += 1
		ParCount := Round((2*subInd)+1, 0)
		ind1 := ParCount
		ind2 := ParCount + 1
		y1 := subInd * 80
		y2 := y1 - 10
		; MsgBox % "subInd: " . subInd . "`nCount: " . ParCount . "`nInd1: " . ind1  . "`nInd2: " . ind2 . "`ny1: " . y1 . "`ny2: " . y2
		GUI, Add, Text, % "x20 y" y1 " +BackgroundTrans", % p[ind1]
		GUI, Add, Picture, % "x310 y" y2 " w55 h55 +BackgroundTrans gWriteReg", % PixPath "\" p[ind2]
	}
	
	Gui, Show, % "w425 h" Htot, % p[1]
	
	return
	
	
	WriteReg:
		Gui, +OwnDialogs
		SplitPath, A_GuiControl, FileName
		NrDirect := HasVal(p, FileName)
		NrKorr := NrDirect-1
		NameKorr := p[NrKorr]
		NrLine :=  Round((NrDirect-2)/2, 0)
		; MsgBox % "Angeklickt wurde: " . NameKorr . ".`nIndex: " . NrLine
		RegWrite, Reg_SZ, HKEY_LOCAL_MACHINE, System\ScriptEngine, MsgOut, % NrLine
		RegWrite, Reg_SZ, HKEY_LOCAL_MACHINE, System\ScriptEngine, Action, % NameOnly
		Gui, Destroy
	return
}

HasVal(haystack, needle)
{
	; Returns the index of the last found object (or 0)
	if !(IsObject(haystack)) || (haystack.Length() = 0)
	{
		return 0
	}
	for index, value in haystack
	{
		if (value = needle)
		{
			return index
		}
	}
	return 0
}
Attachments
Pix.zip
(1.59 MiB) Downloaded 26 times

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Bing [Bot] and 303 guests