OpenCV Python:如何检测窗口是否关闭?

35

我有以下代码:

total_frames = 50
cv2.cv.NamedWindow("Dragonfly Simulation")
cv2.cv.StartWindowThread()
for i in range(total_frames):
    # do stuff
    img_name = # something
    img = cv2.cv.LoadImage(img_name)
    cv2.cv.ShowImage("Dragonfly Simulation", img)
    cv2.cv.WaitKey(2)
cv2.cv.DestroyWindow("Dragonfly Simulation")
cv2.cv.WaitKey(1)
# rest of code

那么它是做什么的:

  1. 打开一个窗口
  2. 在循环中,在窗口上显示一张图片
  3. 完成后,关闭窗口
  4. 运行其余的代码

然而,这种情况下我有之前给定的total_frame。我不想要那个。

相反,我想要一个这样的代码:

  1. 打开一个窗口
  2. 在循环中,在窗口上显示一张图片
  3. 等待用户关闭那个窗口
  4. 当用户关闭窗口时,退出循环,继续执行其余的代码。

然而,我找不到OpenCV中可以检测用户何时关闭窗口的函数。有人能提供解决方法吗?


也许有一些异常可以捕获吗?例如,尝试访问窗口,如果出现NoWindowExistsException/NullPointerException错误,则使用您的代码。 - Marek
@jotto 嗯,抱歉我对OpenCV还不熟悉,我该如何检查异常?使用我的当前代码,如果我关闭窗口,代码就会停止。此外,根据ShowImage的手册: “如果在调用此函数之前没有创建窗口,则假定使用CV_WINDOW_AUTOSIZE创建一个窗口。” - dessskris
我对OpenCV一点也不熟悉。我只是想到在关闭窗口后实例将保留下来。也许这就是你要找的?存储一些全局cv的引用,这样无论窗口是否关闭,都可以访问它?我只是在推测。 - Marek
@jotto 嗯,我正在建模神经元仿真,所以我想让仿真运行尽用户想要的时间(而不是指定的时间长度或帧数)。因此,我想要检测用户何时关闭该窗口。我猜另一种方法是检测键盘按键?但我不太确定如何做到这一点... - dessskris
你能在循环期间关闭窗口吗? - Marek
显示剩余3条评论
9个回答

50

我只是在寻找一种方法来检测窗口何时被关闭,包括等待按键以及使用窗口的 X 按钮,但我无法在任何地方找到答案(Python cv2 模块中不提供 IsWindowVisiblecvGetWindowHandle)。

所以我尝试了一下,这就是它的工作原理:

while cv2.getWindowProperty('window-name', 0) >= 0:
    keyCode = cv2.waitKey(50)
    # ...

cv2.getWindowProperty()在窗口关闭后会立即返回-1


详细信息请查看枚举类型cv::WindowPropertyFlags的文档:获取索引为0的标志是全屏属性,但实际上使用哪个标志都可以,因为它们在窗口关闭后都变成-1


注意:这可能仅适用于某些GUI后端。特别是,在Debian / Ubuntu软件包中使用的GTK后端不起作用。要改用Qt后端,您必须通过pip安装opencv-python


1
是的,您的解决方案有效,但不幸的是会写入到错误流stderr中,“OpenCV Error: Null pointer (NULL window) in cvGetModeWindow_GTK, file /build/opencv-SviWsf/opencv-2.4.9.1+dfsg/modules/highgui/src/window_gtk.cpp, line 555”。 - Vladyslav Savchenko
@VladyslavSavchenko 在 Python 还是 C++ 中?我在 Python 中没有收到任何错误,而且我使用的是 OpenCV 版本 3.1... 你可以尝试将其包装在 try/catch 中,并在 catch 语句中退出。 - Simon Hänisch
3
你使用的是哪个操作系统?我在Ubuntu上无法运行这个程序;即使窗口已经关闭,getWindowProperty函数仍然返回0.0 - HelloGoodbye
3
这对我不起作用,因为 ">=" 捕捉了窗口不可见状态。我使用了以下代码: while cv2.getWindowProperty(name, cv2.WND_PROP_VISIBLE) > 0: keyCode = cv2.waitKey(50) else: print('break') - DeusXMachina
@VladyslavSavchenko 如果只是向stderr写入,但其他方面都正常工作,那真的是一个大问题吗? - eric
显示剩余5条评论

33

从2.2版本开始,有一个简单的解决方案(这是从hist.py中的循环修改而来):

    cv2.imshow('image',im)
    while True:
        k = cv2.waitKey(100) # change the value from the original 0 (wait forever) to something appropriate
...
        elif k == 27:
            print('ESC')
            cv2.destroyAllWindows()
            break        
        if cv2.getWindowProperty('image',cv2.WND_PROP_VISIBLE) < 1:        
            break        
    cv2.destroyAllWindows()

6

我测试了C++代码,使用getWindowProperty('image', WND_PROP_VISIBLE) 但是没有起作用。所以我使用WND_PROP_AUTOSIZE,结果成功了。

我的做法如下:

cv::namedWindow("myTitle", WINDOW_AUTOSIZE);

while(1)
{
    cv::imshow("myTitle", myImage);


    if (cv::getWindowProperty("myTitle", WND_PROP_AUTOSIZE) == -1)
        break;
}

1
同样适用于Python: `cv2.imshow("image", image)if cv2.getWindowProperty('image',1) < 0:break`其中 cv2.getWindowProperty(<windowName>, 1) 是指自动调整大小的标志。 [https://docs.opencv.org/3.1.0/d7/dfc/group__highgui.html#gaeedf4023e777f896ba6b9ffb156f57b8] - Eli

4
        if cv2.getWindowProperty('windowname',1) == -1 :
            break
        cv2.imshow('windowname', image)

6
你的回答只有代码,尝试添加文档参考或解释代码的作用。虽然有时候回答核心问题只需要一些代码,但提供的细节越多越好。 - LW001

3
我使用了以下代码来检查是否同时按下键盘上的某个键和关闭窗口。
    while cv2.getWindowProperty(camera.get_name(), cv2.WND_PROP_VISIBLE) > 0:
        if cv2.waitKey(100) > 0:
            break

    cv2.destroyAllWindows()

2
import cv2
import numpy as np

# total_frames = 50
cv2.namedWindow("Dragonfly Simulation")
cv2.startWindowThread()
# for i in range(total_frames):
while True:
    # do stuff
    img = np.random.randint(0,255,(200,300)).astype(np.uint8)
    cv2.imshow("Dragonfly Simulation", img)
    key = cv2.waitKey(200)
    print key
    if key in [ord('a'), 1048673]:
        print 'a pressed!'
    elif key in [27, 1048603]: # ESC key to abort, close window
        cv2.destroyAllWindows()
        break

# do the rest of processing after break 
print 'results:'

当然,您可以使用waitKey检查用户输入。下面是一个基于您的代码的小例子。我将旧的cv接口更改为cv2。我认为cv已经过时了。
(编辑)我将cv2.destroyAllWindows()移动到while循环内部,以明确窗口在用户按ESC键(您可以分配任何键)时关闭。我不认为opencv有适当的事件处理程序来捕获窗口关闭事件,就像其他GUI工具包(如wxPython等)一样。因此,您需要定义用户应该如何关闭窗口并注意这一点。

@dessskris,这就像是我想让你建议的东西。 - Marek

0

@David Kohen's answer 对我很有效。但如果您有许多图片要展示,最好将其转换为一个函数

 def waitUntilX(window):
    while True:
        k = cv2.waitKey(100) # change the value from the original 0 (wait forever) to something appropriate
        if k == 27:
            print('ESC')
            cv2.destroyAllWindows()
            break        
        if cv2.getWindowProperty(window,cv2.WND_PROP_VISIBLE) < 1:        
            break        
    cv2.destroyAllWindows()

使用方法

invertedmask = cv2.bitwise_not(mask)
cv2.imshow('inverted mask', invertedmask)
waitUntilX('inverted mask')

0

https://www.programcreek.com/python/example/110676/cv2.WND_PROP_VISIBLE

对于我的情况,必须按照以下步骤进行。在检查窗口状态之前,我必须调用k = cv2.waitKey(1) & 0xFF才能使其正常工作。
def show_webcam(mirror=False):
    cam = cv2.VideoCapture(0)
    # cv2.startWindowThread()
    window_name="A_star webcam"
    while True:
        ret_val, img = cam.read()
        if mirror: 
            img = cv2.flip(img, 1)
        cv2.imshow(window_name, img)
        k = cv2.waitKey(1) & 0xFF 
        if not (cv2.getWindowProperty(window_name,cv2.WND_PROP_VISIBLE)):
            break
        
        if cv2.waitKey(1) == 27: 
            break  # esc to quit
     
    cv2.destroyAllWindows()

已安装的库包括:

autopep8==1.6.0
numpy==1.22.1
opencv-python==4.5.5.62
pycodestyle==2.8.0
toml==0.10.2

0
我根据这篇文章编写了一个简单的函数。它在OpenCV 4.5.2中运行良好。
def wait_with_check_closing(win_name):
    """ 
        https://dev59.com/ElsW5IYBdhLWcg3wbWtv"
        "opencv-python-how-to-detect-if-a-window-is-closed/37881722
    """
    while True:
        keyCode = cv2.waitKey(50)
        if keyCode != -1:
            break
        win_prop = cv2.getWindowProperty(win_name, cv2.WND_PROP_VISIBLE)
        if win_prop <= 0:
            break


它可以像以下示例一样替代cv2.waitKey(0)。

# Usage
cv2.imshow(title, img_resized)
# cv2.waitKey(0)
wait_with_check_closing(title)
cv2.destroyAllWindows()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接