Declaring Global Variables Inside a Function?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Declaring Global Variables Inside a Function?

19 Jan 2017, 12:02

I'm not sure why this code isn't working. Instead of declaring all of my variables at the top of my script, I'm trying to declare them within a function. Admittedly this is entirely a convenience conceit for me - Notepad++ lets me collapse functions, so I don't have to scroll past my (rather large) list of global variables every time. For some reason though, it doesn't seem to be working. Here is (a shortened version of) my code.

Code: Select all

; Top of script
SetTimer, DoDeclareGlobals

DoDeclareGlobals
{
	global var1
	global var2
	global var3
	...
	SetTimer, DoDeclareGlobals, Off
}
When I attempt to reference a global variable in another function (specifically by making it the control variable for a GUI) the script fails because "a control's variable must be global". If I pull the variables outside of the function, it works fine.

Any idea what I am doing wrong?
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 12:11

works for me:

Code: Select all

DoDeclareGlobals()
MyFunc()
return

DoDeclareGlobals()
{
   global varr := "hi"
}

MyFunc()
{
   global varr
   MsgBox, %varr%
}
are you also declaring the varaiables as global in your other functions?

do you understand the difference between super global variables and regular global variables?

functions

User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 12:15

Code: Select all

DoDeclareGlobals
{
	global var1
	global var2
	global var3
	...
	SetTimer, DoDeclareGlobals, Off
}
This code does nothing.
Declaring global var1 inside a function means "In this function, var1 is the GLOBAL version of var1, not the LOCAL version of var1"

You are NOT declaring or creating a variable at all in this code.

Post some sample code and I will show you how to fix it.
Please try and keep your posted code as simple as possible - ie only enough code to demonstrate your problem.
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 12:40

That sounds like what I want to do - declare new super-global variables inside a function - just isn't possible. Many of my variables get used in several different functions, so if they aren't super-global I'd need a bunch of extra lines in each function importing them.
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 12:44

If you want to make a variable super-global, you do not do it inside the function, you do it outside the function.

Code: Select all

global var1 := "hello"	; prefixing a var with global, while already in global scope makes var super-global
MyFunc()
return

MyFunc(){
   msgbox % var1
}
As I said though, post some code that exhibits your issue. There is a much better way to do this, you do not need to use globals.
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 13:18

Okay, sure. There are currently two main things I'm using super-globals for - one is tracking the HWND of ~25 different controls because the program I am working with doesn't like to have consistent ClassNNs for its controls. That I doubt can be done without globals, unless I grab the HWND before referencing the control each time, and I'd rather just grab them all at once.

The other thing is because GUIs insist on having super-global variables for their controls. Here is a code snippet example of that:

Code: Select all

RunTimeCheck()
{
	TimeCheck := GetTimeSinceSold()
	if (TimeCheck > 45)
	{
		Gui, TimeCheckWindow: New
		Gui, -SysMenu
		Gui, Add, Text,, % "Item has not sold in " . TimeCheck . " days; continue?"
		Gui, Add, Button, gButtonClose Default, No
		fn := Func("IgnoreTime").Bind(PromoType)
		Gui, Add, Button, vTempVar1 X40 Y25, Yes
		GuiControl, +g, TempVar1, % fn
		Gui, Show
	}
	else
		PromptPromo(PromoType)
	return
}
With this code, if TempVar1 is not super-global, when this function executes it throws an error saying "a control's variable must be global or static" and ends the thread.
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 13:34

so just do global TempVar1 or static TempVar1 at the top of your function

gui variables do not need to be super global. only global or static, just like the msg tells you.

MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 14:48

You are correct, of course, and I meant to include that in my explanation - I basically decided to reuse a small set of super-global variables rather than declaring global variables inside each function with a GUI, mostly for readability reasons.

Honestly it annoys me a little bit that global variables defined inside a function are not super-global... but I suppose that mostly comes down to my insistence on using functions for everything instead of labels. :)
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 15:30

How to do it without any globals:

Uses the HWND (Unique window ID) for GUIs instead of a name.

Code: Select all

#SingleInstance force
RunTimeCheck()

RunTimeCheck()
{
	TimeCheck := GetTimeSinceSold()
	if (TimeCheck > 45)
	{
		Gui, New, hwndTimeCheckWindow
		Gui, -SysMenu
		Gui, Add, Text,, % "Item has not sold in " . TimeCheck . " days; continue?"
		Gui, Add, Button, gButtonClose Default, No
		fn := Func("IgnoreTime").Bind(PromoType)
		Gui, Add, Button, HwndTempVar1 X40 Y25, Yes
		GuiControl, +g, % TempVar1 , % fn
		Gui, Show
	}
	else
		PromptPromo(PromoType)
	return
}

GetTimeSinceSold(){
	return 50
}

PromptPromo(blah := ""){
}

IgnoreTime(blah := ""){
	ToolTip % A_TickCount ; Show tooltip to prove function is being called
}

ButtonClose:
	return
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 15:40

Worthy of mention is that a new gui would be created every time you call RunTimeCheck()

To stop that...

Code: Select all

#SingleInstance force
RunTimeCheck()

RunTimeCheck()
{
	static TimeCheckWindow := 0
	TimeCheck := GetTimeSinceSold()
	if (TimeCheck > 45)
	{
		if (!TimeCheckWindow)
			Gui, New, hwndTimeCheckWindow
		Gui, -SysMenu
		Gui, Add, Text,, % "Item has not sold in " . TimeCheck . " days; continue?"
		Gui, Add, Button, gButtonClose Default, No
		fn := Func("IgnoreTime").Bind(PromoType)
		Gui, Add, Button, HwndTempVar1 X40 Y25, Yes
		GuiControl, +g, % TempVar1 , % fn
		Gui, Show
	}
	else
		PromptPromo(PromoType)
	return
}

GetTimeSinceSold(){
	return 50
}

PromptPromo(blah := ""){
}

IgnoreTime(blah := ""){
	ToolTip % A_TickCount ; Show tooltip to prove function is being called
}

ButtonClose:
	return
But it looks like you alter the window depending on variables.
I would advise taking my code and expanding it - all you need to do is change the contents of the text box ("Item has not sold in x days") each time, you do not need to build a whole GUI
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 15:46

Like this:

Code: Select all

#SingleInstance force
RunTimeCheck()

RunTimeCheck()
{
	static TimeCheckWindow := 0
	TimeCheck := GetTimeSinceSold()
	if (TimeCheck > 45)
	{
		if (!TimeCheckWindow)
		{
			Gui, New, hwndTimeCheckWindow
			Gui, -SysMenu
			Gui, Add, Text, hwndTimeSince w200,
			Gui, Add, Button, gButtonClose Default, No
			fn := Func("IgnoreTime").Bind(PromoType)
			Gui, Add, Button, HwndTempVar1 X40 Y25, Yes
			GuiControl, +g, % TempVar1 , % fn
		}
		GuiControl, , % TimeSince, % "Item has not sold in " TimeCheck " days; continue?"
		Gui, % TimeCheckWindow ":Show"
	}
	else
		PromptPromo(PromoType)
	return
}

GetTimeSinceSold(){
	return 50
}

PromptPromo(blah := ""){
}

IgnoreTime(blah := ""){
	ToolTip % A_TickCount ; Show tooltip to prove function is being called
}

ButtonClose:
	return
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 16:21

Some of what you are doing there is not clicking for me. Oh... Is that code set up to only create the window once, in case it is called multiple times? Okay, that is clever. I'd have to stop destroying windows when cancel is clicked, I suppose. :P

If I understand correctly, you are assigning a hWND to the text field and then later using that hWND to populate the text field, instead of populating it when the GUI is created? Is the point of that to be able to change the text later if desired?

I see that I have apparently being handling GUIs very clumsily in a number of ways...
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 16:48

When you do Gui, Add, Button, HwndTempVar1 X40 Y25, Yes, it says "Create a button, and put it's hwnd in the variable TempVar1".
You can then use that hwnd just like you would a name, eg GuiControl, +g, % TempVar1 , % fn
So instead of you giving it a name, AHK gives it a name (it's hwnd), and you store that name in a variable.
I much prefer this, as it avoids having to have globals in your code. If you start doing more advanced coding with classes and stuff, it becomes incredibly useful.
You can use the same technique for Guis as well as controls.
Be aware that there is always one "Default" gui to start with (which is why you can just start a script with Gui, Add, Edit ...), so you do not have to do Gui, New to get your first gui.
Just use Gui, +HwndDefaultGui to get the hwnd of the default gui into the variable DefaultGui
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 17:19

Thanks, that is very helpful. I've got... a lot of guis to rewrite, but I do see the advantages. :)
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Declaring Global Variables Inside a Function?

19 Jan 2017, 18:22

This is a little example of how you can use classes to wrap GuiControls using this technique.

Simply create a new CEdit class, pass it a "bound function" callback to call when the value changes, and standard parameters as you would pass to an edit anyway, and it handles stuff for you :)

Code: Select all

#SingleInstance force

MyEdit1 := new CEdit(Func("EditChanged"), "w200", "Blah")

Gui, Add, Button, gTest, Click Me
Gui, Show
return

Test:
	MyEdit1.Set("Hello")
	return

EditChanged(value){
	Tooltip % Value
}

GuiClose:
	ExitApp

; CEdit Class - place in an #include or something.
Class CEdit {
	__New(callback, options := "", text := ""){
		this.callback := callback
		Gui, Add, Edit, % options " hwndhwnd", % text
		this.hwnd := hwnd
		fn := this.ValueChanged.Bind(this)
		GuiControl, +g, % this.hwnd, % fn
	}
	
	Get(){
		GuiControlGet, out, , % this.hwnd
		return out
	}
	
	Set(value){
		GuiControl, , % this.hwnd, % value
	}
	
	ValueChanged(){
		this.callback.call(this.Get())
	}
}
or, to get a bit more fancy...

Code: Select all

#SingleInstance force

MyEdit1 := new CEdit(Func("EditChanged").Bind("Edit 1"), "w200", "Type something")
MyEdit2 := new CEdit(Func("EditChanged").Bind("Edit 2"), "w200", "Anything")

Gui, Add, Button, gTest, Click Me
Gui, Show
return

Test:
	MyEdit1.Set("Hello")
	return

EditChanged(name, value){
	Tooltip % Name " changed to: " Value
}
lexikos
Posts: 9621
Joined: 30 Sep 2013, 04:07
Contact:

Re: Declaring Global Variables Inside a Function?

20 Jan 2017, 03:53

MaxAstro wrote:I basically decided to reuse a small set of super-global variables rather than declaring global variables inside each function with a GUI, mostly for readability reasons.
Why a "small" set? There is no reason that you would need more variables if they were not "super", only more declarations for the same variables (unless you use the assume-global mode, in which case you don't need any declarations).
Honestly it annoys me a little bit that global variables defined inside a function are not super-global...
There is a difference between a definition and a declaration. global var1 is just a declaration, var1 := 1 is an assignment which could be called a definition, and global var1 := 1 is both. It is only the declaration that needs to be either outside a function or inside every function which needs it. If the declaration is outside a function, the assignment can be anywhere (subject to control flow).

Declarations are not executed; they are read by the pre-parser (aka "compiler") before the script starts. As a result, they are not subject to control flow - i.e. they can go anywhere outside a function, even in a place that a command would never execute. For example:

Code: Select all

; Top of script
DoInitializeGlobals()
return  ; End of auto-execute section.

;... lots of other code...

MsgBox, This line is never executed.
global var1, var2, var3  ; This line takes effect despite never being executed.

DoInitializeGlobals()
{
	var1 := 42
	;... initialize variables as needed ...
}
Another way to think of it is that the declaration(s) and the variable are separate things. The declaration(s) controls where that particular name resolves to that particular variable. A declaration inside function X is only visible inside function X, so even though the variable exists globally, function Y will not access it by default. By contrast, a declaration outside any function is visible to all functions.

By the way, a function which consists solely of lines like global var1 and global var2 without any assignment won't have any effect when it is called, but its mere existence causes the variables to be created when the script starts. If your function only contains declarations, there really is no need for the function at all. (I guess your actual function contains some assignments, unlike what you showed in your first post.)
MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Declaring Global Variables Inside a Function?

20 Jan 2017, 11:38

lexikos wrote:Why a "small" set? There is no reason that you would need more variables if they were not "super", only more declarations for the same variables (unless you use the assume-global mode, in which case you don't need any declarations).
Correct. What it does take is more lines of code - I can declare a variable once as superglobal, or five or six times as regular global. It also runs the risk of me forgetting to import a variable. It probably would actually result in more variables overall, actually, because I'd probably end up using a different global in each function with a name appropriate to its use in that function, instead of the TempVar1/TempVar2/TempVar3 thing. Which I guess might be smarter anyway, but... xD
lexikos wrote:There is a difference between a definition and a declaration. global var1 is just a declaration, var1 := 1 is an assignment which could be called a definition, and global var1 := 1 is both. It is only the declaration that needs to be either outside a function or inside every function which needs it. If the declaration is outside a function, the assignment can be anywhere (subject to control flow).

Declarations are not executed; they are read by the pre-parser (aka "compiler") before the script starts. As a result, they are not subject to control flow - i.e. they can go anywhere outside a function, even in a place that a command would never execute.

Another way to think of it is that the declaration(s) and the variable are separate things. The declaration(s) controls where that particular name resolves to that particular variable. A declaration inside function X is only visible inside function X, so even though the variable exists globally, function Y will not access it by default. By contrast, a declaration outside any function is visible to all functions.
That's all helpful to know. At the very least, it helps me understand why you can't declare a super-global inside a function.
lexikos wrote:By the way, a function which consists solely of lines like global var1 and global var2 without any assignment won't have any effect when it is called, but its mere existence causes the variables to be created when the script starts. If your function only contains declarations, there really is no need for the function at all. (I guess your actual function contains some assignments, unlike what you showed in your first post.)
I understand that. As I said initially, the only reason I want to put it in a function is because Notepad++ allows me to collapse functions, so I wouldn't have to scroll past all my globals when browsing my script. Obviously that isn't possible, so I'll just live with the minor inconvenience. :)
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: Declaring Global Variables Inside a Function?

20 Jan 2017, 12:22

MaxAstro wrote: As I said initially, the only reason I want to put it in a function is because Notepad++ allows me to collapse functions, so I wouldn't have to scroll past all my globals when browsing my script. Obviously that isn't possible, so I'll just live with the minor inconvenience. :)
that seems like a poor reason, but depending on your NP++ collapse feature, you could just wrap the globals in a redundant {block}:

Code: Select all

{                                ; unnecessary but used for editor highlighting/collapse
global varr := "hi"
}
MyFunc()
return

MyFunc()
{
   MsgBox, %varr%
}
rather than mucking up your code, the real solution is to use a better editor that allows you to collapse whatever you want manually. that shouldve been the first and only answer

MaxAstro
Posts: 557
Joined: 05 Oct 2016, 13:00

Re: Declaring Global Variables Inside a Function?

20 Jan 2017, 12:29

Oddly, putting it in a redundant block ALSO causes the variables to not be super-global.

Can you recommend a good editor? I've used NPP for a while, but a couple things about it do bother me - one is that the way themes are set up means I can't use themes while preserving AHK-appropriate color coding, and another is that it is extremely aggressive when it comes to autocomplete (it will try to autocomplete variable names using option strings or silly things like that... for that matter, it tries to autocomplete my comments as variable names...).
guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: Declaring Global Variables Inside a Function?

20 Jan 2017, 12:43

MaxAstro wrote:Oddly, putting it in a redundant block ALSO causes the variables to not be super-global.
yes it does. did you try my code?
MaxAstro wrote: Can you recommend a good editor? I've used NPP for a while, but a couple things about it do bother me - one is that the way themes are set up means I can't use themes while preserving AHK-appropriate color coding, and another is that it is extremely aggressive when it comes to autocomplete (it will try to autocomplete variable names using option strings or silly things like that... for that matter, it tries to autocomplete my comments as variable names...).
editors are wildly personal and will open up a can of worms. but its the one program that you will use for the rest of your computing lifetime, so you might as well find one that is extremely customizable to all of your needs. as you bump into limitations as you have, you either want to look up how you can solve them in your current editor, or look for a new editor

i use Vim as my editor, but the learning curve is very large. my current folding/collapse method is to collapse on any indentation. in Vim, the command to close a fold is zc and to open it is zo. so in the following example, all i need to do is indent the globals at the start and then i can use them as a fold. alternatively, you see that i can change my folding method to 'manual', which allows me to mark my own fold section with zf:

http://imgur.com/a/pSZ3f

i do like the idea of indenting the auto exectute section and i may adopt that


Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: No registered users and 151 guests