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.


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.
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 of1.0after the second call tosetWindowProperty... interesting...)