Click-through window will blink due setWindowFlags
-
Hello! I'm new to PyQt and also I'm writing my first python app. It's pretty simple, an invisible root window with floating children windows containing images. These windows can sometimes have lowered opacity and be set to click through so the user can draw through them on their painting or 3D program of choice.
I'm using
Qt.WindowType.WindowTransparentForInput
to make the windows click-through, it was the only method that worked. The issue is that setting window flags automatically hides them, forcing me toshow()
them. The resulting blink looks like a glitch, it's really bad.Is there any other way to make windows transparent to at least left-clicks that wouldn't involve hiding and showing the window again?
If not I've came up with a workaround for the blinking. It's a convoluted illusion and sometimes doesn't work either:
- I'm taking a screenshot of the window with flags about to be altered.
- That window is covered with a new one with the screenshot to create the illusion it's still there.
- The appearance of the placeholder window fires a signal for the flag to be set. The real window behind it disappears and is shown again.
- The reveal of the real window fires a second signal for the placeholder be hidden.
Unfortunately, it turns out that
isVisible()
only informs someone set a window to be show, not that it's actually drawn. It always returnsTrue
when there's no placeholder yet.I've set the first signal to fire when the mouse enters the placeholder then, as it'll be shown on top of the old window. This
Enter
event the very last event fired when the placeholder shows up, and it still doesn't guarantee the paint event was done. 1 in out of 10 times the signal will manage to fire before the placeholder is ready. I'm having the same issue when restoring the real window.These are the events for the placeholder:
Type.ZOrderChange (placeholder) - is spontaneous: False Type.Show (placeholder) - is spontaneous: False Type.CursorChange (placeholder) - is spontaneous: False Type.ShowToParent (placeholder) - is spontaneous: False Type.ZOrderChange (placeholder) - is spontaneous: False Type.Paint (placeholder) - is spontaneous: True Type.UpdateLater (placeholder) - is spontaneous: False Type.UpdateRequest (placeholder) - is spontaneous: False Type.Paint (placeholder) - is spontaneous: True Type.Enter (placeholder) - is spontaneous: False
There's always an
UpdateRequest
before the finalPaint
event, then it firesEnter
as the fake window pops up under the cursor. The real window restore is similar, but without the finalEnter
since it's being forced to appear under the placeholder. None of these events seems to guarantee the window was rendered, this that it was requested.Ideas? Either an alterative method to make windows click-through without hiding them or a way to detect painting was done on a window would help.
On, and I'm using PyQt6 on Windows, but am aiming to make the app multiplatform.
-
I've set this aside for a while but started to poke at it again. Looks like there is another method that could help with setting flags without hiding the window:
QtWidgets.QWidget.overrideWindowFlags( WindowFlags )
"Sets the window flags for the widget to flags, without telling the window system.
Do not call this function unless you really know what you’re doing."Sounds ideal if I can control when the window system gets told about the new flags. And how do you notify it?
¯\(ツ)/¯
I've been scouring the internet to fulfil that "know what you're doing" warning, but there are no detailed docs, no actual examples. I've only found a thread from 2009 with zero replies. No wonder no one seems to be using it. Doing a simple stay on top test looks like doing nothing at all:
flags = self.windowFlags() flags |= Qt.WindowType.WindowStaysOnTopHint self.overrideWindowFlags(flags)
The window won't disappear, but it won't update either. I've tried to repaint, update, even hide and show but it's no good. I've not found any pyqt native method to notify the window system, and if I'm supposed to use low level messages from another modules to notify then overrideWindowFlags isn't that useful. I might just setflags with them as well.
P.s.: Besides hiding the widget, a regular
setWindowFlag
will remove the winId and reassign it, then set the icon again. Override doesn't do any of this. I've been fumbling with the winId then, but again the lack of methods is getting in the way. -
I run into the same issue. Except I am using Pyside2. Is there any way to fix this issue? Calling update hides the window. So after setWindowFlags function, I have to call show function but like OP it flickers. Not sure why this sort of design is implemented. Can this be remedied in a future version or is there a workaround? My code is like this:
def setPassThroughMode(window, enable): if enable: # Set the window to be transparent for input window.setWindowFlags(QtCore.Qt.Tool | QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowTransparentForInput) else: # Restore the normal floating window behavior window.setWindowFlags(QtCore.Qt.Tool | QtCore.Qt.FramelessWindowHint) window.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents, enable) window.setAttribute(QtCore.Qt.WA_NoMousePropagation, enable) # Ensure changes take effect window.show() # Reapply visibility to update flags def togglePassThroughMode(window): # Check if the window is valid if not shiboken2.isValid(window): return # Check if the window is in pass-through mode isPassThrough = isPassThroughEnabled(window) # Toggle the state using setPassThroughMode setPassThroughMode(window, not isPassThrough) # To turn it off, you have to call the function twice if isPassThrough: setPassThroughMode(window, not isPassThrough)
I have to call it twice to turn these flags off also, not sure why that is.