Search code examples
arraysmultidimensional-arrayglobal-variablesautohotkey

Creating a multidimensional array in AutoHotKey that is updated according to user inputs


General problem

In AutoHotKey, I'm having a hard time appending values to an Array object that starts off empty and then gets filled up with values as the user performs certain actions.

The script

Copied below is a small reproducible version of my script. In it, the goal is for the NumpadAdd key to add the current mouse position to the Array called list_of_mouse_positions. After adding a few points, the user can use the Numpad0 key to look at the list of positions stored thus far.

;---------------;
; GENERAL SETUP ;
;---------------;

coordmode Mouse, screen

; Creates an empty array. This will store the mouse positions.
list_of_mouse_positions := Array()

;-----------;
; FUNCTIONS ;
;-----------;

; Function used to add the current mouse position to the global
; list of mouse positions. The `x` parameter serves no purpose.
AddLocation(x)
{
    MouseGetPos, xpos, ypos
    mouse_pos := Array()
    mouse_pos[1] := xpos
    mouse_pos[2] := ypos
    list_of_mouse_positions.Push(mouse_pos)
}

;-------------;
; KEYBINDINGS ;
;-------------;

; Adds the current mouse position to the 
NumpadAdd::
    AddLocation(0)
    return

; Checking the mouse positions stored so far
NumPad0::
    MsgBox, %list_of_mouse_positions%
    return

Expected Output vs Actual Output

After running the script and hitting the NumpadAdd key to capture a couple of coordinates, I hit the Numpad0 key. What I expected to show up was a Message Box containing all of the coordinates. However, what showed up instead was just an empty message box.

Specific question

How can I update an Array object (in my case, the list_of_mouse_positions variable) by appending new rows to it, each one of which stores two new values?

Note about the expected dimensions of list_of_mouse_positions

I expect the list_of_mouse_positions variable to be a multidimensional array of dimensions (n x 2), where the first column contains all of the x values, and the second column contains all of the y values.

For example, suppose the user "captured" the coordinates of the following mouse positions:

  • (100, 200)
  • (400, 100)
  • (600, 150)

In this case, the structure of the list_of_mouse_positions would be as follows:

list_of_mouse_positions[1,1] = 100
list_of_mouse_positions[1,2] = 200
list_of_mouse_positions[2,1] = 400
list_of_mouse_positions[2,2] = 100
list_of_mouse_positions[3,1] = 600
list_of_mouse_positions[3,2] = 150

Solution

  • Corrected code to match your new code, but with super-global edit (see doc quote below)

    Super-global variables [v1.1.05+]: If a global declaration appears outside of any function, it takes effect for all functions by default (excluding force-local functions). This avoids the need to redeclare the variable in each function. However, if a function parameter or local variable with the same name is declared, it takes precedence over the global variable. Variables created by the class keyword are also super-global.

    with some included comments on your code with regards to simplicity you may want to do, or not as you desire:

    ;---------------;
    ; GENERAL SETUP ;
    ;---------------;
    ; Setting the coordinate mode
    coordmode Mouse, screen
    
    ; This variable will store the mouse positions. 
    ; The command below establishes that it is a global variable. 
    ; Initializes the global variables
    Global list_of_mouse_positions := Array()
    Return ; end auto-execute section of script
    
    
    ;-----------;
    ; FUNCTIONS ;
    ;-----------;
    
    
    ; Adds the current mouse position to an array of mouse positions
    AddLocation()
    {
        ; global list_of_mouse_positions ; not needed, declared super-global above.
        MouseGetPos, xpos, ypos
        mouse_pos := Array() ; not needed see below
        mouse_pos.Push(xpos) ; not needed see below
        mouse_pos.Push(ypos) ; not needed see below
        list_of_mouse_positions.Push(mouse_pos)
        ; instead of making 3 additonal lines you can just do 'list_of_mouse_positions.Push(Array(xpos, ypos))'
        return
    }
    
    ; Prints 2D arrays
    print2DArr(byRef Arr, setDelim="`r`n", valDelim=", ") 
    {
        printText := ""
        For _, Row in Arr 
        {
            printText .= (printText ? setDelim : "") . print1DArr(Row, valDelim)
        }
        Return printText
    }
    
    ; Prints 1D arrays
    print1DArr(byRef Array, valDelim=", ") 
    {
        printText := ""
        For _, Val in Array 
        {
            printText .= (printText ? valDelim : "") . Val
        }
        Return printText
    }
    
    ;-------------;
    ; KEYBINDINGS ;
    ;-------------;
    ; Adds new positions to the list of mouse positions
    NumpadAdd::
        AddLocation()
        return
    
    ; Checking the mouse positions stored so far
    NumPad0::
        global list_of_mouse_positions
        MsgBox % print2DArr(list_of_mouse_positions)
        return