首页 > 解决方案 > 如何检查之前获得的 GDKWindow 在 GTK+-3 中是否仍然有效?

问题描述

我使用 GDK 库在 GTK+-3 中编写了一个屏幕截图程序,它大部分都可以工作,但我发现有时程序会随机崩溃并出现 X11 错误。我意识到如果立即打开和关闭一个窗口,就会触发崩溃,并且我能够编写以下程序来重现该问题。

from time import sleep
from gi.repository import GLib, Gtk, Gdk

# (Open and select a window before the program starts.)
print("Open your window and focus on it.")

for i in range(5):
    print("Program starts in %d seconds" % (5 - i))
    sleep(1)

# Get an active window.
screen = Gdk.Screen.get_default()
window = Gdk.Screen.get_active_window(screen)

def loop():
    # Close the window in the middle of this callback.
    print(window.get_geometry())  # Crash
    return True

GLib.timeout_add(100, loop)
Gtk.main()

如果我在开始执行之前打开一个窗口loop(),然后突然关闭中间的窗口loop(),程序会因 X11 错误而崩溃。

Gdk-ERROR **: 00:13:54.693: The program 'test.py' received an X Window System error.
This probably reflects a bug in the program.
The error was 'BadDrawable (invalid Pixmap or Window parameter)'.
  (Details: serial 175 error_code 9 request_code 14 (core protocol) minor_code 0)
  (Note to programmers: normally, X errors are reported asynchronously;
   that is, you will receive the error a while after causing it.
   To debug your program, run it with the GDK_SYNCHRONIZE environment
   variable to change this behavior. You can then get a meaningful
   backtrace from your debugger if you break on the gdk_x_error() function.)

很明显,它崩溃是因为这个 GDKWindow 后面的底层 X11 窗口之前已经消失了get_geometry()。但是如何确保 GDKWindow 仍然有效?我尝试使用is_destroyed(),process_all_updates()flush(),但没有任何效果。


已解决:正如重复的问题所示,X 是一个异步协议,因此根本不可能确保窗口存在。相反,我们应该做的是安装一个异常处理程序,以便在错误发生后从 X 中捕获错误。在 GDK 中,异常处理可以通过gdk_x11_display_error_trap_push()(开始忽略并记录异常)和gdk_x11_display_error_trap_pop()(如果有则返回错误代码)来实现。

正确的程序如下所示。

from time import sleep
from gi.repository import GLib, Gtk, Gdk, GdkX11

# (Open and select a window before the program starts.)
print("Open your window and focus on it.")

for i in range(5):
    print("Program starts in %d seconds" % (5 - i))
    sleep(1)

# Get an active window.
screen = Gdk.Screen.get_default()
window = Gdk.Screen.get_active_window(screen)

def loop():
    # Close the window in the middle of this callback.

    # ignore and catch all errors at this point.
    display = GdkX11.X11Display.get_default()
    GdkX11.X11Display.error_trap_push(display)

    # do something that may throw an X error
    print(window.get_geometry())

    # stop ignoring errors, return the error code if any
    error = GdkX11.X11Display.error_trap_pop(display)
    if (error == 0):
        print("previous operation succeed.")
    else:
        print("previous operation failed, error %d." % error)
        # do error recovery here

    return True


GLib.timeout_add(100, loop)
Gtk.main()

输出:

(x=0, y=0, width=952, height=545)
previous operation succeed.
(x=0, y=0, width=952, height=545)
previous operation succeed.
(x=-1013446544, y=22086, width=-1013446512, height=22086)
previous operation failed, error 9.
(x=-1013446544, y=22086, width=-1013446512, height=22086)
previous operation failed, error 9.

标签: pythonx11gtk3gdk

解决方案


推荐阅读