Change Script Features on the Fly with the Windows System Tray Icon Context Menu (AutoHotkey Tip)

Dynamically Switch the Actions of a Running AutoHotkey Script with a Simple Click of a System Tray Right-Click Menu Item

From Rick Corbett:

Hi, Jack,

Talking of “Adding Actions to Windows System Tray Icon Menu“, perhaps you would consider writing about using—for example—ToggleCheck, MenuItemName to amend a running script dynamically, i.e. change something (like toggling logging to a file versus a MsgBox), then reload.

*          *          *

AutoHotkey Library Deal
AutoHotkey Library Deal

That’s an excellent question, Rick! Often, after loading a script,  we want to either turn features off and on or change how they function. Adding separate Hotkeys or rewriting scripts becomes wearisome. But, what if we could add a feature to the Windows System Tray Icon Context Menu (right-click) which either toggles an action on and off or completely changes how it works? AutoHotkey offers a straightforward way to get it done.

Adding a Toggling Checkmark to a Menu

Three AutoHotkey Menu command parameters either add a checkmark to a menu item, remove it, or both (excerpted from the online documentation):

Check, MenuItemName: Adds a visible checkmark in the menu next to MenuItemName (if there isn’t one already).

Uncheck, MenuItemName: Removes the checkmark (if there is one) from MenuItemName.

ToggleCheck, MenuItemName: Adds a checkmark if there wasn’t one; otherwise, removes it.

When using any of these Menu commands in conjunction with the System Tray icon, the code takes the form of:

Menu, Tray, ToggleCheck, MenuItemName

While this command turns the checkmark on and off with each use, from the perspective of the AutoHotkey user, the command merely adds or removes the checkmark icon. Nothing else happens without more code. Plus, while many other AutoHotkey commands offer built-in variables containing the state of specific settings, the Menu command does not tell you anything about the checked position (i.e. yes/no) of a menu item. That means you must build your own routine to determine the state of a checked (or unchecked) menu item.

Note: More complex Windows techniques will return the state of a checked menu item, but it’s simpler to build your own routine employing a toggling variable.

From Lexikos (Steve Gray) developer of AutoHotkey_L—the current official version of AutoHotkey:

DllCall and the Win32 API can be used to determine the checked state of a menu, but it is usually much easier to maintain a variable.

The other two Check commands:

Menu, Tray, Check, MenuItemName

and

Menu, Tray, Uncheck, MenuItemName

independently add and remove the checkmark, respectively, while on each use of the menu command, ToggleCheck reverses the item’s checked state. In most cases, ToggleCheck results in the desired action while eliminating one or more lines of code.

Tracking the State of a Toggling Checked Menu Item

The easiest way to ascertain the state of a checked menu item involves tracking the value rather than attempting to detect the state. That means creating a variable to hold that value (checked or unchecked). Since the ToggleCheck command automatically switches the value of the menu item, the variable must simultaneously do the same. (The toggle used here acts in a similar fashion to the toggle routines discussed in the previous blogs “Toggling AutoHotkey Hotkeys On and Off ” and “AutoHotkey Toggles and the Ternary Operator“, plus the book AutoHotkey Hotkey Techniques.) This checked state tracking only requires a couple of lines of code, but first, we must add

togglecheckmark
The new Save Date menu item toggles the checkmark on and off.

the new menu item to the auto-execute section of the script:

 

Menu, tray, add, Save Data, SaveData

The new System Tray icon item (Save Data) appears at the bottom of the menu. However, a checkmark cannot be added until issuing the Check or ToggleCheck command.

Place the ToggleCheck command inside the called label (subroutine) SaveData:—along with the tracking variable (SaveOn). This pairs and syncs the checkmark action with the setting of the toggling variable.

The subroutine SaveData includes only two lines of code:

SaveData:
  Menu, Tray, ToggleCheck, Save Data
  SaveOn := !SaveOn  ; classic variable toggle
Return

The first line (Menu, Tray, ToggleCheck, Save Data) turns the checkmark on and off on the menu item. Click Save Data and AutoHotkey adds a checkmark. Click it again to remove the checkmark.

Since the ToggleCheck command partners with the second line of code (SaveOn := !SaveOn), the variable toggles true/false with the menu item. As long as no external routines affect either the setting of the checkmark or the variable value, the checked status of the menu item always matches the toggling variable’s value.

Note: Since the variable does not exist until the first click of the menu item, its value necessarily defaults to 0 or false. With the first running of the subroutine, the variable sets to 1 or true through this classic toggle switch (SaveOn is set equal to (=:) not (!) SaveOn). Thus, if the menu item is checked, then SaveOn is true. If unchecked, SaveOn is false.

Alternative Actions

Now, we can use the state of the variable SaveOn to alter the operation of the script:

^!w::
If SaveOn
  {
    FileAppend , %A_now% New Data, datafile.txt
  }
Else
  {
    MsgBox, Data not logged!
  }
Return

Depending upon the value of the variable SaveOn, the above Hotkey combination CTRL+ALT+W (^!w) activates either (If SaveOn) the logging of the date and time (A_Now) with the phrase “New Data” to the text file datafile.txt using the FileAppend command or (Else) runs the MsgBox command. If checked, the menu item Save Data logs data. Otherwise, the MsgBox command displays, “Data not logged!”

By substituting alternative actions in the two sections of the If-Else conditional above, any AutoHotkey script can change its features on-the-fly by clicking the appropriate item in the System Tray icon right-click context menu. Putting all the pieces together in one script yields the following in three parts:

; Part One: Add the menu item
Menu, tray, add, Save Data, SaveData
Return    ; remove Return command for checked default

; Part Two: Include the check toggle tracking routine
SaveData:  ; tracking the checked status
  Menu, tray, togglecheck, Save Data
  SaveOn := !SaveOn 
Return

; Part Three: Substitute alternative actions
^!w::      ; alternative actions
If SaveOn
  {
    FileAppend , %A_now% New Data, datafile.txt
  }
Else
  {
    MsgBox, Data not logged!
  }
Return

First, add the menu item to the System Tray icon menu. Second, add the subroutine which includes the ToggleCheck command for checking and unchecking the menu, plus a synchronized toggling variable. Third, include an action routine which switches script functions depending upon the state of the checked menu item.

Tip: Upon loading, you can force the menu item to default to checked by adding the two toggling code lines to the auto-execute portion of the script. However, placing the check tracking routine just after the auto-execute section while removing the auto-execute terminating Return command provides the same effect without adding two extra lines of code. Without the auto-execute Return in place, the script, when loading, drops pass the SaveData label and runs the subroutine— adding a checkmark to the menu item and setting SaveOn to true. Remember, any code—whether in the auto-execute section or a label (subroutine)—continues executing successive lines until encountering the Return or Exit command, a Hotkey, or Hotstring.

jack

Advertisements

3 thoughts on “Change Script Features on the Fly with the Windows System Tray Icon Context Menu (AutoHotkey Tip)

  1. Hi Jack,

    That’s great… excellent explanation! It was what I was struggling with, as quite often I find it difficult to follow the documentation on the AutoHotkey site. My apologies for the delay in commenting but, thanks to you, I’ve now got the hang of it for what I call ‘static’ scripts, i.e. triggered by a hotkey.

    Unfortunately I still can’t get my head around where in the script to add the tray, menu commands to a running program that just sits there doing nothing but loop through a sequence of actions rather than triggered by a hotkey.

    Is it possible you could provide an example with explanation?

    Background:
    After a huge amount of trial and error (mostly error ) I now have an AHK script that monitors an online forum. For several reasons (not least of which is its length) I can’t post the code publicly (although I have no problem sending via email) but basically it started off as this:

    Start loop
    {
    Refresh web page
    Check metadata for possible condition
    If condition detected
    {
    Warn via MsgBox
    Log
    }
    Else
    {
    Log
    }
    Sleep for a minute and start all over
    }

    The ‘log’ bit is because the loop stops after a while, usually after several hours, so I added a ‘log’ bit to record exactly when. (At first I thought the problem was with AHK but I now know that it’s the webpage. On occasion it doesn’t complete reloading so the script doesn’t receive the message from Internet Exploder to continue the loop… but that’s another matter.)

    The script worked great but I decided to check for more conditions and spiff up the ‘Warn’ routine to make it less obtrusive. I changed it to:

    If detected
    {
    Warn by flashing very small red square
    Log
    }
    Else
    {
    Warn by flashing very small green square
    Log
    }

    That worked great too.

    Then I decided to vary the size of the square because the very small flashing square was OK when right in front of me but I often missed it when it was on another monitor in my peripheral vision, i.e. I wanted to be able to vary the size depending on which monitor I was displaying it on. Then I thought ‘But what if I’m in the kitchen and can’t see either monitor? I know… I’ll add both a toggling square size AND a toggling SoundBeep’. See where I’m going?

    I can add the toggle actions to the tray menu if the script is static but I can’t work out how to do it with a running script once it’s started it’s main loop. I guess I’ll have to use the ‘Reload’ command after each toggle action but I can’t even get that far. The loop no longer loops once I add the tray menu commands so I’m obviously adding them incorrectly. I’ve tried dozens of different combinations over several days so far but to no avail. Basically I have no idea what needs to be in the autoexecute section, what needs to be further down but outside the loop and what needs to be inside the loop.

    Regards,

    Rick

    Like

  2. Hi, Rick,

    I wonder if you might not be better served with the SetTimer command rather than using a Loop which sleeps for one minute at a time. That way the routine can be set up to run every minute rather than continuously. If the Web page loads slowly on a previous thread, the new thread launched by SetTimer should interrupt the old thread and reload the page. This may also work better for the Tray menu since the routine does not run continuously.

    I think it’s worth investigation.

    -Jack

    Like

  3. Hi Jack,

    I hadn’t used SetTimer before so have been reading up on it, playing with the examples in the docs and searching the AHK forums for examples, problems, etc.

    My tiny test scripts to familiarise myself have been successful so I’m about to try with my much longer and more important ‘forum monitor’ script.

    Many thanks for your suggestion to use SetTimer and for your blogs. Much appreciated!

    Regards,

    Rick

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s