Function calls on Menu.Add items Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
Inspector
Posts: 27
Joined: 29 Jul 2019, 05:17

Function calls on Menu.Add items

Post by Inspector » 08 Sep 2022, 08:41

Hello everyone,

can anybody please explain what am I actually supposed to do in order to be able to call a function from a menu? I even made a class, but still get an error:
"Error: Parameter #2 of Menu.Prototype.Add requires an object, but received an empty string."
Here is my current testing code:

Code: Select all

#SingleInstance Force


myclass := CustomClass()
context := Menu()
context.add "Print Name", myclass.PrintName()
context.add "Print Name Length", myclass.CountName()

Pause::context.Show 
F3::myclass.PrintName()
F4::myclass.CountName()

class CustomClass {
    ClassName := "Awesome Class"

    PrintName() {
        MsgBox(This.ClassName)
    }

    CountName() {
        MsgBox("The string length is " . StrLen(This.ClassName))
    }
}
The method works fine when I use a hotkey, though.

I just want to optimize the code because I used to spam different functions for a menu item, although the difference was only in one word. It looked like this:

Code: Select all

context.add my_email, mymail
context.add his_email, hismail
mymail(*)
	{
	Send my_email
	return
	}
hismail(*)
	{
	Send his_email
	return
	}

Descolada
Posts: 1098
Joined: 23 Dec 2021, 02:30

Re: Function calls on Menu.Add items  Topic is solved

Post by Descolada » 08 Sep 2022, 10:07

Menu.Add expects a function, but you are providing the result of the function: myclass.PrintName() calls PrintName() and the result (which is nothing) will be passed to Menu.Add.
To get around this you need to create a BoundFunc: context.add "Print Name", ObjBindMethod(myclass, "PrintName")
You can also use Bind(), but then you need to bind the implicit "this" as well (which in your case is myclass): context.add "Print Name", myclass.PrintName.Bind(myclass)

You also need to modify your receiving functions to receive arguments (and optionally ignore the arguments): PrintName(*)
Though I am unsure why you need to do this, since the documentation says
The function can optionally define parameters as shown below:

FunctionName(ItemName, ItemPos, MyMenu)
"Optionally" implies that one could also use PrintName(), but that throws an error...

Inspector
Posts: 27
Joined: 29 Jul 2019, 05:17

Re: Function calls on Menu.Add items

Post by Inspector » 08 Sep 2022, 13:14

Thank you very much for your help! It now finally works, although I did not really grasp the Bind() option - specifically why 'myclass' is a param for the function.
Descolada wrote:
08 Sep 2022, 10:07
You can also use Bind(), but then you need to bind the implicit "this" as well (which in your case is myclass): context.add "Print Name", myclass.PrintName.Bind(myclass)

And to sum up, the code with both working options:
Spoiler

iPhilip
Posts: 796
Joined: 02 Oct 2013, 12:21

Re: Function calls on Menu.Add items

Post by iPhilip » 08 Sep 2022, 13:33

Inspector wrote:
08 Sep 2022, 08:41
Hello everyone,

can anybody please explain what am I actually supposed to do in order to be able to call a function from a menu? I even made a class, but still get an error
If you didn't need the class you can simply call the functions from a menu as follows:

Code: Select all

context := Menu()
context.add "Print Name", PrintName  ; PrintName is a Function Object
context.add "Print Name Length", CountName  ; CountName is a Function Object

Pause::context.Show 
F3::PrintName()
F4::CountName()

PrintName(*) {
   MsgBox A_ThisFunc
}

CountName(*) {
   MsgBox A_ThisFunc
}
Cheers!
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)

lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Function calls on Menu.Add items

Post by lexikos » 08 Sep 2022, 17:10

Inspector wrote:
08 Sep 2022, 13:14
... I did not really grasp the Bind() option - specifically why 'myclass' is a param for the function.
Each method definition creates a Func with a hidden first parameter named this, and defines a property which is used to call the method or retrieve its function object.
Source: Objects - Definition & Usage | AutoHotkey v2
The value on the left hand side of . becomes the this parameter of the function which implements the method. This is how methods know which object they apply to.

Descolada wrote:
08 Sep 2022, 10:07
You also need to modify your receiving functions to receive arguments (and optionally ignore the arguments): PrintName(*)
Though I am unsure why you need to do this, since the documentation says
The function can optionally define parameters as shown below:

FunctionName(ItemName, ItemPos, MyMenu)
"Optionally" implies that one could also use PrintName(), but that throws an error...
This documentation is left over from previous versions which ignored excess parameters passed to functions.

Though it's also true that PrintName(*) does not define any parameters, and it works, therefore defining the parameters is optional.

Inspector
Posts: 27
Joined: 29 Jul 2019, 05:17

Re: Function calls on Menu.Add items

Post by Inspector » 12 Sep 2022, 14:21

@iPhilip
You are right, I can call function like that but my point was to use different arguments arguments with the same function, like this:

Code: Select all

context := Menu()
context.add "Print Name", PrintName("Sample Text 1") 
context.add "Print Name", PrintName("Sample Text 2") 

Pause::context.Show 

PrintName(TextToPrint) {
   MsgBox TextToPrint
}
But this option doesn't work, and Descolada has gladly explained why above (I marked his comment as a solution).

lexikos wrote:
Inspector wrote:
08 Sep 2022, 13:14
... I did not really grasp the Bind() option - specifically why 'myclass' is a param for the function.
Each method definition creates a Func with a hidden first parameter named this, and defines a property which is used to call the method or retrieve its function object.
Source: Objects - Definition & Usage | AutoHotkey v2
The value on the left hand side of . becomes the this parameter of the function which implements the method. This is how methods know which object they apply to.
Thank you for the input. Do you think I could suggest some additional examples for the documentation as to the use of the functions with Menu.Add? Because the existing information was not enough for the dummy me :roll:

iPhilip
Posts: 796
Joined: 02 Oct 2013, 12:21

Re: Function calls on Menu.Add items

Post by iPhilip » 12 Sep 2022, 15:06

Inspector wrote:
12 Sep 2022, 14:21
@iPhilip
You are right, I can call function like that but my point was to use different arguments arguments with the same function, like this:

Code: Select all

context := Menu()
context.add "Print Name", PrintName("Sample Text 1") 
context.add "Print Name", PrintName("Sample Text 2") 

Pause::context.Show 

PrintName(TextToPrint) {
   MsgBox TextToPrint
}
But this option doesn't work, and Descolada has gladly explained why above (I marked his comment as a solution).
It can work with some minor changes. See below:

Code: Select all

context := Menu()
context.add "Print Name 1", PrintName.Bind("Sample Text 1") 
context.add "Print Name 2", PrintName.Bind("Sample Text 2") 

Pause::context.Show

PrintName(TextToPrint, *) {
   MsgBox TextToPrint
}
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)

Inspector
Posts: 27
Joined: 29 Jul 2019, 05:17

Re: Function calls on Menu.Add items

Post by Inspector » 26 Sep 2022, 14:31

iPhilip wrote:
12 Sep 2022, 15:06

It can work with some minor changes. See below:

Code: Select all

PrintName(TextToPrint, *) {
   MsgBox TextToPrint
}
I don't understand why it works when the function parameters include the ,*. When it is just TextToPrint it fails, although I don't have and don't need other parameters?

safetycar
Posts: 435
Joined: 12 Aug 2017, 04:27

Re: Function calls on Menu.Add items

Post by safetycar » 26 Sep 2022, 15:08

It's related to the parameters mentioned in the first reply. The example where that is uses Bind, putting TextToPrint before all other parameters, but the others are still there. Ahk fills those when you click the item.

iPhilip
Posts: 796
Joined: 02 Oct 2013, 12:21

Re: Function calls on Menu.Add items

Post by iPhilip » 04 Oct 2022, 14:58

@Inspector, In case @safetycar's response wasn't clear enough, let me add the following.

The second argument of the Menu.Add() method should be a Function Object or a Menu. In the above cases, PrintName and PrintName.Bind(String) are both function objects, the latter being more specifically a BoundFunc Object.

As stated in the documentation, if a function object is specified, the function should be defined as follows:

Code: Select all

FunctionName(ItemName, ItemPos, MyMenu)
Thus, you could define the PrintName function as:

Code: Select all

PrintName(TextToPrint, ItemName, ItemPos, MyMenu) {
   MsgBox TextToPrint
}
as it would satisfy the requirement that all parameters, including the bound one, are specified.

It is important to note that, unlike AutoHotkey v1, you cannot omit the parameters of a function. That's why this version of the function fails:

Code: Select all

PrintName(TextToPrint) {
   MsgBox TextToPrint
}

The * character is a convenient way to tell AutoHotkey: "ignore all remaining parameters of the function as I won't be using them". Several examples in the Menu documentation page show the use of the * character to do that. In my opinion, the best description of its use is in the OnMessage documentation page:
https://lexikos.github.io/v2/docs/commands/OnMessage.htm#The_Functions_Parameters wrote:You can omit one or more parameters from the end of the list if the corresponding information is not needed, but in this case an asterisk must be specified as the final parameter. For example, a function defined as MyMsgMonitor(wParam, lParam, *) would receive only the first two parameters, and one defined as MyMsgMonitor(*) would receive none of them.
Thus, the short version of the PrintName function can be written as:

Code: Select all

PrintName(TextToPrint, *) {
   MsgBox TextToPrint
}
I hope this helps.
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)

AHK_user
Posts: 515
Joined: 04 Dec 2015, 14:52
Location: Belgium

Re: Function calls on Menu.Add items

Post by AHK_user » 06 Oct 2022, 02:55

I am missing a fat arrow example in this discussion. This works great with the Menu object.

I would advice this as it is way more flexible and you do not need to adapt the parameters of the PrintName function.

Code: Select all

context := Menu()
context.add("Print Name1", (*) => (PrintName("Sample Text 1")) 
context.add("Print Name2", (*) => (PrintName("Sample Text 2")) 

Pause::context.Show 

PrintName(TextToPrint) {
   MsgBox TextToPrint
}
"(*) =>" could also be replaced by "(ItemName, ItemPos, MenuName) =>" if the parameters are used.

Post Reply

Return to “Ask for Help (v2)”