1

I am setting a window to always display on top using the following command in Python. I would like this setting to be toggleable, however I am not able to "unset" this property once it has been set.

import cv2
import numpy as np

frame = np.zeros((240, 320, 3), np.uint8)
window = "Test Window"

def show_window(window, frame):
    print(cv2.getWindowProperty(window, cv2.WND_PROP_TOPMOST))
    while(True):
        cv2.imshow(window, frame)
        if cv2.waitKey() > 0:
            break

cv2.namedWindow(window)

cv2.setWindowProperty(window, cv2.WND_PROP_TOPMOST, 1)
show_window(window, frame)

cv2.setWindowProperty(window, cv2.WND_PROP_TOPMOST, 0)
show_window(window, frame)

This prints out

1.0
1.0

and the window remains in a top-most state (other windows won't appear above even if they have focus).

4
  • 1
    Did you actually run your minimal reproducible example before posting it? Because that's going to crash on the third line (for two separate reasons, one being undefined variable, the other non-existent window), and lacks a cv2.waitKey... not to mention not really demonstrating the actual problem. (Something like this does tho: pastebin.com/raw/VJTNfGMD -- it even suggests that the property remains at value of 1.0 after the second call to setWindowProperty... interesting...) Commented Jun 28, 2024 at 21:34
  • 1
    I'm confident this is a bug. Writing an answer with a work-around, and I'll file an issue since the patch is quite trivial it seems. Commented Jun 28, 2024 at 22:01
  • 1
    I presume this is happening on MS Windows? (Geez, how did this get through the staging ground, I though that was meant to avoid such issues...) Commented Jun 28, 2024 at 22:12
  • 1
    No, @toyota-Supra, this has nothing to do with NumPy (it's just needed to make a dummy image to display), nor computer vision (just a bug in a utility function in a library). No need to add more tag abuse. Commented Jun 29, 2024 at 0:48

1 Answer 1

1

This looks like a bug, so let's prove it.

The implementation looks like this:

static bool setPropTopmost_(CvWindow& window, bool topmost)
{
    HWND flag    = topmost ? HWND_TOPMOST : HWND_TOP;
    BOOL success = SetWindowPos(window.frame, flag, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

    // The rest is not relevant...
}

The first statement is key -- to enable the top-most state, it uses HWND_TOPMOST, and to disable it, it uses HWND_TOP and herein lies the problem.

If we refer to the documentation of SetWindowPos, we can see that for HWND_BOTTOM it does mention that

... If the hWnd parameter identifies a topmost window, the window loses its topmost status and is placed at the bottom of all other windows.

However, it does not say this for HWND_TOP, only that it places the window on the top. Furthermore, it mentions later on the page that (emphasis mine)

If a topmost window is repositioned to the bottom (HWND_BOTTOM) of the Z order or after any non-topmost window, it is no longer topmost.

Finally, there is the pseudo-handle HWND_NOTOPMOST described as (again emphasis mine)

Places the window above all non-topmost windows (that is, behind all topmost windows).

Therefore I conclude, that HWND_TOP is an incorrect way of making a window no longer top-most. Instead, HWND_NOTOPMOST should be used. This hypothesis can be easily verified using PyWin32, which lets us use Windows API directly from Python. We simply replace the cv2.setWindowProperty by a FindWindow followed by SetWindowPos with the right pseudo-handle.

import cv2
import numpy as np
import win32gui
import win32con

frame = np.zeros((240, 320, 3), np.uint8)
window = "Test Window"

def show_window(window, frame):
    print(cv2.getWindowProperty(window, cv2.WND_PROP_TOPMOST))
    while(True):
        cv2.imshow(window, frame)
        if cv2.waitKey() > 0:
            break

cv2.namedWindow(window)

cv2.setWindowProperty(window, cv2.WND_PROP_TOPMOST, 1)
show_window(window, frame)

wnd_handle = win32gui.FindWindow(None, window)  
win32gui.SetWindowPos(wnd_handle, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE + win32con.SWP_NOSIZE);

show_window(window, frame)

This prints

1.0
0.0

and indeed after pressing a key to get into the second stage, the window no longer remains top-most.

The window is top-most

The window is no longer top-most


Bug reported: https://github.com/opencv/opencv/issues/25833

PR submitted: https://github.com/opencv/opencv/pull/25836

And merged, should be in release 4.11.

Sign up to request clarification or add additional context in comments.

2 Comments

@IInspectable I don't follow. I definitely don't expect the Z order to change. I expect the style to go away, and it doesn't with HWND_TOP. (it still has the TOPMOST style, and behaves as such)
True, I misread this proposed answer. It does look like HWND_TOPMOST sets the WS_EX_TOPMOST window style and HWND_NOTOPMOST unsets it. All other (pseudo-)handles have no effect on this window style, and HWND_TOP is indeed the wrong one to use. I'll just delete that comment.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.