cvui icon indicating copy to clipboard operation
cvui copied to clipboard

Window Close button Issue

Open lukadt opened this issue 7 years ago • 9 comments

If you try to use the Native Window Close button, the window is not responding. This behaviour can be reproduced in every cvui example. I workaround and was able to make borderless window using classic tricky windows api calls. Basically I modified the watch function (and lose linux compatibility with the inclusion of windows.h ). So if @Dovyski or other users find this code useful feel free to use or modified to make it a general solution. Let me know your thoughts,

inline void watch(const cv::String& theWindowName, bool theCreateNamedWindow, bool createBorder) {
	cvui_context_t aContex;

	if (theCreateNamedWindow) {
		cv::namedWindow(theWindowName,0);

		if (!createBorder)
		{
			HWND win_handle = FindWindow(0, theWindowName.c_str());
			if (!win_handle)
			{
				printf("Failed FindWindow\n");
			}

			// Borderless
			SetWindowLong(win_handle, GWL_STYLE, GetWindowLong(win_handle, GWL_EXSTYLE) | WS_EX_TOPMOST);
			ShowWindow(win_handle, SW_SHOW);
		}

	}

	aContex.windowName = theWindowName;
	aContex.mouse.position.x = 0;
	aContex.mouse.position.y = 0;
	
	internal::resetMouseButton(aContex.mouse.anyButton);
	internal::resetMouseButton(aContex.mouse.buttons[RIGHT_BUTTON]);
	internal::resetMouseButton(aContex.mouse.buttons[MIDDLE_BUTTON]);
	internal::resetMouseButton(aContex.mouse.buttons[LEFT_BUTTON]);

	internal::gContexts[theWindowName] = aContex;
	cv::setMouseCallback(theWindowName, handleMouse, &internal::gContexts[theWindowName]);
}

cvui_window_issue

lukadt avatar Oct 10 '18 15:10 lukadt

That borderless window looks gorgeous! :smile: Thank you for sharing the code snippet.

Unfortunately, I can't afford to lose platform compatibility with any addition to cvui. However, I think your suggestion could be easily incorporated in a platform-specific guide/tutorial that I'm planning to add to the docs.

Dovyski avatar Oct 11 '18 06:10 Dovyski

I understand @Dovyski your point. No matter how would you solve the windows close buttons? In the meantime I have improved and fixed the code or borderless window. Use this :-)

inline void watch(const cv::String& theWindowName, bool theCreateNamedWindow, bool createBorder) {
	cvui_context_t aContex;

	if (theCreateNamedWindow)
	{
		if (createBorder)
		{
			cv::namedWindow(theWindowName);
		}
		else
		{
			cv::namedWindow(theWindowName, 0);
			HWND win_handle = FindWindowA(0, theWindowName.c_str());
			if (win_handle)
			{
				// Borderless
				SetWindowLong(win_handle, GWL_STYLE, GetWindowLong(win_handle, GWL_EXSTYLE) | WS_EX_TOPMOST);
				ShowWindow(win_handle, SW_SHOW);
			}
		}
	}

	aContex.windowName = theWindowName;
	aContex.mouse.position.x = 0;
	aContex.mouse.position.y = 0;
	
	internal::resetMouseButton(aContex.mouse.anyButton);
	internal::resetMouseButton(aContex.mouse.buttons[RIGHT_BUTTON]);
	internal::resetMouseButton(aContex.mouse.buttons[MIDDLE_BUTTON]);
	internal::resetMouseButton(aContex.mouse.buttons[LEFT_BUTTON]);

	internal::gContexts[theWindowName] = aContex;
	cv::setMouseCallback(theWindowName, handleMouse, &internal::gContexts[theWindowName]);
}

lukadt avatar Oct 11 '18 09:10 lukadt

Thanks, @lukadt !

Apparently, the native close button not being able to properly close the window is a limitation in OpenCV itself. I think it would be useful to add a function to cvui to help developers monitor if a window has been closed, so they can end the application accordingly.

As I said, I can only proceed with that if a solution is available on all platforms.

Dovyski avatar Oct 12 '18 06:10 Dovyski

Dear @Dovyski , I dig a little bit deeper into this issue and solved it using a suggestion found online. Basically my loop guard is like this on all the samples you provide:

       // Init cvui and tell it to create a OpenCV window, i.e. cv::namedWindow(WINDOW_NAME).
	cvui::init(WINDOW_NAME);

	//while cv2.getWindowProperty('window-name', 0) >= 0:
	while (cv::getWindowProperty(WINDOW_NAME, 0) >= 0)
	{
		// Fill the frame with a nice color
		frame = cv::Scalar(49, 52, 49);

		// Show some pieces of text.
		cvui::text(frame, 50, 30, "Hey there!");
                
               // This function must be called *AFTER* all UI components. It does
		// all the behind the scenes magic to handle mouse clicks, etc.
		cvui::update();

		// Show everything on the screen
		cv::imshow(WINDOW_NAME, frame);

		// Check if ESC key was pressed
		if (cv::waitKey(20) == 27) {
			break;
		}
}

Try it yourself and let me know, Luca

lukadt avatar Oct 16 '18 10:10 lukadt

That's a great solution! I've tested it in C++ and Python and everything seems to be working properly (at least on Windows).

I think putting the call to getWindowProperty() at the end, within the if that checks for the ESC key, makes things a bit more clear, since such if is already checking if the app should end. What do you think?

E.g.:

while (cv::getWindowProperty(WINDOW_NAME, 0) >= 0)
{
	// Fill the frame with a nice color
	frame = cv::Scalar(49, 52, 49);

	// Show some pieces of text.
	cvui::text(frame, 50, 30, "Hey there!");
                
         // This function must be called *AFTER* all UI components. It does
	// all the behind the scenes magic to handle mouse clicks, etc.
	cvui::update();

	// Show everything on the screen
	cv::imshow(WINDOW_NAME, frame);

	// Check if ESC key was pressed or if the window was closed
	if (cv::waitKey(20) == 27 || cv::getWindowProperty(WINDOW_NAME, cv::WND_PROP_ASPECT_RATIO) < 0) {
		break;
	}
}

Additionally, I don't think using cv::getWindowProperty(WINDOW_NAME, 0) instead of cv::getWindowProperty(WINDOW_NAME, cv::WND_PROP_*) is ideal, because the code could break in the future if OpenCV developers make any of the cv::WND_PROP_* stop using the value zero.

Dovyski avatar Oct 16 '18 11:10 Dovyski

Dear @Dovyski, your refactoring is a good one, it's better than my proof of concept. That being said I just found a small issue and it's related to cvui init with waitkey enable. If you try this code you can see the potential issue in action, i.e. the window is not closed. Let me know your findings,

// Init cvui and tell it to create a OpenCV window, i.e. cv::namedWindow(WINDOW_NAME).
cvui::init(WINDOW_NAME, 2000);
    
while (cv::getWindowProperty(WINDOW_NAME, 0) >= 0)
{

}

HTH, Luca

lukadt avatar Oct 16 '18 14:10 lukadt

You are absolutely right, my suggestion will not work when cvui is handling the calls to waitKey. It seems the use of while (cv::getWindowProperty(WINDOW_NAME, 0) >= 0) is the best way to go.

Dovyski avatar Oct 18 '18 06:10 Dovyski