Sign in to follow this  
Followers 0
MilesAhead

Empty TrayGetMsg queue

4 posts in this topic

#1 ·  Posted (edited)

Here's a simple technique I came up with to empty the TrayGetMsg queue on return from a blocking function. A small demo script is shown below.

Let's say you have a Tray hotkey app where a window pops up when the user double clicks the tray icon, or hits a hotkey. The hotkey can easily be screened out by using a flag on entry to the blocking function. In the code below I use a global $windowOpen as the flag to tell me the blocking function is active. Could be a popup gui that's open or an about box.

The problem lies when you have double click on the icon set to do the same thing. Just checking the flag won't work because when you pop down your window or about box, you set the flag to False. The TrayGetMsg has the mouse events queued up. So next time through the loop, $openWindow is False, it keeps popping up the window or about dialog for every double click that happened while the function blocked.

I used to avoid this by disabling the tray icon and using a delay to avoid double clicking what moved to the position vacated by the tray icon. This is messy and a bit disconcerting to the user.

Now I just leave the tray icon alone and drain the event queue as shown in the loop.

;=================================================================
;
; AboutDemo - demonstrates drain of TrayGetMsg queue on
; return from blocking funciton.
;
; MilesAhead
;=================================================================
#include <Constants.au3>
AutoItSetOption("TrayMenuMode", 3)
$aboutitem = TrayCreateItem("About Demo")
TrayCreateItem("")
$exititem = TrayCreateItem("Quit")
TrayItemSetState($aboutitem, $TRAY_DEFAULT)
TraySetState()

Global $windowOpen = False
Global $demoHotKey = "{F8}"

HotKeySet($demoHotKey, "_About")

While 1
    $msg = TrayGetMsg()
    Select

        Case $msg = $aboutitem
            _About()
            ; on return from blocking function, drain the queue
            Do
                $msg = TrayGetMsg()
            Until $msg = 0
            ContinueLoop

        Case $msg = $exititem
            Exit
    EndSelect
WEnd

Func _About()
    If $windowOpen Then Return
    $windowOpen = True
    MsgBox(0x1040, "About Demo", "Dummy Message")
    $windowOpen = False
EndFunc   ;==>_About
Edited by MilesAhead

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

MilesAhead,

I used to use your suggested technique from time to time on both GUIGetMsg and TrayGetMsg, but with experience I have found that it is much better coding practice to disable the affected controls/HotKeys while the blocking function is running - if for no other reason than it makes clear what is going on. :unsure:

Your code would then look like this:

#include <Constants.au3>

Opt("TrayMenuMode", 3)

$aboutitem = TrayCreateItem("About Demo")
TrayCreateItem("")
$exititem = TrayCreateItem("Quit")
TrayItemSetState($aboutitem, $TRAY_DEFAULT)
TraySetState()

Global $demoHotKey = "{F8}"

HotKeySet($demoHotKey, "_About")

While 1
    $msg = TrayGetMsg()
    Select
            Case $msg = $aboutitem
            _About()
        Case $msg = $exititem
            Exit
    EndSelect
WEnd

Func _About()
    TrayItemSetState($aboutitem, $TRAY_DISABLE) ; <<<<<<<<<<<<<<<<<
    HotKeySet($demoHotKey)                      ; <<<<<<<<<<<<<<<<<
    MsgBox(0x1040, "About Demo", "Dummy Message")
    TrayItemSetState($aboutitem, $TRAY_ENABLE)  ; <<<<<<<<<<<<<<<<<
    HotKeySet($demoHotKey, "_About")            ; <<<<<<<<<<<<<<<<<
EndFunc   ;==>_About

If you need to disable/enable a number of controls/keys then a function setting the requried state for them all would make the coding easier. ;)

M23

Edit: Rereading this I fear you could take it as simply belittling your example. I did not mean to do that - just to offer an alternative which has, over the years, proved to be a better way to do things. :>

Edited by Melba23

Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

#3 ·  Posted (edited)

I tried using TrayItemSetState but it doesn't work for the default item since the system will queue up mouse double-clicks while the function is blocking. I also tried changing the state before calling the function, and restoring it on return. Didn't work for me. The Tray Menu did not update.

afa the hotkey disable, I did that in the past. But there's no need since it will interrupt what's going on and therefore all I need is a flag ($windowOpen.) If the window is already open just return.

Seems simpler and neater to do it my way. But we are programmers because we want to do it our own way. :unsure:

If you have a system modal window popped up in the middle of the screen and the user is trying to execute other commands in the menu, to me that's just trying to break code where a double-click may be inadvertent.

Also, other than the $windowOpen flag, my method centralizes the queue emptying code in the msg loop handling instead of scattering it all over functions.

Another item in your About example, I tend to cut & paste about and ini read write functions from app to app, just changing the particulars. I don't like to enable and disable hotkeys inside the function the hotkey is calling. I like to have each function as stand-alone as possible. A test for a global $windowOpen and a reset at the end of the func is pretty reusable if I have a global $windowOpen in each app. Again easier to cut & paste.

To me the problem is simplest as draining the queue. Not rerouting the program flow just so the queue won't accumulate events while the app is busy. Kind of cart before the horse to my philosophy.

edit: For your purposes you may prefer your method. However my utilities tend to have 3 or 4 dialogs, change the hotkey, about box, main program interface etc., where I only want one to be showing. Also I may have 3 or 4 hotkeys for the one app. Using the flag technique to ensure only one window is open at a time I think is cleaner for my use. I don't have to disable several hotkeys and menu commands only to enable them again a bit later.

But I would also admit either method is preferable to the way I was doing it.. hiding the Tray Icon. Having the icons jump around in the tray is bad news. I'm going to fix all my old utilities that used that method eventually to use the event queue drain instead.

There's usually several ways to skin the cat and I appreciate your comments. :>

Edited by MilesAhead

Share this post


Link to post
Share on other sites

After more work it turns out I didn't implement this correctly. The problem with draining the Tray Icon event queue inside the loop is that the flag $windowOpen is set to False on return from blocking functions. The offending mouse double click can still work its way into the mischief.

The alternative I came up with is a function to drain the queue to be called just before setting $windowOpen flag to False in each function. I've added it to my file of general purpose functions. For this demo I pasted the function at the bottom.

;=================================================================
;
; AboutDemo - demonstrates _EmptyTrayQ() function and
; $windowOpen flag to avoid reentering blocking functions
; due to Tray Icon queueing of events.
;
; MilesAhead
;=================================================================
#include <Constants.au3>
AutoItSetOption("TrayMenuMode", 3)
$aboutitem = TrayCreateItem("About Demo")
TrayCreateItem("")
$exititem = TrayCreateItem("Quit")
TrayItemSetState($aboutitem, $TRAY_DEFAULT)
TraySetState()

Global $windowOpen = False
Global $demoHotKey = "{F8}"

HotKeySet($demoHotKey, "_About")

While 1
    $msg = TrayGetMsg()
    Select

        Case $msg = $aboutitem
            _About()

        Case $msg = $exititem
            Exit
    EndSelect
WEnd

Func _About()
    If $windowOpen Then Return
    $windowOpen = True
    MsgBox(0x1040, "About Demo", "Dummy Message")
    ; drain the tray icon queue before setting flag to False
    _EmptyTrayQ()
    $windowOpen = False
EndFunc   ;==>_About

; Empty Tray Icon Queue of events
Func _EmptyTrayQ()
    Local $m = 0
    Do
        $m = TrayGetMsg()
    Until $m = 0
EndFunc   ;==>_EmptyTrayQ

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0