Slider control's A_GuiEvent = 4 is unreliable

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by pneumatic » 30 Aug 2017, 09:45

Nightwolf85 wrote:What was wrong with:
You seem to have skipped right over our suggestions.
I have trouble understanding whether your solution is suitable for my script as I need my sliders to fire immediately on every event and also on mouse release, and I couldn't figure out if yours does that, or what I would have to add.
I think your solution would be harder to integrate into my script; I'd have to create a new WatchSlider for each of my (~200) sliders.

edit: tested it out and it seems to work fine. I just don't understand it , read it about 10 times and my brain is refusing :roll:

Nightwolf85
Posts: 302
Joined: 05 Feb 2017, 00:03

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by Nightwolf85 » 30 Aug 2017, 10:07

pneumatic wrote:edit: tested it out and it seems to work fine. I just don't understand it , read it about 10 times and my brain is refusing :roll:
Every time the slider's g-label is called If the reason is 4 or Normal ( Mouse release or at the end of any other reason is always a repeated normal ) it will run your update one last time, and soundplay. Otherwise it will watch the slider and update whenever it's value changes.

I can see how if you have more than one slider, and you need to do something different depending on which slider is being moved, can complicate things, but what solution do you already use for that? I will try to get a sample version working with two different sliders and see if I can help with that.

Edit:
I have an example for using two sliders and doing something different with each:

Code: Select all

#Persistent
#SingleInstance Force
SetBatchLines , -1

DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
Sleep 1000
DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
Global 1ms := (CounterAfter - CounterBefore) / 1000

Gui, 1:New
Gui, Add, Slider, ToolTip AltSubmit x10 y10 w720 Range-35-35 vSlider1 gSliderG ; Note all sliders point to same g-label
Gui, Add, Slider, ToolTip AltSubmit x10 y50 w720 Range-35-35 vSlider2 gSliderG
Gui, Add, Text, vaText w250 x300 yp+50
Gui, Show, y200 w740 h150, Slider Test (Window 1)
Gui, 2:New
Gui, Add, Slider, ToolTip AltSubmit x10 y10 w720 Range-35-35 vSlider1 gSliderG ; Note all sliders point to same g-label
Gui, Add, Slider, ToolTip AltSubmit x10 y50 w720 Range-35-35 vSlider2 gSliderG
Gui, Add, Text, vaText w250 x300 yp+50
Gui, Show, y400 w740 h150, Slider Test (Window 2)
Return


SliderG:
IF (!IsObject(useFunction))
	useFunction := Func("watchSlider").Bind(A_Gui, A_GuiControl) ; Only way I know how to use setimer on a function with parameters. Doesn't work if you keep overwriting the binding, so only do it if the binding doesn't already exist.
IF (A_GuiEvent = 4 || A_GuiEvent = "Normal") {
	SetTimer, % useFunction, -1 ; -1 instead of Off to have it update on the final position.
	AlreadyWatching := False
	useFunction := "" ; Clear the function Binding.
	Soundplay , *64
}
Else IF !(AlreadyWatching) {
	SetTimer, % useFunction, 1
	AlreadyWatching := True
}
Return

watchSlider(guiName, controlSlide) {
	Static prevVal
	Gui, %guiName%:Default
	GuiControlGet, sliderVal,, %controlSlide%
	IF (sliderVal != prevVal) {
		IF (controlSlide = "Slider1") { ; Put whatever you use to differentiate different sliders here using controlSlide
			DllCall("QueryPerformanceCounter", "Int64*", CounterBefore)
			counter := 0
			loop 1500000
			{
			counter += 1
			}
			DllCall("QueryPerformanceCounter", "Int64*", CounterAfter)
			GuiControl,, aText, % "Subroutine duration: " . Round(((CounterAfter - CounterBefore) / 1ms)) . "ms"
			prevVal := sliderVal
			ToolTip, %sliderVal% ; Just an example, to show how to use the current value of the slider, without needing to submit the GUI.
		}
		Else IF (controlSlide = "Slider2") {
			ToolTip, You're Moving Slider 2 of GUI:%guiName%
		}
	}
	Return
}
Edit: Updated to make prevVal static, and showing how it works with 2 separate GUIs.
Last edited by Nightwolf85 on 30 Aug 2017, 13:32, edited 1 time in total.

pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by pneumatic » 30 Aug 2017, 10:34

Nightwolf85 wrote: Every time the slider's g-label is called If the reason is 4 or Normal ( Mouse release or at the end of any other reason is always a repeated normal ) it will run your update one last time, and soundplay. Otherwise it will watch the slider and update whenever it's value changes.
Got it, thanks.
Nightwolf85 wrote: I can see how if you have more than one slider, and you need to do something different depending on which slider is being moved, can complicate things, but what solution do you already use for that?
Still trying to figure it that out.

Nightwolf85
Posts: 302
Joined: 05 Feb 2017, 00:03

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by Nightwolf85 » 30 Aug 2017, 10:41

pneumatic wrote:Still trying to figure it that out.
Check out my posted example, seems relatively easy to manage. You'd get to keep all the different tasks in the same place, and it should work across different GUIs even.

pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by pneumatic » 30 Aug 2017, 22:35

Nightwolf85 wrote: Check out my posted example, seems relatively easy to manage. You'd get to keep all the different tasks in the same place, and it should work across different GUIs even.
I think your implementation is reliable in that it will always keep the slider position and gsub in sync regardless of how fast or when the mouse button is released, but there are 3 downsides to it:

1. it's a lot more code for me to implement (probably a whole day's work to adapt my script) whereas just adding a single WM_LBUTTONUP function and not having to change any of my slider subroutines is a lot less work
2. the gsub frequency is delayed by timer resolution (~15.6ms or whatever the windows time slice is - apparently this varies from system to system) so the updates to the xgraph are not as smooth
3. if the user's computer is slow, or another thread is interrupting (my script has about 2 other threads running, so it's a realistic possibility) or the user just happens to release mouse during the slider's subroutine, A_GuiEvent = 4/Normal will still be missed and the timer will not be turned off. edit4: possible workaround: inside watchSlider: , if prevVal = sliderVal , then turn the timer off.

But my implementation has problems also:

1. WM_LBUTTONUP is not guaranteed to "see" the right A_GuiControl value
2. WM_LBUTTONUP has higher priority than Critical threads and will interrupt them, which can lead to the sub interrupting itself. If this happened it would be really really bad for my script, disasterous in fact ( I believe this is why this problem exists to begin with - slider subroutines must not interrupt themselves otherwise we'd have bits and pieces of subroutine getting executed in some crazy unpredictable order as the slider constantly interrupts itself while being dragged - slider subroutines are effectively forced Critical , and WM_LBUTTONUP overrides critical).

Luckily I don't actually need to do the slider's subroutine on mouse release, but a different section of code which is safe to execute at any time, so I don't risk having the slider's sub interrupting itself, but I would still like the ability to call the slider's subroutine to ensure my script's state is perfectly in sync with the slider on mouse release.
Last edited by pneumatic on 31 Aug 2017, 04:24, edited 5 times in total.

pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by pneumatic » 31 Aug 2017, 00:55

tbh I'm not really satisfied with any of the solutions, they are all very convoluted and I don't have a lot of confidence that they aren't going to cause bugs in my program. The interaction with controls has to be air tight. The ordering of events has to be guaranteed. Luckily I realised I can just move my code elsewhere instead of executing on slider release so I might use that instead.

just me
Posts: 9574
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by just me » 31 Aug 2017, 01:06

As long as you don't show your code it's almost impossible to provide reliable alternative solutions.

pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by pneumatic » 31 Aug 2017, 01:36

just me wrote:As long as you don't show your code it's almost impossible to provide reliable alternative solutions.
If it works in the demo then it will work in my script.

just me
Posts: 9574
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by just me » 31 Aug 2017, 02:03

It does work in the demo. :roll:

pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by pneumatic » 31 Aug 2017, 04:16

just me wrote:It does work in the demo. :roll:
But not well enough for the reasons mentioned.

just me
Posts: 9574
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by just me » 31 Aug 2017, 04:54

Well,
1. it's a lot more code for me to implement (probably a whole day's work to adapt my script) whereas just adding a single WM_LBUTTONUP function and not having to change any of my slider subroutines is a lot less work
IMO, all you have to do is to move the XGraph related code into a timer and to add a few lines to the control label as well as the timer code.
2. the gsub frequency is delayed by timer resolution (~15.6ms or whatever the windows time slice is - apparently this varies from system to system) so the updates to the xgraph are not as smooth
You said that your current control label is running about 24 ms on your i7-4790k CPU. So a timer delay of max 15.6 ms doesn't matter.
3. if the user's computer is slow, or another thread is interrupting (my script has about 2 other threads running, so it's a realistic possibility) or the user just happens to release mouse during the slider's subroutine, A_GuiEvent = 4/Normal will still be missed and the timer will not be turned off.
I don't think this will happen, but if, the user would'nt have much pleasure for other reasons (e.g. the time needed to run the XGraph updates might increase dramatically).

pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by pneumatic » 31 Aug 2017, 07:49

just me wrote:You said that your current control label is running about 24 ms on your i7-4790k CPU. So a timer delay of max 15.6 ms doesn't matter.
Even though the documentation doesn't specifically say, I believe it's the timer's duration in addition to the slider's subroutine time which is actually occurring. The evidence of this is if you put the sound effect on for all slider events you can hear it beeps at a slower interval, and remember that's with the subroutine at 100ms (or whatever it produces on your cpu). If you lower the subroutine loop so it reports 24ms the difference is even more obvious. btw 24ms is a worse case scenario though for my script, usually it's around 10ms.
just me wrote:I don't think this will happen
There is nothing preventing it from happening. "Maybe your program won't crash" isn't good enough :D
Reliability: A timer might not be able to run as often as specified under the following conditions:
1.Other applications are putting a heavy load on the CPU
2.The timer subroutine itself takes longer than its own period to run, or there are too many other competing timers (altering SetBatchLines may help).
3.The timer has been interrupted by another thread, namely another timed subroutine, hotkey subroutine, or custom menu item (this can be avoided via Critical). If this happens and the interrupting thread takes a long time to finish, the interrupted timer will be effectively disabled for the duration. However, any other timers will continue to run by interrupting the thread that interrupted the first timer.
Timer = botch job :thumbdown:

The best solution would be for ahk to specially buffer a single iteration of the gosub on WM_LBUTTONUP .

Nightwolf85
Posts: 302
Joined: 05 Feb 2017, 00:03

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by Nightwolf85 » 31 Aug 2017, 09:04

Well I don't have any more assistance to give.
pneumatic wrote:1. it's a lot more code for me to implement (probably a whole day's work to adapt my script) whereas just adding a single WM_LBUTTONUP function and not having to change any of my slider subroutines is a lot less work
Wouldn't any solution to this problem require more code? and wouldn't you rather code in the best practice you can, rather than a quick band-aid. Also constantly monitoring WM_LBUTTONUP with OnMessage for the entire time your program is running doesn't sound very efficient either.

pneumatic wrote:2. the gsub frequency is delayed by timer resolution (~15.6ms or whatever the windows time slice is - apparently this varies from system to system) so the updates to the xgraph are not as smooth
I don't have any way to see this, or see how certain code changes effect it, so I guess I'll take your word, even though in playing around with the test code given I see no delay or jerkyness until the loop time gets up to ~300ms

pneumatic wrote:3. if the user's computer is slow, or another thread is interrupting (my script has about 2 other threads running, so it's a realistic possibility) or the user just happens to release mouse during the slider's subroutine, A_GuiEvent = 4/Normal will still be missed and the timer will not be turned off.
AND
Timer = botch job :thumbdown:
I started Prime95 and let it take up 100% of my CPU and ran my example, worked exactly how it should. Even when the loop was lowered dramatically, performance only improved so I'm not sure what you meant when you said that "lowering the subroutine loop so it reports 24ms the difference is even more obvious"




I'm with just me, if we can't see a better representation of your code where it isn't working we can't help come up with a different solution.

pneumatic
Posts: 338
Joined: 05 Dec 2016, 01:51

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by pneumatic » 31 Aug 2017, 23:03

Nightwolf85 wrote:wouldn't you rather code in the best practice you can, rather than a quick band-aid.
I agree thats why I ended up putting the code elsewhere on other controls in my script rather than using one of these band aids.
Nightwolf85 wrote: Also constantly monitoring WM_LBUTTONUP with OnMessage for the entire time your program is running doesn't sound very efficient either.
I was only monitoring it for the module of my script which uses sliders, but even with constant monitoring it still didn't turn out to be much overhead (~0.1ms of processing time on slider release to check if A_GuiControl was in the list of sliders I'm monitoring).
Nightwolf85 wrote:
pneumatic wrote:2. the gsub frequency is delayed by timer resolution (~15.6ms or whatever the windows time slice is - apparently this varies from system to system) so the updates to the xgraph are not as smooth
I don't have any way to see this, or see how certain code changes effect it, so I guess I'll take your word, even though in playing around with the test code given I see no delay or jerkyness until the loop time gets up to ~300ms
In further testing it seems it's just the sound effect that is getting delayed by timer duration - the actual code itself seems to be running fast if I measure the inter-sub delta. i.e even though the timer missed its next 15.6ms interval due to the slider sub being busy for 100ms, ahk is still buffering the waiting timer to execute immediately after the slider is finished, i.e the timer didn't get discarded and therefore didn't add an extra 15.6ms. But, i've noticed ahk has this behaviour where it sometimes can only buffer a maximum of 2 threads and some of that behaviour isn't documented, for example two threads that are both sleeping will prevent any other threads from interrupting. Also the behaviour when a critical thread tries to interrupt another critical thread isn't documented, but in my testing it doesn't interrupt and gets buffered (whether it's subject to the 2 thread limit I don't know). If the whole script is made critical then it seems to buffer a crap ton more. There is also the possibility of the mouse refresh rate causing a different number of events while dragging, and the number of ticks on the slider ("range") which also affects the sub frequency. eg. a long slider bar with only 35 ticks on it will trigger the sub a lot less than one with 100 ticks on it.

Nightwolf85 wrote: I'm not sure what you meant when you said that "lowering the subroutine loop so it reports 24ms the difference is even more obvious
What I meant was the difference between 100ms and 115.6ms is less noticeable than 24ms vs 39.6ms. But since ahk appears to be buffering the next timer iteration (on the proviso the 2 thread limit isn't in effect) then it shouldn't behave like that anyway.
Nightwolf85 wrote: if we can't see a better representation of your code where it isn't working we can't help come up with a different solution.
I wasn't actually asking for help though (I didn't post this in the ask for help section) but I appreciate your assistance.

Nightwolf85
Posts: 302
Joined: 05 Feb 2017, 00:03

Re: Slider control's A_GuiEvent = 4 is unreliable

Post by Nightwolf85 » 01 Sep 2017, 06:43

pneumatic wrote:I wasn't actually asking for help though (I didn't post this in the ask for help section) but I appreciate your assistance.
Ah so it was moved here. I had no way of knowing that, sorry.

Post Reply

Return to “Ask for Help (v1)”