Mbee Posted May 10, 2018 Share Posted May 10, 2018 Hi! I've read and presumably understood the very fine Interrupting a running function wiki and @Melba23's example solutions to this dilemma, but I need additional help to solve my problems.,, What may be happening is that certain button clicks are being either repeated or lost. In practice, I'll press a button to display the next or previous image (which takes a moderate amount of time), but on moderately rare and unpredictable cases, the same image is displayed instead. The next/previous functions increment or decrement an index into an array, then display the new image, but curiously that must not be happening. because the displayed image stays the same. But there's no other code path to displaying any image, so I figure the only way that can happen is if next/previous click is being lost. (I'm using Event Mode) I'm assured that Windows button click events are queued, but that seems not to be happening in this case. So I tried to queue these button events myself, which may or may not be happening (it seems not). So that's when I studied the wiki page above, suspecting that these occasional clicks are being lost because the inc/dec & display function is still running when the new click occurs. As Melba23 and the Wiki (kinda) suggested, I'm using a dummy accelerator as the interrupt handler and a logical flag to indicate that an interrupt occurred. But I obviously I didn't get the setting and clearing of the flag right, because as soon as one of the two functions are called, it enters an infinite loop waiting for the flag to be cleared. But I can't figure out the correct solution. At last, here's some code samples... expandcollapse popup; The following is in the Main code $G_DummyAccelCtrlID = GUICtrlCreateDummy() GUICtrlSetOnEvent($G_DummyAccelCtrlID, "_AdvanceBtn_AccelHandler") Local $L_AccelKeyAra[1][2] = [["!+z", $G_DummyAccelCtrlID]] GUISetAccelerators( $L_AccelKeyAra, <the correct GUI window handle> ) Func ExampleAdvanceFunc() ; _NextClick() or _PreviousClick() If $G_AdvanceClickActive Then _QueuePicAdv( $Gc_NextClicked ) ; Queue a Next Click While $G_AdvanceClickActive Sleep (50) WEnd EndIf $G_AdvanceClickActive = True ; <Code to adjust index and display image> $Lf_Stat = _DeQueuePicAdv() ; Checks if another advance button was queued If $Lf_Stat <> -1 Then ; -1 = Nothing queued If $Lf_Stat = $Gc_NextClicked Then ; If another Next button was queued... $G_AdvanceClickActive = False _NextClick() Else $G_AdvanceClickActive = False _PreviousClick() EndIf EndIf $G_AdvanceClickActive = False Return EndFunc Func _QueuePicAdv( $arg_PicAdvanceDirection ) If $G_PicAdvClickQueueIdx < $Gc_PicAdvClickQueueMax Then $G_PicAdvClickQueue[$G_PicAdvClickQueueIdx] = $arg_PicAdvanceDirection $G_PicAdvClickQueueIdx += 1 Else MsgBox($MB_OK, "title", "_QueuePicAdv -- Pic Advance Queue Overrun! - Exiting") Exit EndIf Return EndFunc Func _DeQueuePicAdv() Local $Lf_AdvanceDirection = -1 ; Default is nothing queued If $G_PicAdvClickQueueIdx > 1 Then $G_PicAdvClickQueueIdx -= 1 $Lf_AdvanceDirection = $G_PicAdvClickQueue[$G_PicAdvClickQueueIdx] EndIf Return $Lf_AdvanceDirection EndFunc Func _AdvanceBtn_AccelHandler() $G_AdvanceClickActive = True Return EndFunc Please let me know what I'm doing wrong and how to fix it. Thanks! Link to comment Share on other sites More sharing options...
Mbee Posted May 10, 2018 Author Share Posted May 10, 2018 Oh, sorry, I forgot to at least try to make the code runnable. The following runs, but does NOT reproduce the problem! Anyway, here ya go... expandcollapse popup#include <MsgBoxConstants.au3> #include <GuiButton.au3> #include <GUIConstants.au3> Opt( "GUICloseOnESC", 1 ) Opt( "GUIOnEventMode", 1 ) ; The following is in the Main code Global Const $Gc_PicAdvClickQueueMax = 10, $Gc_PreviousClicked = 1, $Gc_NextClicked = 2 Global $G_Index = 0, $G_AdvanceClickActive = False Global $G_PicAdvClickQueue[$Gc_PicAdvClickQueueMax], $G_PicAdvClickQueueSize=0, $G_PicAdvClickQueueIdx=0 Global $G_GuiHandle, $G_NextBtnID, $G_PreviousBtnID Global $G_DummyAccelCtrlID = GUICtrlCreateDummy() GUICtrlSetOnEvent($G_DummyAccelCtrlID, "_AdvanceBtn_AccelHandler") Local $L_AccelKeyAra[1][2] = [["!+z", $G_DummyAccelCtrlID]] GUISetAccelerators( $L_AccelKeyAra ) $G_GuiHandle = GUICreate( "ClickTestGUI", 120, 100 ) GUISetOnEvent( $GUI_EVENT_CLOSE, "_GUIWinClose", $G_GuiHandle ) $G_NextBtnID = GUICtrlCreateButton( "Next", 10, 50 ) GUICtrlSetOnEvent(-1,"_NextClick") $G_PreviousBtnID = GUICtrlCreateButton( "Previous", 50, 50 ) GUICtrlSetOnEvent(-1,"_PreviousClick") GUISetState( @SW_SHOW ) While True Sleep( 100 ) WEnd Exit Func _NextClick() ConsoleWrite( @CRLF & "Next called - Current Index = " & $G_Index ) If $G_AdvanceClickActive Then ConsoleWrite( @CRLF & "Next called while $G_AdvanceClickActive is True" ) _QueuePicAdv( $Gc_NextClicked ) ; Queue a Next Click While $G_AdvanceClickActive Sleep (50) WEnd EndIf $G_AdvanceClickActive = True ; <Code to adjust index and display image> $G_Index += 1 $Lf_Stat = _DeQueuePicAdv() ; Checks if another advance button was queued If $Lf_Stat <> -1 Then ; -1 = Nothing queued If $Lf_Stat = $Gc_NextClicked Then ; If another Next button was queued... ConsoleWrite( @CRLF & "A Queued Next Click was Dequeued in _NextClick()" ) $G_AdvanceClickActive = False _NextClick() Else ConsoleWrite( @CRLF & "A Queued Previous Click was Dequeued in _NextClick()" ) $G_AdvanceClickActive = False _PreviousClick() EndIf EndIf $G_AdvanceClickActive = False Return EndFunc Func _PreviousClick() ; _NextClick() or _PreviousClick() ConsoleWrite( @CRLF & "Previous called - Current Index = " & $G_Index ) If $G_AdvanceClickActive Then ConsoleWrite( @CRLF & "Previous called while $G_AdvanceClickActive is True" ) _QueuePicAdv( $Gc_PreviousClicked ) ; Queue a Previous Click While $G_AdvanceClickActive Sleep (50) WEnd EndIf $G_AdvanceClickActive = True ; <Code to adjust index and display image> If $G_Index > 0 Then $G_Index -= 1 $Lf_Stat = _DeQueuePicAdv() ; Checks if another advance button was queued If $Lf_Stat <> -1 Then ; -1 = Nothing queued If $Lf_Stat = $Gc_NextClicked Then ; If another Next button was queued... ConsoleWrite( @CRLF & "A Queued Next Click was Dequeued in _PreviousClick()" ) $G_AdvanceClickActive = False _NextClick() Else ConsoleWrite( @CRLF & "A Queued Previous Click was Dequeued in _PreviousClick()" ) $G_AdvanceClickActive = False _PreviousClick() EndIf EndIf $G_AdvanceClickActive = False Return EndFunc Func _QueuePicAdv( $arg_PicAdvanceDirection ) If $G_PicAdvClickQueueIdx < $Gc_PicAdvClickQueueMax Then $G_PicAdvClickQueue[$G_PicAdvClickQueueIdx] = $arg_PicAdvanceDirection $G_PicAdvClickQueueIdx += 1 Else MsgBox($MB_OK, "title", "_QueuePicAdv -- Pic Advance Queue Overrun! - Exiting") Exit EndIf Return EndFunc Func _DeQueuePicAdv() Local $Lf_AdvanceDirection = -1 ; Default is nothing queued If $G_PicAdvClickQueueIdx > 1 Then $G_PicAdvClickQueueIdx -= 1 $Lf_AdvanceDirection = $G_PicAdvClickQueue[$G_PicAdvClickQueueIdx] EndIf Return $Lf_AdvanceDirection EndFunc Func _AdvanceBtn_AccelHandler() $G_AdvanceClickActive = True Return EndFunc Func _GUIWinClose() Exit EndFunc But since the above doesn't reproduce the problem, and since my code is about 10,000 lines of code, so I cannot post it, I don't know how to proceed... Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted May 10, 2018 Moderators Share Posted May 10, 2018 Mbee, That code is severely recursive - you are calling functions from within themselves - and I would hazard a guess that is where you are "losing" events as AutoIt really does queue them for you. Try recasting the code so that you return to your idle loop before calling any function - and certainly the same one. This simple example allows to see that AutoIt does indeed queue button events - it counts up and down quite nicely for me: expandcollapse popup#include <GUIConstantsEx.au3> Opt( "GUIOnEventMode", 1 ) Global $iClickCounter = 1 $G_GuiHandle = GUICreate( "ClickTestGUI", 120, 100 ) GUISetOnEvent( $GUI_EVENT_CLOSE, "_GUIWinClose", $G_GuiHandle ) $G_NextBtnID = GUICtrlCreateButton( "Next", 10, 50 ) GUICtrlSetOnEvent(-1,"_NextClick") $G_PreviousBtnID = GUICtrlCreateButton( "Previous", 50, 50 ) GUICtrlSetOnEvent(-1,"_PreviousClick") GUISetState( @SW_SHOW ) While True Sleep(10) WEnd Func _NextClick() $iClickCounter += 1 If $iClickCounter > 10 Then $iClickCounter = 10 ConsoleWrite("Counter: " & $iClickCounter & @CRLF) Sleep(2000) ; Simulate the delay while the new picture is displayed EndFunc Func _PreviousClick() $iClickCounter -= 1 If $iClickCounter < 1 Then $iClickCounter = 1 ConsoleWrite("Counter: " & $iClickCounter & @CRLF) Sleep(2000) ; Simulate the delay while the new picture is displayed EndFunc Func _GUIWinClose() Exit EndFunc M23 Mbee 1 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
Mbee Posted May 10, 2018 Author Share Posted May 10, 2018 Aach! What a dummy I am! Thanks, Melba! I totally spaced on the stupid crazy recursion Ninny! Why didn't I see that? As I was falling asleep a short time ago (I often think more clearly then than while active), I recalled from the Wiki tutorial and your code elsewhere about the need to do part of the code in the main Main routine, as you pointed again just now. You wrote: "Try recasting the code so that you return to your idle loop before calling any function - and certainly the same one." But I don't know how to adapt my code to do that, since I absolutely need On Event Mode, thus no event loop. Do I switch modes when necessary? And under what situations? Or is this one of the situations which AutoIt can't deal with as far as interrupting a running function? As far as my blinkered recursion errors, off the top of my head it occurs to me that instead of those recursive calls, perhaps I could send an appropriate Windows message to simulate a next/previous click which would get queued and eventually trigger the appropriate button click handler (unless there's a better way). Well, I better go back to bed. I'll check in tomorrow. Thanks again! Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted May 10, 2018 Moderators Share Posted May 10, 2018 Mbee, You still have an idle loop even in OnEvent mode - or else the script would just end! ; OnEvent idle loop While True Sleep(10) WEnd So you need to make sure that you return to this loop - i.e. terminate a function before you recall the same function. And I still fail to see why you need to send any Windows messages - AutoIt is quite happy dealing with the standard Windows message stream and will happily queue events for you, as the example script I posted above shows. I get the impression that you are serious overthinking this problem when a perfectly simple solution is out there waiting for you to stumble over it. Logically I see it as something like this: Global _ImageToShowNext variable Global _ImageShowingNow variable Advance button fires _Advance function Previous button fires _Previous function Start of idle loop keeping script alive Sleep(10) Look to see if _ImageToShowNext is different to _ImageShowingNow If it is - show the required picture via the _ShowImage function Match the 2 variables End of idle loop _Advance function Increase the _ImageToShowNext variable - what else do you need to do? _Previous function Decrease the _ImageToShowNext variable - what else do you need to do? _ShowImage function Show whatever image has been passed as a parameter That would seem to do the trick. Multiple rapid presses of the buttons should be queued and then processed in turn as the previous _ShowImage function ends. M23 Mbee 1 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
Mbee Posted May 10, 2018 Author Share Posted May 10, 2018 You're absolutely right, of course. My only excuse is that I started writing this thing 3-4 years ago when I was an even more ignorant newb than I am today! And not very long later I stopped development for a number of reasons, including the fact that the code was always, always crashing in a GDI+ function that no one understood. But fairly recently I contacted you for help on the code that was calling that GDI+ function, which was trying to calculate the maximum number of characters that would fit into a text field of a given number of pixels and all the font information. That's when I had a flash insight about how to do that (technically, estimate that) extremely easily, by a combination of your StringSize and simple division. So that's when I finally resumed development again, by which point I was substantially more experienced and knowledgeable on AutoIt and all the various UDFs. And I figured that I'd leave as much as I had done years ago largely alone, which obviously was a mistake. Anyway, with your gracious advice and assistance, the seemingly "missing" click events no longer happen, and I'm a very happy camper. Thank you enormously for your patience and generosity! Link to comment Share on other sites More sharing options...
Mbee Posted May 11, 2018 Author Share Posted May 11, 2018 (edited) Damn! It actually is still losing clicks after all! How do I know? I created a folder of 60 images whose names are just numbers in sequential order. Then I started viewing them and clicked Next fairly fast, while counting the number of times I clicked it. If no Next clicks were being lost, then when I finished clicking, the file name/number should be exactly equal the number of times I clicked + 1. But when I test it, the final file number being displayed is anywhere from 5 to 15 lower than it should be -- it never caught up, demonstrating that clicks are being lost. NOTE that I tested this first without any queuing/dequeuing. But when that was such a failure, I added that capability (as seen below), which actually undeniably helped, but not enough. If I click slowly enough, everything's fine. But when I click even moderately fast such that I click Next while a previous image is being displayed (which can take a while, especially for animated GIFs), some Next clicks certainly appear to not be queued. So I added some code to watch Next and Previous clicks and queue them myself if needed, such that I would dequeue them in the main loop. But again, the following code does not reproduce the problem. expandcollapse popup#include <MsgBoxConstants.au3> #include <GuiButton.au3> #include <GUIConstants.au3> ; The following included UDF can be found here: https://www.autoitscript.com/forum/topic/64738-mouseonevent-udf/ ; I'm showing the standard include, but because I want X64 mode and the standard UDF doesn't work in X64, I added the ; workaround code provided by "LarsJ" on the very last page of that thread and called it ""MouseOnEvent64.au3", ;~ #include "MouseOnEvent.au3" #include "MouseOnEvent64.au3" Opt( "GUICloseOnESC", 1 ) Opt( "GUIOnEventMode", 1 ) Global Const $Gc_PicAdvClickQueueMax=10, $Gc_PreviousClicked=1, $Gc_NextClicked=2 Global $G_PicAdvClickQueueIdx=0, $G_PicAdvClickQueue[$Gc_PicAdvClickQueueMax] Global $G_AdvanceClickActive = False, $G_CurFileIndex = 0, $G_LastFileIndex = 0, $G_CUR_PICADV_INDEX=0, $G_TOTAL_PICADV_CLICK_COUNT=0 Global $G_GuiHandle = GUICreate( "ClickTestGUI", 120, 100 ) GUISetOnEvent( $GUI_EVENT_CLOSE, "_GUIWinClose", $G_GuiHandle ) Global $G_NextBtnID = GUICtrlCreateButton( "Next", 10, 50 ) GUICtrlSetOnEvent(-1,"_NextClick") Global $G_PreviousBtnID = GUICtrlCreateButton( "Previous", 50, 50 ) GUICtrlSetOnEvent(-1,"_PreviousClick") GUISetState( @SW_SHOW ) Local $Lf_Stat = _MouseSetOnEvent($MOUSE_PRIMARYUP_EVENT, "_MyLeftClickHandler" ) While True Sleep(20) ; Sleep for 20 milliseconds If $G_CurFileIndex <> $G_LastFileIndex Then If $G_CurFileIndex > $G_LastFileIndex Then _NextClick() Else _PreviousClick() EndIf EndIf $Lf_Stat = _DeQueuePicAdv() ; Checks if another pic advance button was queued If $Lf_Stat <> -1 Then ; -1 = Nothing queued If $Lf_Stat = $Gc_NextClicked Then ; If another Next button was queued... ConsoleWrite( @CRLF & "Main -- Found a QUEUED 'NEXT' Click'" ) _NextClick() Else ConsoleWrite( @CRLF & "Main -- Found a QUEUED 'PREVIOUS' Click'" ) _PreviousClick() EndIf EndIf WEnd ; = = = = = = = = = = = = = = = = = = = Func _NextClick() $G_AdvanceClickActive = True ; We're currently processing an advance click ConsoleWrite( @CRLF & "Next called - Current Index = " & $G_CurFileIndex ) $G_CurFileIndex += 1 ; Try the next index $G_LastFileIndex = $G_CurFileIndex For $i = 0 To 10000 ; Simulate the CPU time needed to display the image Local $Lf_Junk = Cos( Random( 0.1, 360.0 ) ) Next $G_AdvanceClickActive = False Return EndFunc ;==>NextClick ; = = = = = = = = = = = = = = = = = = = Func _PreviousClick() $G_AdvanceClickActive = True ConsoleWrite( @CRLF & "Previous called - Current Index = " & $G_CurFileIndex ) If $G_CurFileIndex > 0 Then $G_CurFileIndex -= 1 ; Try the previous index $G_LastFileIndex = $G_CurFileIndex For $i = 0 To 10000 ; Simulate the CPU time needed to display the image Local $Lf_Junk = Cos( Random( 0.1, 360.0 ) ) Next EndIf $G_AdvanceClickActive = False Return EndFunc ; = = = = = = = = = = = = = = = = = = = Func _QueuePicAdv( $arg_PicAdvanceDirection ) If $G_PicAdvClickQueueIdx < $Gc_PicAdvClickQueueMax Then $G_PicAdvClickQueue[$G_PicAdvClickQueueIdx] = $arg_PicAdvanceDirection $G_PicAdvClickQueueIdx += 1 Else ConsoleWrite( @CRLF & "_QueuePicAdv -- Pic Advance Queue Overrun! - Exiting") Exit EndIf Return EndFunc ; = = = = = = = = = = = = = = = = = = = Func _DeQueuePicAdv() Local $Lf_AdvanceDirection = -1 ; Default is nothing queued If $G_PicAdvClickQueueIdx > 1 Then $G_PicAdvClickQueueIdx -= 1 $Lf_AdvanceDirection = $G_PicAdvClickQueue[$G_PicAdvClickQueueIdx] EndIf Return $Lf_AdvanceDirection EndFunc ; = = = = = = = = = = = = = = = = = = = Func _MyLeftClickHandler() Local $Lf_ControlID Local $Lf_CursorInfo = GUIGetCursorInfo() $Lf_ControlID = $Lf_CursorInfo[4] If ($Lf_ControlID = $G_PreviousBtnID) Or ($Lf_ControlID = $G_NextBtnId) Then $G_TOTAL_PICADV_CLICK_COUNT += 1 If $G_AdvanceClickActive Then If $Lf_ControlID = $G_PreviousBtnId Then $G_CUR_PICADV_INDEX -= 1 _QueuePicAdv( $Gc_PreviousClicked ) Else $G_CUR_PICADV_INDEX += 1 _QueuePicAdv( $Gc_NextClicked ) EndIf EndIf EndIf Return $MOE_RUNDEFPROC EndFunc ; = = = = = = = = = = = = = = = = = = = Func _GUIWinClose() Exit EndFunc Please let me know if I've made more stupid mistakes... Download Modified MouseOnEvent for x64 mode: MouseOnEvent64.au3 Unfortunately, I don't know how to give you runnable code that does reproduce the problem. Edited May 11, 2018 by Mbee Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted May 11, 2018 Moderators Share Posted May 11, 2018 Mbee, The fact you are having problems getting a reproducer script to demonstrate the effect makes me pretty sure that you are removing these events from the message queue in a section of your code somewhere in the original larger script. AutoIt does not eat events - or else we would have many such complaints, which we do not. Unless you can provide some code which does show the same effect - or let us see the whole script (and pray we can spot something) - I am afraid that we cannot really help you any further. M23 mikell 1 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now