Using associative arrays here?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
Scr1pter
Posts: 1277
Joined: 06 Aug 2017, 08:21
Location: Germany

Using associative arrays here?

06 Jul 2018, 06:32

Hi guys,

I want to create a gui which shows this picture:
Image

The code will probably be something like:

Code: Select all

Gui, -Caption
Gui, +AlwaysOnTop
Gui, Show, x0 y0 w1920 h1080 
Gui, Add, Picture, x0 y0 w1920 h1080, picture.png
return
Whenever left-clicking on a square, a specific function should be executed + the color of the pressed square should change.
(But let's ignore the coloring function at the moment...)

I believe it's correct/helpful that all squares should be defined as array elements.
A1, B1, C1, D1 etc.

In the AHK tutorial I read that associative arrays contain both elements and values, which are linked to the elements.
Like:

Code: Select all

array := {ten: 10, twenty: 20, thirty: 30}
Ok, but in my case I would need more than 1 value which is linked to the element.
It should be something like:
squareArray := {A1: x = 0..100 y = 0..100, B1: x = 101..200 y = 101..200}

Whenever I click with the left mouse button inside of a square, AHK would check the x/y-coordinates.
if x is in the range of 0 till 100 and y is in the range of 0 till 100, it would mean the square is A1.

I know that using arrays isn't necessary for the functionality, because I already got it work without arrays,
but using arrays might give me more possibilities, especially when I want to colorize a square.

Code: Select all

lbutton:: ; Left-click
MouseGetPos, xx, yy ; Check current mouse position
if (xx < 128) & (yy < 128)
{
  MsgBox, A1 
}
else if (xx < 256) & (yy < 128)
{
  MsgBox, B1
}
else if (xx < 384) & (yy < 128)
{
  MsgBox, C1
}
etc.
Good, the concrete questions so far are:
1. Is it meaningful to work with (associative) arrays here? (As mentioned, I would like to colorize the squares later...)
2. How would I specify that A1 is from x = 0 till 100 and y = 0 till 100?

Thanks for any help!
Please use [code][/code] when posting code!
Keyboard: Logitech G PRO - Mouse: Logitech G502 LS - OS: Windows 10 Pro 64 Bit - AHK version: 1.1.33.09
User avatar
TheDewd
Posts: 1513
Joined: 19 Dec 2013, 11:16
Location: USA

Re: Using associative arrays here?

06 Jul 2018, 12:36

You could create multiple controls with different variable names instead of using a background image and mouse coordinates.

See below:

Code: Select all

#SingleInstance, Force

Colors := ["FF8A80", "FF80AB", "EA80FC", "B388FF", "8C9EFF", "80D8FF", "A7FFEB", "F4FF81", "FFD180"]

Gui, Margin, 0, 0

Loop, 36 {
	Row := (100 * (Ceil(A_Index / 6) - 1))
	Col := (100 * ((!(M := Mod(A_Index, 6)) ? 6 : M) - 1))
	Gui, Add, Picture, % "x" Col " y" Row " w" 100 " h" 100 " Border +0x4E gTileClick vTile" A_Index " +HWNDTile" A_Index
}

Gui, Show, AutoSize, Example
return

TileClick:
	CreatePixel(GetRandomHexColor(), %A_GuiControl%)
return

CreatePixel(Color, Handle) {
	VarSetCapacity(BMBITS, 4, 0), Numput("0x" . Color, &BMBITS, 0, "UInt")
	hBM := DllCall("Gdi32.dll\CreateBitmap", "Int", 1, "Int", 1, "UInt", 1, "UInt", 24, "Ptr", 0, "Ptr")
	hBM := DllCall("User32.dll\CopyImage", "Ptr", hBM, "UInt", 0, "Int", 0, "Int", 0, "UInt", 0x2008, "Ptr")
	DllCall("Gdi32.dll\SetBitmapBits", "Ptr", hBM, "UInt", 3, "Ptr", &BMBITS)
	DllCall("User32.dll\SendMessage", "Ptr", Handle, "UInt", 0x172, "Ptr", 0, "Ptr", hBM)
}

GetRandomHexColor() {
	Global Colors
	Random, RandCol, 1, Colors.MaxIndex()
	return Colors[RandCol]
}
A_AhkUser
Posts: 1147
Joined: 06 Mar 2017, 16:18
Location: France
Contact:

Re: Using associative arrays here?

06 Jul 2018, 14:14

Here's another suggestion - trying to make best use of GUI commands, styles and options. For sure, the solution suggested by TheDewd's is the better one; it is the solution of a game maker.

Code: Select all

; https://www.autohotkey.com/docs/misc/Styles.htm#Text:
SS_SUNKEN := 0x1000 ; Draws a half-sunken border around a static control.

tileWidth := 100, tileHeight := 100
tileSize := Format("w{:1} h{:2}", tileWidth, tileHeight) ; simple shorcut for 'wmyWidth hmyHeight'
staticOptions := SS_SUNKEN . " xp yp wp hp +BackgroundTrans"
columns := rows := 6
maxTiles := columns ** 2 ; ** > power (exponentiation)
colors := ["olive", "navy", "yellow", "fuchsia", "grey", "lime", "teal", "black", "white"]

GUI, -Caption
GUI, Margin, 0, 0
GUI, Add, Text, Section xm ym w0 h0, ; dummy control to create a Section: https://www.autohotkey.com/docs/commands/Gui.htm#PosSize
colCurrent := 0, rowCurrent := 1 ; variables intended to keep track of the current row/column in the loop
Loop % maxTiles
{
	Random, r, 1, % colors.length()
	if not (mod(++colCurrent, columns + 1)) { ; if 'i' equals a multiple of columns + 1... (https://www.autohotkey.com/docs/commands/Math.htm#Mod)
		Gui, Add, Progress, % "vpgrss" . a_index . " hidden xs " . tileSize . " c" . colors[r], 100
		GUI, Add, Text, % "Section " . staticOptions,
	colCurrent := 1, rowCurrent++
	} else {
		Gui, Add, Progress, % "vpgrss" . a_index . " hidden xs+" + (colCurrent - 1) * tileHeight . A_Space . "ys " . tileSize . " c" . colors[r], 100
		GUI, Add, Text, % staticOptions,
	}
	func := Func("tileOnClick").bind(rowCurrent, colCurrent) ; create a boundfunc object (https://www.autohotkey.com/docs/objects/Functor.htm#BoundFunc)
	GuiControl, +g, % "Static" . a_index + 1, % func
	; set g-label > indicates to launch the function object 'func' automatically when the user clicks or changes the control the function is bound to (https://www.autohotkey.com/docs/commands/Gui.htm#label)
}
GUI, Show, AutoSize
return

tileOnClick(_row, _column, _hwnd) {
static _lastFoundHwnd := "", _lastFoundIndex := "" ; variables intended to keep track of the last clicked item's own ID and index in the table
global columns
ToolTip % _row . "," _column "|" _hwnd
	if (_lastFoundHwnd) {
		GuiControl, Hide, % "pgrss" . _lastFoundIndex
		GuiControl, Show, % _lastFoundHwnd,
	}
	GuiControl, Hide, % _lastFoundHwnd:=_hwnd,
	GuiControl, Show, % "pgrss" . _lastFoundIndex:=(--_row * columns) + _column
}
my scripts
User avatar
FanaticGuru
Posts: 1907
Joined: 30 Sep 2013, 22:25

Re: Using associative arrays here?

06 Jul 2018, 15:01

TheDewd wrote:

Code: Select all

CreatePixel(Color, Handle) {
	VarSetCapacity(BMBITS, 4, 0), Numput("0x" . Color, &BMBITS, 0, "UInt")
	hBM := DllCall("Gdi32.dll\CreateBitmap", "Int", 1, "Int", 1, "UInt", 1, "UInt", 24, "Ptr", 0, "Ptr")
	hBM := DllCall("User32.dll\CopyImage", "Ptr", hBM, "UInt", 0, "Int", 0, "Int", 0, "UInt", 0x2008, "Ptr")
	DllCall("Gdi32.dll\SetBitmapBits", "Ptr", hBM, "UInt", 3, "Ptr", &BMBITS)
	DllCall("User32.dll\SendMessage", "Ptr", Handle, "UInt", 0x172, "Ptr", 0, "Ptr", hBM)
}
Nice!

I wish that was within my skill set. I still view it as a mystery like objects, RegEx, COM, etc. use to be to me. I really need to dive into GDI/GDI+ and up my game.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
Scr1pter
Posts: 1277
Joined: 06 Aug 2017, 08:21
Location: Germany

Re: Using associative arrays here?

07 Jul 2018, 05:09

Ok, the posted codes are indeed very interesting.

However, I would still like to know if it's possible to address multiple values to an Array element. :)
Please use [code][/code] when posting code!
Keyboard: Logitech G PRO - Mouse: Logitech G502 LS - OS: Windows 10 Pro 64 Bit - AHK version: 1.1.33.09
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Using associative arrays here?

07 Jul 2018, 05:27

Scr1pter wrote: However, I would still like to know if it's possible to address multiple values to an Array element. :)

Code: Select all

A := { a1 : { x1:0, x2:100, y1:0, y2:100 }, a2 : { ... }, ... }
Cheers.
User avatar
Scr1pter
Posts: 1277
Joined: 06 Aug 2017, 08:21
Location: Germany

Re: Using associative arrays here?

09 Jul 2018, 12:07

Thanks, and this covers also the values which are between the two ones?
If I click the cursor at x=60, y=60, it must be also A1.

I guess I have to loop through the Array in order to let AHK write the value to a MsgBox?

Code: Select all

^F1:: ; Ctrl+F1
squareArray := {A1: {x1:0, x2:127, y1:0, y2:127}, A2: {x1:128, x2:255, y1:0, y2:127} }
square := squareArray[x]
MouseGetPos, xx, yy
if (xx < 128) & (yy < 128)
{
  MsgBox, %square%
}
return
Something like this doesn't work.

The other idea was this, but it didn't work either.

Code: Select all

^F1:: ; Strg+F1
MouseGetPos, xx, yy
squareArray := {A1: {xx1:0, xx2:127, yy1:0, yy2:127}, A2: {xx1:128, xx2:255, yy1:0, yy2:127}}
square := squareArray[x]
for x, square in squareArray
if square = "A1"
{
  MsgBox, %square%
}
return
I don't know how to proceed here...

P.S. I also tried it with index instead of X.
Please use [code][/code] when posting code!
Keyboard: Logitech G PRO - Mouse: Logitech G502 LS - OS: Windows 10 Pro 64 Bit - AHK version: 1.1.33.09
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Using associative arrays here?

10 Jul 2018, 01:07

what is x in square := squareArray[x]? To find the square in the array, you can do,

Code: Select all

xx := 300, yy := 25		; outside
xx := 40, yy := 25		; A1
xx := 127, yy := 127	; A1
xx := 150, yy := 25		; A2

squareArray := {A1: {xx1:0, xx2:127, yy1:0, yy2:127}, A2: {xx1:128, xx2:255, yy1:0, yy2:127}}
for x, square in squareArray
	if pointIsInSquare(xx, yy, square)
		msgbox % x

pointIsInSquare(x, y, s){
	return x >= s.xx1 && x <= s.xx2 && y >= s.yy1 && y <= s.yy2
}
You'd need to consider the behaviour on the border of two squares, i.e., when to use >= or >, and <= or <.
A much simpler way to determine which square a point belongs to is to calculate it, rather than search for it, I'd only use the above array approach if the grid isn't defined by a simple transformation. Your grid is simple, hence this will do the job,

Code: Select all

getSquare(x,y){
	; with and height of a square.
	static w := 100
	static h := 100
	row := ceil((y+1)/h)	; +1 for 1-based grid index
	col := ceil((x+1)/w)
	
	return [row, col] ; point (x,y) is in row and column
}
Example usage, here square (4,4) (row, column) is assigned an action,

Code: Select all

myGrid := []
myGrid[4,4] := func("bingo")	; square (4,4) calls function "bingo"

myTimer := func("timer").bind(myGrid)
settimer % myTimer, 250
esc::exitapp

timer(grid){
	coordmode mouse, screen
	mousegetpos x, y
	rc := getSquare(x, y)
	if f:=grid[rc.1, rc.2]
		f.call()
	else
		tooltip % rc.1 "`t" rc.2
}

bingo(){
	tooltip bingo
}

getSquare(x,y){
	; with and height of a square.
	static w := 100
	static h := 100
	row := ceil((y+1)/h)	; +1 for 1-based grid index
	col := ceil((x+1)/w)
	
	return [row, col] ; point (x,y) is in row and column
}
Cheers.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: No registered users and 150 guests