ENH: ensure window focus for the first win.flip() with pyglet backend on Windows OS#6874
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## release #6874 +/- ##
========================================
Coverage 49.62% 49.62%
========================================
Files 332 332
Lines 61201 61208 +7
========================================
+ Hits 30373 30377 +4
- Misses 30828 30831 +3
|
|
Lol just did more testing and figured out what's actually going on... Good news: the loss of focus and visible mouse do not occur naturally to any of the released Standalone PsychoPy distributions, including 2024.2.2. Everyone's behavioral response data are safe, yay! Bad news (for me): the reason why I was seeing the visible mouse after Oh well, at least I figured out the solution. If the psychopy team don't mind, I'd appreciate you merging the line into the next release. Calling the extra |
win.flip() with pyglet backend on Windows OS
|
These changes all look good, to give some context on You're right about the downside of attributeSetter being that values are sometimes returned as a method handle, it's a tradeoff we make for speed. I've tried to create workarounds for this in the past but with limited success. One option I've been meaning to try out is to use the |
|
@TEParsons thanks for the contextual info! Yeah if it's used frame-by-frame then we won't be worried about the attribute not being set before called... so it's probably better to use the more traditional @Property syntax on these experimental settings :) Thanks for merging quickly! |
|
Sounds like a good solution! Back in the days, I timed getting Here's the StackOverflow question where the attributeSetter solution came from. It includes some alternative solutions too: https://stackoverflow.com/questions/17576009/python-class-property-use-setter-but-evade-getter The primary motivation was actually to be able to set attributes like |
|
Thanks for that additional insight @lindeloev !! I agree that it sounds like PsychoPy should continue to use the But for experiment settings like |
Alright, round two of mouse visibility PRs (first one was #6465). Hopefully this time the annoying mouse is gone for good.
Previously there was a buggy
draggable()event that caused visible mouse despite correct settings ofshowMouseandfullScreen. #6465 fixed it, and the mouse visibility is behaving correctly on macOS since then. Yesterday I started testing on Windows laptops and was horrified by the visible mouse again on standalone PsychoPy via builder.After some digging, I've fixed the issues around this. Here's a breakdown of the commits, which also improve how
mouseVisibleandfullscrattributes are stored inwindow.Window().__dict__[ATTRIBUTE_NAME]along with@attributeSetter def ATTRIBUTE_NAME(self). This setup has a dangerous wobbly attribute type forATTRIBUTE_NAME. Beforeself.ATTRIBUTE_NAME = xxxis called, queryingself. ATTRIBUTE_NAMEreturns an attributeSetter method; while after the attribute is set, it returns the actual attribute. This makes type setting and downstream usage ofself.ATTRIBUTE_NAMEunreliable since the attribute is not guaranteed to be set before called. It is better to use@property+@ATTRIBUTE_NAME.setter def ATTRIBUTE_NAME(self)to provide the same API without the problematic mutable type behavior. b6025f7 and 4a99206 implement this formouseVisibleandfullscr. In addition, sinceWindow()always has a backend that has amouseVisibleattribute already, it's better to haveWindow.mouseVisiblereturnself.backend.mouseVisibledirectly instead of keeping another attribute variable around, which may go out of sync with the backendmouseVisibleif something goes wrong and makes it difficult to debug.allowGUIparam toWindowconstructor. There appears to be some adhoc code added to attempt to set mouse visibility when generating scripts from builder. Since theallowGUIparameter is available and accepted towindow.Window(), we should use that instead of the extra manualwin.mouseVisible = allowGUI. This keeps thewin.allowGUIrelevant and avoids confusing syntax. This is done by d052c89.pygletbackend. I'm guessing thatwin.mouseVisible = allowGUIwas added because people found that passingallowGUI=Falseintowindow.Window()didn't hide the mouse. But clearly manually settingwin.mouseVisible = allowGUIdoesn't work either unless it is called every frame on Windows OS using thepygletbackend withWin32Windowwindow type.The core of this problem is a bit complicated: After instantiating the backend and the
Win32Windowhandle, the_hwndhas lost its foreground status by the time it gets to the very firstwin.flip()call that does not have anything in._to_draw(). This is due to the additional process started during starting ioHub server. This causes the PsychoPy window to temporarily obtain focus viasetCurrent()duringwin.flip()and immediately lose the focus after, regardless of whetherwinis on focus before thiswin.flip()call. Therefore, now the PsychoPy screen has lost focus despite still being visible.Due to the way
pygletWin32Windowupdates_mouse_platform_visibleusing a sequence oforstatements:, the mouse now stays visible, since the window no longer has focus.
UPDATE: this problem only affects very few people. If your mouse is not visible when experiment starts, you are probably fine! This is actually a very vulnerable behavior beyond the annoying visible mouse, unless users have always clicked mouse at least once on the PsychoPy screen to force grab focus and bring it to foreground from the OS level before doing anything else. Since I'm running without a mouse, this causes the focus to stay on the builder view. And sadly I'm using the
spacebarto advance through PsychoPy screens, but now the keypress is double registered to the builder view, which maps to "Run Experiment" - so as I'm running a PsychoPy task, it silently spawned another experiment behind the screen... Similarly spacebar and any other keypress could cause unwanted consequences in this situation due to the messed up focus, including clearing the Runner output panel, etc., etc.There are many different ways to fix this issue, but the cleanest solution only requires one extra line of code before the
win.flip()and is provided in b295a89.@peircej @TEParsons I think this is a critical PR that should be pushed out soonish, since it can impact people using Standalone PsychoPy via builder on Windows OS, unless they have developed the "good" habit of clicking the PsychoPy window every time they start an experiment. I unfortunately found that the lost focus issue could sometimes result in missing the first keyboard press on each new screen, and only the second keypress gets registered (with the first keypress gaining focus temporarily but losing it immediately after). This issue will impact logged behavioral outputs, so it'll be good to fix.