[2.0-beta.1] Is it valid to set property of A_Args array? Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

[2.0-beta.1] Is it valid to set property of A_Args array?

01 Aug 2021, 15:45

I prefer not to use global variables to share data between functions and if at all needed, I would use A_Args in V1 as associative array.
In V2, A_Args is of type Array and I am not able to use DefineProp()
The following seems to work, but I'm unsure it is valid.

V2 experts, please suggest.

Code: Select all

; Autohotkey 2.0-beta.1
#Warn
#SingleInstance

A_Args.MyProp := Map()
A_Args.MyProp["Name"] := 1
A_Args.MyProp.Delete("Name")

If ! A_Args.MyProp.Count
     A_Args.DeleteProp("MyProp")

MsgBox( HasProp(A_Args, "MyProp") )
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

01 Aug 2021, 20:19

I find it hard to believe that this would be necessary at all in v2. Can you provide a real life example for this?
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

01 Aug 2021, 23:55

Bear with me, I have only 3 days experience in V2.

I create a Gui inside a function,
and need to clean up inside GuiClose function.
Need to access the map object created in former at latter.
lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: [2.0-beta.1] Is it valid to set property of A_Args array?  Topic is solved

02 Aug 2021, 04:05

You can do whatever you want with A_Args, and so can any other author. I hope you don't think this use of A_Args is good for a script you are sharing. It is not. For instance, if someone assigns it a new array (perhaps conditionally), your properties will disappear.

I think what kczx3 was getting at is that it's not necessary to use this technique. What you've described is the reason you want to share data between two functions, but there are multiple ways around that which are more sensible, including an ordinary global variable.

For instance, GuiClose may be a closure defined inside the function which creates the Gui, with access to all of that function's local variables. Or it may be a bound function, with the value of a shared variable or object bound to a parameter. Or the overall set of functions can be methods within a class, globally reserving only the name of the class instead of one or more functions. (If the class extends Gui, there is provision for the instance to handle its own events without creating a circular reference.)

If you really want to hide your shared variables but keep them "globally" accessible, I would suggest that you use one of your own functions instead of A_Args. If you've defined the function F, F.MyProp := x will define or assign the MyProp property of the function object. It's intentional that you can add properties to any object derived from Object (including Array and Func) via assignment, although I'm not sure that it's explicitly documented as such.

As for A_Args, it is just an Array, so of course you can add properties via assignment.
A_Args is of type Array and I am not able to use DefineProp()
Why not?
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

02 Aug 2021, 05:57

SKAN wrote:
01 Aug 2021, 23:55
I create a Gui inside a function,
and need to clean up inside GuiClose function.
Need to access the map object created in former at latter.
I've come across stuff like this, if I'm not mistaken.

In this case I would attach the Map to the GUI i a property, and in the GuiClose func it will be accessible.

Code: Select all

_g := Gui()
_g.OnEvent("close",gui_close)
_g.custom := Map("test","map")
_g.Show("w256 h256")

gui_close(g) {
    msgbox g.custom["test"] ; displays "map"
    ExitApp
}
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

02 Aug 2021, 06:24

Thank you very much @lexikos.
So many cleaner solutions and yet I chose the improper one.
It wasn't intentional. I'm unable to think clearly when typing V2 code. I will become better when I gain muscle memory.
Thanks again for the elaborate answer.
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

02 Aug 2021, 06:44

TheArkive wrote:
02 Aug 2021, 05:57
I've come across stuff like this, if I'm not mistaken.
In this case I would attach the Map to the GUI i a property, and in the GuiClose func it will be accessible.
:shock:
That will simplify my code to a great extent. I will try that, Thanks. :)
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

02 Aug 2021, 13:21

Hi @TheArkive

Your suggestion works well. Thanks.
Here is a bare minimum replica of a V1 script that I use during monsoon.
 
image.png
(9.24 KiB) Downloaded 306 times
 

Code: Select all

#Warn
#SingleInstance

;Loop 1000
rfCalc()

f2::rfCalc()


rfCalc() {                                                ; Rainfall calculator for AutoHotkey 2.0-beta.1
                                                          ; by SKAN on D481/D482 @ __
    Local  Glob
    Local  H, Y, GroupBox1, MyGui
    Local  Font   := Map( "TextItalic", ["s11 Norm Italic",    "Calibri"]
                        , "TextUnderL", ["s11 Norm Underline", "Calibri"]
                        , "TextNormal", ["s11 Norm",           "Consolas"]
                        , "MonoNormal", ["s12 Norm",           "Consolas"] )

    MyGui         := Gui("-Theme -MinimizeBox", "Rainfall calculator")
    Glob          := Map(1,0, 2,0, 3,0, 4,0)

    MyGui.MyProp := ObjPtrAddRef(Glob)

    MyGui.MarginX := 16
    MyGui.MarginY :=  8

    MyGui.SetFont(Font["TextItalic"]*)
    GroupBox1 := MyGui.Add("GroupBox", "xm ym  w180 h120", " Parameters ")

    MyGui.SetFont(Font["TextUnderL"]*)
    MyGui.Add("Text", "xp+16 yp+24 w148 Section Right", "    Diameter of collector")

    MyGui.SetFont(Font["MonoNormal"]*)
    Glob[1] := MyGui.Add("Edit", "xs y+0 wp-24 h18 -E0x200 Right")

    MyGui.SetFont(Font["TextNormal"]*)
    MyGui.Add("Text", "x+0 yp w24 hp 0x200 Right", "cm")

    MyGui.SetFont(Font["TextUnderL"]*)
    MyGui.Add("Text", "xs y+12 w148 Right", "  1 cm rainfall equals to")

    MyGui.SetFont(Font["MonoNormal"]*)
    Glob[2] := MyGui.Add("Edit", "xs y+0 wp-24 h18 -E0x200  Right")

    MyGui.SetFont(Font["TextNormal"]*)
    MyGui.Add("Text", "x+0 yp w24 0x200 Right", "ml")

    GroupBox1.GetPos(,,,&H),    Y := (MyGui.MarginY * 2) + H   ;  adjusted Y pos for DPI scaling
    MyGui.SetFont(Font["TextItalic"]*)
    MyGui.Add("GroupBox", "xm  w180 h120 y" Y, "   Calculation  ")

    MyGui.SetFont(Font["TextUnderL"]*)
    MyGui.Add("Text", "xp+16 yp+24 w148 Section Right", "    Collected rain")

    MyGui.SetFont(Font["MonoNormal"]*)
    Glob[3] := MyGui.Add("Edit", "xs y+0 wp-24 h18 -E0x200  Right")

    MyGui.SetFont(Font["TextNormal"]*)
    MyGui.Add("Text", "x+0 yp w24 0x200 Right", "ml")

    MyGui.SetFont(Font["TextUnderL"]*)
    MyGui.Add("Text", "xs y+12 w148 Right", "         Rainfall")

    MyGui.SetFont(Font["MonoNormal"]*)
    Glob[4] := MyGui.Add("Edit", "xs y+0 wp-24 h18 -E0x200  Right")

    MyGui.SetFont(Font["TextNormal"]*)
    MyGui.Add("Text", "x+0 yp w24 0x200 Right", "cm")

    Glob[1].OnEvent( "Change", rfCalc_EditRoutine.Bind(Glob, Glob[1].Hwnd) )
    Glob[2].OnEvent( "Change", rfCalc_EditRoutine.Bind(Glob, Glob[2].Hwnd) )
    Glob[3].OnEvent( "Change", rfCalc_EditRoutine.Bind(Glob, Glob[3].Hwnd) )
    Glob[4].OnEvent( "Change", rfCalc_EditRoutine.Bind(Glob, Glob[4].Hwnd) )

    Glob[1].Value := IniRead(A_ScriptDir "\rfCalc.ini", "rfCalc", "Dia", "")
    Glob[2].Value := IniRead(A_ScriptDir "\rfCalc.ini", "rfCalc", "Vol", "")

    If Glob[2].Value
       Glob[3].Focus()

    MyGui.OnEvent("Close",  rfCalc_Close)
    MyGui.OnEvent("Escape", rfCalc_Close)

    MyGui.Show("AutoSize")
}


rfCalc_Close(MyGui) {
    Local Glob := ObjFromPtr( MyGui.MyProp )

    IniWrite(Glob[1].Value, A_ScriptDir "\rfCalc.ini", "rfCalc", "Dia")
    IniWrite(Glob[2].Value, A_ScriptDir "\rfCalc.ini", "rfCalc", "Vol")

    MyGui.DeleteProp("MyProp") ; Is necessary?
    Glob := ""                 ; Is necessary?
    MyGui.Destroy()
}


rfCalc_EditRoutine(Glob, Hwnd, *) {
    Static PI := 3.14159265358979
    Local  R, Rmm, Dia, Vol, Col

    Switch( Hwnd )
    {
           Case Glob[1].Hwnd :
           {
                 If ! IsNumber( Dia := Glob[1].Value )
                      Return

                 R   := Dia/2
                 Vol := PI * R * R
                 Glob[2].Value := Round(Vol, 6)
                 Glob[3].Value := ""
                 Glob[4].Value := ""
           }


           Case Glob[2].Hwnd :
           {
                 If ! IsNumber( Vol := Glob[2].Value )
                      Return

                 Vol := Glob[2].Value
                 Dia := Sqrt(Vol/PI) * 2
                 Glob[1].Value := Round(Dia, 6)
                 Glob[3].Value := ""
                 Glob[4].Value := ""
           }


           Case Glob[3].Hwnd :
           {
                 If ! IsNumber( Dia := Glob[1].Value )
                 || ! IsNumber( Vol := Glob[2].Value )
                 || ! IsNumber( Col := Glob[3].Value )
                      Return

                 Glob[4].Value := Round(Col/Vol, 4 )
           }


           Case Glob[4].Hwnd :
           {
                 If ! IsNumber( Dia := Glob[1].Value )
                 || ! IsNumber( Vol := Glob[2].Value )
                 || ! IsNumber( Rmm := Glob[4].Value )
                      Return

                 Glob[3].Value := Round(Vol*Rmm, 4 )
           }
    }      ; end switch
}
Experts let me know if I am doing something wrong.
If I start off with a single GUI, the memory usage is like 1.5 MiB to 2.0 MiB
If I start off with 1000 GUI's the memory usage is like (mere) 18 MiB.
But when I close them all and return to a single GUI, the memory usage is like 4.0 MiB.

Am I leaking something? :?

PS: I don't need 1000 GUI's but I intend to put many such tiny apps into a single always-running-script.
So, all GUI need to cleanly exit.
User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

02 Aug 2021, 13:56

I have found the memory usage of AHK v2 to not always do what i expect. But it's not an issue for my scripts so far.

In your case I would create 1000 GUIs, and destroy them, in succession a few times.

And see if the me usage continues to increase. If not, then I imagine you should be ok.

EDIT: The mem usage you have reported sounds about on par for what I have experienced.
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

02 Aug 2021, 14:30

@SKAN I guess I don't see why you need to do it that way.

Don't have your _Close and _EditRoutine functions be out in the global scope. Just put them at the bottom of your rfCalc function as nested functions (i.e. closures). That avoids the whole need for attaching some random prop to your Gui instance. Or as Lexikos stated, make your function a class that extends Gui but I think the nested function route will be easier for you to start with.

And having said that... you're just storing GuiControl references in the Glob prop. All your callbacks have access to the Gui instance from which you can access named controls via __Item. Here's how I'd do it...

Code: Select all

#Warn
#SingleInstance

;Loop 1000
rfGui := rfCalc()
F2::rfGui.Show("AutoSize")

rfCalc() {                                                ; Rainfall calculator for AutoHotkey 2.0-beta.1
                                                          ; by SKAN on D481/D482 @ __
    Local  H, Y, GroupBox1, MyGui
    Local  Font   := Map( "TextItalic", ["s11 Norm Italic",    "Calibri"]
                        , "TextUnderL", ["s11 Norm Underline", "Calibri"]
                        , "TextNormal", ["s11 Norm",           "Consolas"]
                        , "MonoNormal", ["s12 Norm",           "Consolas"] )

    MyGui         := Gui("-Theme -MinimizeBox", "Rainfall calculator")

    MyGui.MarginX := 16
    MyGui.MarginY :=  8

    MyGui.SetFont(Font["TextItalic"]*)
    GroupBox1 := MyGui.Add("GroupBox", "xm ym  w180 h120", " Parameters ")

    MyGui.SetFont(Font["TextUnderL"]*)
    MyGui.Add("Text", "xp+16 yp+24 w148 Section Right", "    Diameter of collector")

    MyGui.SetFont(Font["MonoNormal"]*)
    MyGui.Add("Edit", "xs y+0 wp-24 h18 -E0x200 Right vGlob1").OnEvent( "Change", rfCalc_EditRoutine )

    MyGui.SetFont(Font["TextNormal"]*)
    MyGui.Add("Text", "x+0 yp w24 hp 0x200 Right", "cm")

    MyGui.SetFont(Font["TextUnderL"]*)
    MyGui.Add("Text", "xs y+12 w148 Right", "  1 cm rainfall equals to")

    MyGui.SetFont(Font["MonoNormal"]*)
    MyGui.Add("Edit", "xs y+0 wp-24 h18 -E0x200  Right vGlob2").OnEvent( "Change", rfCalc_EditRoutine )

    MyGui.SetFont(Font["TextNormal"]*)
    MyGui.Add("Text", "x+0 yp w24 0x200 Right", "ml")

    GroupBox1.GetPos(,,,&H),    Y := (MyGui.MarginY * 2) + H   ;  adjusted Y pos for DPI scaling
    MyGui.SetFont(Font["TextItalic"]*)
    MyGui.Add("GroupBox", "xm  w180 h120 y" Y, "   Calculation  ")

    MyGui.SetFont(Font["TextUnderL"]*)
    MyGui.Add("Text", "xp+16 yp+24 w148 Section Right", "    Collected rain")

    MyGui.SetFont(Font["MonoNormal"]*)
    MyGui.Add("Edit", "xs y+0 wp-24 h18 -E0x200  Right vGlob3").OnEvent( "Change", rfCalc_EditRoutine )

    MyGui.SetFont(Font["TextNormal"]*)
    MyGui.Add("Text", "x+0 yp w24 0x200 Right", "ml")

    MyGui.SetFont(Font["TextUnderL"]*)
    MyGui.Add("Text", "xs y+12 w148 Right", "         Rainfall")

    MyGui.SetFont(Font["MonoNormal"]*)
    MyGui.Add("Edit", "xs y+0 wp-24 h18 -E0x200  Right vGlob4").OnEvent( "Change", rfCalc_EditRoutine )

    MyGui.SetFont(Font["TextNormal"]*)
    MyGui.Add("Text", "x+0 yp w24 0x200 Right", "cm")

    static vals := [
        IniRead(A_ScriptDir "\rfCalc.ini", "rfCalc", "Dia", ""),
        IniRead(A_ScriptDir "\rfCalc.ini", "rfCalc", "Vol", ""),
        "",
        ""
    ]

    If vals[1]
       MyGui["glob3"].Focus()

    MyGui.OnEvent("Close",  rfCalc_Close)
    MyGui.OnEvent("Escape", rfCalc_Close)

    MyGui.Show("AutoSize")
    return MyGui
    
    rfCalc_Close(MyGui) {
        IniWrite(myGui["glob1"].value, A_ScriptDir "\rfCalc.ini", "rfCalc", "Dia")
        IniWrite(myGui["Glob2"].value, A_ScriptDir "\rfCalc.ini", "rfCalc", "Vol")
    }


    rfCalc_EditRoutine(ctrl, *) {
        Static PI := 3.14159265358979
        Local  R, Rmm, Dia, Vol, Col
        
        glob1 := ctrl.gui["glob1"]
        glob2 := ctrl.gui["glob2"]
        glob3 := ctrl.gui["glob3"]
        glob4 := ctrl.gui["glob4"]

        Switch( ctrl )
        {
               Case glob1:
               {
                     If ! IsNumber( Dia := glob1.value )
                          Return

                     R   := Dia/2
                     Vol := PI * R * R
                     glob2.value := Round(Vol, 6)
                     glob3.value := ""
                     glob4.value := ""
               }


               Case glob2:
               {
                     If ! IsNumber( Vol := glob2.value )
                          Return

                     Dia := Sqrt(Vol/PI) * 2
                     glob1.value := Round(Dia, 6)
                     glob3.value := ""
                     glob4.value := ""
               }


               Case glob3:
               {
                     If ! IsNumber( Dia := glob1.value )
                     || ! IsNumber( Vol := glob2.value )
                     || ! IsNumber( Col := glob3.value )
                          Return

                     glob4.value := Round(Col/Vol, 4 )
               }


               Case glob4:
               {
                     If ! IsNumber( Dia := glob1.value )
                     || ! IsNumber( Vol := glob2.value )
                     || ! IsNumber( Rmm := glob4.value )
                          Return

                     glob3.value := Round(Vol*Rmm, 4 )
               }
        }      ; end switch
    }
}
EDIT: A couple more corrections. Based on this script alone, you probably shouldn't be recreating the gui every time the function is called anyways. Just call it once and return the Gui object. Then for your hotkey, just show the gui.
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

02 Aug 2021, 15:57

TheArkive wrote:
02 Aug 2021, 13:56
In your case I would create 1000 GUIs, and destroy them, in succession a few times.
Tried that about 10 times. Seems OS releases memory at its own pace.
This does help:

Code: Select all

DllCall("kernel32.dll\SetProcessWorkingSetSize", "ptr",-1, "ptr",-1, "ptr",-1)
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

02 Aug 2021, 16:17

kczx3 wrote:
02 Aug 2021, 14:30
@SKAN I guess I don't see why you need to do it that way.
I don't have any particular way. I'm just experimenting with whatever that may work. :)
I'm aware V2 provides better ways to do things, but there isn't enough examples out there in our forum, yet.
Don't have your _Close and _EditRoutine functions be out in the global scope. Just put them at the bottom of your rfCalc function as nested functions (i.e. closures). That avoids the whole need for attaching some random prop to your Gui instance. Or as Lexikos stated, make your function a class that extends Gui but I think the nested function route will be easier for you to start with.
Class is a bit hard for me to understand. Closure seems easy now as you have provided a demo. Thank you.
Based on this script alone, you probably shouldn't be recreating the gui every time the function is called anyways. Just call it once and return the Gui object. Then for your hotkey, just show the gui.
This app needs to be multi-instance, for me.
There is a system-wide sound equalizer app which needs to be single instance and I will write it accordingly.

Thanks again for all the help. :)
lexikos
Posts: 9494
Joined: 30 Sep 2013, 04:07
Contact:

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

06 Aug 2021, 17:58

SKAN wrote:
02 Aug 2021, 13:21
MyGui.MyProp := ObjPtrAddRef(Glob)
What made you choose to store it by raw pointer? It is less convenient and more error-prone. It gives you control over reference counting, such as to avoid a circular reference, but you aren't utilizing that. The reference count is the same as if you assigned MyGui.MyProp := Glob and just used MyGui.DeleteProp("MyProp") or MyGui.MyProp := "" to free it. Regardless of how you store Glob, there aren't any problematic circular references because Glob only contains controls, not the GUI itself, and each control has an uncounted reference to its GUI.

If your cleanup code doesn't run, such as if the GUI is destroyed without being closed, the raw object pointer will not be released.
MyGui.DeleteProp("MyProp") ; Is necessary?
If you just assign MyGui.MyProp := Glob and don't do anything to free it, it will be freed when MyGui is deleted. If the script retains a reference to the GUI after the window is destroyed, the object still won't be deleted until the script releases its reference. But you probably want to avoid that anyway since the GUI object itself occupies memory and is useless after the window is destroyed. Resources associated with the controls (aside from the control object itself) are freed when the GUI is destroyed, even if you retain references to the control objects.

If Glob contained a counted reference to the GUI, there would be a circular reference which would prevent either object from being deleted. In that case, deleting the property explicitly when you close the GUI would be necessary to break the circular reference.

When MyProp just contains an integer, deleting the property just frees a very small amount of memory, and is redundant if the GUI object itself is about to be freed.
Glob := "" ; Is necessary?
It's a local variable and you are about to return from the function, so no.
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: [2.0-beta.1] Is it valid to set property of A_Args array?

07 Aug 2021, 04:42

@lexikos

I have been reading doc in parts and try things. It will take a while for me to get the full picture.
Meanwhile, such elaborate explanations are very helpful. Thank you.

Return to “Ask for Help (v2)”

Who is online

Users browsing this forum: bj8tr, MagEpub, niCode and 31 guests