Reformatting my script from v1 to v2

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Reformatting my script from v1 to v2

13 Mar 2024, 20:43

Going through an extensive v1 script and I'm mostly using the v2 debugger to fix things, but I'm confused about how to reformat the following line:

Code: Select all

Window := % "ahk_id" . WindowList%A_Index%
In fact, I'm confused about how to convert any bit of code that used the single % to denote the rest of the line as pulled variable values.
Last edited by chinagreenelvis on 14 Mar 2024, 19:30, edited 1 time in total.
User avatar
mikeyww
Posts: 27198
Joined: 09 Sep 2014, 18:38

Re: Reformatting % lines v1 to v2

13 Mar 2024, 21:03

Your statement shows an example of how an automated translator fails. := is always followed by an expression, so % is not used to force an expression there.

Code: Select all

#Requires AutoHotkey v1.1.33.11
Window1 := % "ahk_id"
Window2 :=   "ahk_id"
MsgBox % Window1 "`n" Window2
Generally speaking, however, delete the first % or % that forces an expression in an AHK command parameter, because v2 uses expressions throughout all scripts.

Explained: Expressions

Pseudoarrays have even fewer roles in v2. Arrays are simpler and more straightforward.

In v2, ahk_id typically serves no critical role without multiple criteria in a WinTitle.

Code: Select all

#Requires AutoHotkey v2.0
For hWnd in WinGetList('ahk_exe notepad.exe')
 WinActivate hWnd
Another example

You need not be confused. The documentation provides examples for each function call.
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Re: Reformatting % lines v1 to v2

13 Mar 2024, 22:41

In v2, ahk_id typically serves no critical role without multiple criteria in a WinTitle.
Yeah, I'm using it in that context, trying to save msyelf from having to use it in every instance. Does this look right?

Code: Select all

MouseGetPos OutputVarX, OutputVarY, WinUMID, WinControl
MouseWindow := "ahk_id" . WinUMID
MouseWinClass := WinGetClass(MouseWindow)
I'm noticing in the documentation for v2 the variables from something like MouseGetPos now have a & in front of them, what's the purpose of that and is it necessary?


[Mod edit: Changed quote tags to code tags.]
niCode
Posts: 310
Joined: 17 Oct 2022, 22:09

Re: Reformatting % lines v1 to v2

13 Mar 2024, 23:43

The ampersand & is a variable reference. When using a regular variable, it goes to the address in memory and returns the value you want. A variable reference is a reference to that address so it can be directly manipulated.

If that went over your head, here is a brief comparison example showing one way it is used:
Example 1 is what you're probably used to.
Example 2 is a different way to do the same thing using a variable reference.
Example 3 is just example 1 trying to be used like example 2 but failing because the variable is passed by value (copy) instead of by reference (original source).

Code: Select all

; example 1
animal_1 := 'hippo'                     ; animal is a hippo
animal_1 := ChangeAnimal1(animal_1)     ; pass hippo to function and save value returned as the new animal
MsgBox(animal_1)                        ; displays giraffe

ChangeAnimal1(animal) {
    return 'giraffe'                    ; return giraffe
}


; example 2
animal_2 := 'monkey'                    ; animal is a monkey
ChangeAnimal2(&animal_2)                ; pass a reference of the animal to function that can be directly manipulated
MsgBox(animal_2)                        ; displays lion

ChangeAnimal2(&animal) {
    animal := 'lion'                    ; change animal to lion
}


; example 3
animal_3 := 'snake'                     ; animal is a snake
ChangeAnimal3(animal_3)                 ; pass snake to function
MsgBox(animal_3)                        ; displays snake

ChangeAnimal3(animal) {
    animal := 'mongoose'                ; change local variable to mongoose (it is never used)
}
In the context of MouseGetPos, it's AHK's way of returning multiple variables from the same function. The parameters are optional but if you want to retrieve the value from one of them, then yes, the & is necessary.

You can even create your own functions like that.

Code: Select all

MyFunc(&firstVar, &secondVar)
MsgBox(firstVar '`n' secondVar)

MyFunc(&var1?, &var2?) {
    var1 := 'string one'
    var2 := 'string two'
}
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Re: Reformatting % lines v1 to v2

13 Mar 2024, 23:54

Ah, thanks.

I'm also having an issue with the following code:

Code: Select all

Loop NumMonitors
	{
		MonNum := A_Index
		OldMon%MonNum%WorkAreaRight := Mon%MonNum%WorkAreaRight
		OldMon%MonNum%WorkAreaBottom := Mon%MonNum%WorkAreaBottom
		Mon%MonNum% := MonitorGet(%MonNum%, &Mon%MonNum%Left, &Mon%MonNum%Top, &Mon%MonNum%Right, &Mon%MonNum%Bottom)
		Mon%MonNum%WorkArea := MonitorGetWorkArea(%MonNum%, &Mon%MonNum%WorkAreaLeft, &Mon%MonNum%WorkAreaTop, &Mon%MonNum%WorkAreaRight, &Mon%MonNum%WorkAreaBottom)
	}
v2 doesn't like that OldMon1WorkAreaRight and so on aren't previously defined. Is there a different way of doing this that wouldn't require manually defining each variable prior to the loop?

I'm not able to convert everything that relies on %MonNum% into object properties, I don't think, because some wind up having properties of their own later on:

Code: Select all

Mon%MonNum%Col%ColNumber% := Object()
Mon%MonNum%Col%ColNumber%["X"] := (((ColNumber - 1) * Mon%MonNum%ColWidth) + Mon%MonNum%Left)
Mon%MonNum%Col%ColNumber%["XEnd"] := ((((ColNumber - 1) * Mon%MonNum%ColWidth) + Mon%MonNum%ColWidth) + Mon%MonNum%Left)
User avatar
boiler
Posts: 17215
Joined: 21 Dec 2014, 02:44

Re: Reformatting % lines v1 to v2

14 Mar 2024, 01:08

Use arrays instead. Use regular variables to get the values out of the MonitorGetWorkArea parameters, then assign them to your array elements. And even in v1, you wouldn’t put % around a variable in an expression like a function parameter as you have done with MonNum.
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Re: Reformatting % lines v1 to v2

14 Mar 2024, 01:09

And more issues:

Code: Select all

AddWindowTypes()
{
	ObjRelease(WindowsToHack)
	WindowsToHack := Object()
	NumberOfItems := 0
	WindowTypes := IniRead("WindowHacks.ini", "WindowTypes")
	Loop Parse, WindowTypes, "`n"
	{
		PreferenceX := IniRead("WindowHacks.ini", A_LoopField, "PreferenceX")
		If (PreferenceX)
		{
			NumberOfItems++
			WindowsToHack.InsertAt(NumberOfItems, A_LoopField)
		}
	}
	Loop Parse, WindowTypes, "`n"
	{
		PreferenceX := IniRead("WindowHacks.ini", A_LoopField, "PreferenceX")
		If (!PreferenceX)
		{
			NumberOfItems++
			WindowsToHack.InsertAt(NumberOfItems, A_LoopField)
		}
	}
	
;	For Index, Element in WindowsToHack
;	{
;		MsgBox %Index%: %Element%
;	}

}
This is my code from the v1. Apparently now Instead of Object(), I use Map() ? But I'm not able to use InsertAt on a map like I was on an Object. So I'm at a complete loss on how to convert this.
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Re: Reformatting % lines v1 to v2

14 Mar 2024, 01:12

boiler wrote:
14 Mar 2024, 01:08
Use arrays instead. Use regular variables to get the values out of the MonitorGetWorkArea parameters, then assign them to your array elements. And even in v1, you wouldn’t put % around a variable in an expression like a function parameter as you have done with MonNum.
Except I'm creating dynamic array names, resulting in something like this:

Mon1Col1[]
Mon1Col2[]
Mon2Col1[]

and so fourth, depending on how high MonNum and ColNum wind up being. The script works fine in v1 and embedding variables within variables using % signs was the only way to do it.
Last edited by chinagreenelvis on 14 Mar 2024, 01:13, edited 1 time in total.
User avatar
boiler
Posts: 17215
Joined: 21 Dec 2014, 02:44

Re: Reformatting % lines v1 to v2

14 Mar 2024, 01:12

chinagreenelvis wrote: Apparently now Instead of Object(), I use Map() ? But I'm not able to use InsertAt on a map like I was on an Object. So I'm at a complete loss on how to convert this.
No, you don’t use Map for a simple array. And there is no reason for you to keep track of the number of items and use InsertAt for the next item. That’s what Push does.

Code: Select all

AddWindowTypes()
{
	WindowsToHack := []
	WindowTypes := IniRead("WindowHacks.ini", "WindowTypes")
	Loop Parse, WindowTypes, "`n"
	{
		PreferenceX := IniRead("WindowHacks.ini", A_LoopField, "PreferenceX")
		If (PreferenceX)
			WindowsToHack.Push(A_LoopField)
	}
	Loop Parse, WindowTypes, "`n"
	{
		PreferenceX := IniRead("WindowHacks.ini", A_LoopField, "PreferenceX")
		If (!PreferenceX)
			WindowsToHack.Push(A_LoopField)
	}

;	For Index, Element in WindowsToHack
;	{
;		MsgBox %Index%: %Element%
;	}

}
User avatar
boiler
Posts: 17215
Joined: 21 Dec 2014, 02:44

Re: Reformatting % lines v1 to v2

14 Mar 2024, 01:26

chinagreenelvis wrote: Except I'm creating dynamic array names, resulting in something like this:

Mon1Col1[]
Mon1Col2[]
Mon2Col1[]

and so fourth, depending on how high MonNum and ColNum wind up being.
Don't. Get out of the v1 dynamic variable name mindset. Use multi-dimensional arrays instead.

chinagreenelvis wrote: The script works fine in v1 and embedding variables within variables using % signs was the only way to do it.
It was not the only way to do it. It was a way to do it. In v2, use arrays.
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Re: Reformatting % lines v1 to v2

14 Mar 2024, 02:17

boiler wrote:
14 Mar 2024, 01:26
In v2, use arrays.
I hate to keep digging for help on this, but I really can't wrap my brain around any other way to do accomplish the following:

Code: Select all

Loop NumMonitors
{
	MonNum := A_Index
	OldMon%MonNum%WorkAreaRight := Mon%MonNum%WorkAreaRight
	OldMon%MonNum%WorkAreaBottom := Mon%MonNum%WorkAreaBottom
	Mon%MonNum% := MonitorGet(%MonNum%, &Mon%MonNum%Left, &Mon%MonNum%Top, &Mon%MonNum%Right, &Mon%MonNum%Bottom)
	Mon%MonNum%WorkArea := MonitorGetWorkArea(%MonNum%, &Mon%MonNum%WorkAreaLeft, &Mon%MonNum%WorkAreaTop, &Mon%MonNum%WorkAreaRight, &Mon%MonNum%WorkAreaBottom)
}

MonNumPrimary := MonitorGetPrimary()

Loop NumMonitors
{
	MonNum := A_Index
	If (OldMon%MonNum%WorkAreaRight != Mon%MonNum%WorkAreaRight || OldMon%MonNum%WorkAreaBottom != Mon%MonNum%WorkAreaBottom)
	{		
		GetGridInfo()
		Break
	}
}

GetGridInfo()
{
	Global
	Loop NumMonitors
	{
		MonNum := A_Index
		
		ResX := (Mon%MonNum%Right - Mon%MonNum%Left)
		ResY := (Mon%MonNum%Bottom - Mon%MonNum%Top)
		
		Mon%MonNum%NumCols := IniRead("WindowHacks.ini", ResX . "x" . ResY, "NumCols")
		Mon%MonNum%NumRows := IniRead("WindowHacks.ini", ResX . "x" . ResY, "NumRows")
		
		Mon%MonNum%SpaceX := (Mon%MonNum%WorkAreaRight - Mon%MonNum%Left)
		Mon%MonNum%SpaceY := (Mon%MonNum%WorkAreaBottom - Mon%MonNum%Top)
		Mon%MonNum%ColWidth := (Mon%MonNum%SpaceX / Mon%MonNum%NumCols)
		
		Loop Mon%MonNum%NumCols
		{
			ColNumber := A_Index
			ObjRelease(Mon%MonNum%Col%ColNumber%)
			Mon%MonNum%Col%ColNumber% := Object()
			Mon%MonNum%Col%ColNumber%["X"] := (((ColNumber - 1) * Mon%MonNum%ColWidth) + Mon%MonNum%Left)
			Mon%MonNum%Col%ColNumber%["XEnd"] := ((((ColNumber - 1) * Mon%MonNum%ColWidth) + Mon%MonNum%ColWidth) + Mon%MonNum%Left)
		}
	}
}
Like, the object names have to be either dynamically generated or predefined, right? Because Mon1Col2[] has keys and values. Sorry, it's probably a little confusing because it's not a complete script, I just wind up using these associative arrays a lot down the line.
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Re: Reformatting % lines v1 to v2

14 Mar 2024, 02:24

Actually, nevermind, it's probably object properties with parameters that I'm supposed to be looking at, I think I'm just tired and will have to come back to it later but I appreciate any suggestions in the meantime.
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Dynamic Variable Creation

14 Mar 2024, 02:53

[Moderator note: The next several posts starting with this one were moved from a thread started in the "Wish List" sub-forum.]

https://www.autohotkey.com/docs/v2/Language.htm#dynamic-variables
Note: A variable cannot be created by a dynamic reference, but existing variables can be assigned. This includes all variables which the script contains non-dynamic references to, even if they have not been assigned values.
This was possible in v1.

If this restriction in v2 is arbitrary, I think it should be changed. Being able to dynamically generate variables is incredibly useful, and having to manually declare every possible dynamic reference can just about defeat the purpose.
Descolada
Posts: 1174
Joined: 23 Dec 2021, 02:30

Re: Dynamic Variable Creation

14 Mar 2024, 03:54

@chinagreenelvis, I'm fairly sure that the change was done to coerce users into using objects instead: Object, Array, Map, etc. It makes sense to use them, instead of polluting the global namespace with variables which should have been objects instead.

For example, this v1 code

Code: Select all

Loop 3
{
   var%A_Index% := A_Index
}
should obviously be an array:

Code: Select all

var := []
Loop 3 {
    var.Push(A_Index)
}
It has the included benefit of knowing how many elements you have (in v1, how would you know what the maximum varN is?) with var.Length, and you can remove the elements more easily (var := [] clears all values).

With Maps you can create key-value pairs:

Code: Select all

myMap := Map()
myMap["value1"] := "myValue"
myMap[1] := "number 1"
MsgBox myMap["value1"]
Or you can use regular objects:

Code: Select all

obj := {}
obj.value1 := "myValue"
obj.1 := "number1"
MsgBox obj.1
MsgBox obj.%2-1% ; you can even evaluate expressions to access properties
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Re: Dynamic Variable Creation

14 Mar 2024, 11:04

Yes, but what if I was using v1 to create dynamic objects?

What would be the equivalent to something more complicated like

Code: Select all

var%A_Index%also%Other_Index% := Object()
var%A_Index%also%Other_Index%["Key"] := someVariable
Descolada
Posts: 1174
Joined: 23 Dec 2021, 02:30

Re: Dynamic Variable Creation

14 Mar 2024, 11:30

@chinagreenelvis, you can nest objects as much as you want:

Code: Select all

var := [[Map("Key", "someVariable")]]
MsgBox var[1][1]["Key"]
Perhaps you could show a certain scenario which you want to convert to v2? There might be even better alternatives available.
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Re: Dynamic Variable Creation

14 Mar 2024, 12:35

Descolada wrote:
14 Mar 2024, 11:30
@chinagreenelvis, you can nest objects as much as you want:

Code: Select all

var := [[Map("Key", "someVariable")]]
MsgBox var[1][1]["Key"]
Perhaps you could show a certain scenario which you want to convert to v2? There might be even better alternatives available.
Thanks, this might be what I'm looking for.

Basically, I have a window-management program that arranges explorer windows according to available "columns" on a monitor, depending on resolution. So Monitor 1 may have four columns while monitor 2 may have three, and each column has its own values like top-left position, height, width, etc. But the numbers of available columns fluctuate based on how many monitors there are and what the resolution is.

So I get the number of monitors and I search for a value in an INI for how many columns that monitor should have based in its resolution. But let's assume that I have two monitors with three columns each.

That leaves me with six objects that I would need to declare manually in v2: Mon1Col1, Mon1Col2, and so forth.

How would I got about dynamically generating MonCol[1][1]["Height"] and MonCol[1][2]["Width"] et al?
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Re: Dynamic Variable Creation

14 Mar 2024, 12:38

Here's a link to the original code posted in a help thread:

viewtopic.php?p=563009#p563009
chinagreenelvis
Posts: 133
Joined: 19 Oct 2015, 16:21

Re: Dynamic Variable Creation

14 Mar 2024, 12:43

Instead of

Code: Select all

Global Mon1Col1 := Map()
Mon%MonNum%Col%ColNumber%["X"] := WhateverVal
Would I just use

Code: Select all

MonCol := [[Map()]]
MonCol[MonNum][ColNumber]["X"] := WhateverVal
Descolada
Posts: 1174
Joined: 23 Dec 2021, 02:30

Re: Dynamic Variable Creation

14 Mar 2024, 12:53

@chinagreenelvis, MonCol := [[Map()]] is not enough, as it will create only one column for one monitor. If you know the number of monitors and columns beforehand then you can define the MonCol manually like that. Otherwise you can just push new elements into the array.

For example

Code: Select all

Monitors := [] ; define the array of monitors
Monitors.Push([]) ; add array for columns
Monitors[1].Push({Width:0, Height:1}) ; column 1
Monitors[1].Push({Width:10, Height:10}) ; column 2
Monitors[1].Push({Width:100, Height:100}) ; column 3
MsgBox "There are " Monitors.length " monitors available"
MsgBox "Monitor 1 has " Monitors[1].Length " columns"
MsgBox "Monitor 1 column 1 width is " Monitors[1][1].Width
In actuality you'd write some Loops to read monitor/column info and push them in the array.

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: Noitalommi_2 and 47 guests