5

Gtk+写了一些代码C,使用Cairo和计时器做一些动画。大多数情况下,当我单击关闭应用程序图标时,我会在终端上收到以下消息:

Gtk-CRITICAL **:gtk_widget_queue_draw:断言“GTK_IS_WIDGET(小部件)”失败

现在我假设这正在发生,因为在我关闭应用程序的那一刻,计时器被触发并且主窗口小部件被访问但已经被销毁。确定 Gtk 小部件是否仍然有效并且可以引用的常用方法是什么?

有问题的代码在这里:

gboolean rotate_cb( void *degrees )
{
    rotation += DegreesToRadians((*(int*)(degrees)));
    // Tell our window that it should repaint itself (ie. emit an expose event)
    /* need to only call gtk_widget_queue_draw() if window is still valid / exists */
    gtk_widget_queue_draw(window);
    return( TRUE );
}

我假设必须有某种方法来测试是否window仍然有效和有效?

4

3 回答 3

11

你的问题很微妙。这通常是因为 GObject/GtkObject 的所有权和销毁规则。让我提醒他们:

  • GObjects 只是计数引用。它们在计数达到 0 时被销毁。新创建的对象的计数为 1。
  • GInitiallyUnowneds 也是计数引用的,当计数达到 0 时它们也会被销毁。但是新创建的对象具有浮动计数。这意味着第一次增加计数时,它实际上并没有增加,但是浮动计数被沉没了,即转换为普通引用。

GtkObjects GInitiallyUnowned对象,因此它们具有神奇的浮动计数

但你可能知道这一切......现在,我的问题:

谁拥有可见主 GtkWindow 的计数器?

这实际上很简单,GTK 框架有一个列表,并保留了每个可见的引用GtkWindow。但是,另一个问题:

GTK 框架何时释放其 GtkWindow 的引用?

你还记得gtk_widget_destroy()函数和destroy信号吗?它们正是为此:当您想要删除GtkWindow您调用的顶层时gtk_widget_destroy(),它会激活信号destroy,该信号由 GTK 框架接收,该框架将删除实际窗口并释放其对对象的引用。

这就是您的问题的原因:如果 GTK 框架保留对 的唯一现有引用,则GtkWindow该对象实际上已被释放。如果那时,您的计时器尝试访问它,它将失败,因为窗口不再存在。

最后,这里(希望)出现了解决方案:

  • g_object_ref()/g_object_ref_sink()启动计时器时调用窗口。还要destroy为窗口的信号注册一个处理程序。
  • 在窗口信号的处理程序中destroy:调用g_object_unref()窗口并停止定时器。

当然,这个部分解决方案也应该可以工作,因为窗口不会在不发送destroy信号的情况下被破坏:

  • 将处理程序注册到destroy窗口的信号。
  • destroy在窗口信号的处理程序中:停​​止定时器。

但是,当您实际保留指向对象的指针时,增加对象的引用计数器被认为是一种好习惯。

于 2012-06-14T19:44:09.307 回答
3

没有办法,因为您将查询不再在内存中的对象的状态,即一些未定义的内存块。这样的功能可能会失败,例如,如果在同一内存地址创建了另一个小部件。原则上,您可以只使用该GTK_IS_WIDGET()宏,但不能保证即使大多数情况下也能正常工作。

解决问题的正确方法是在销毁rotate_cb()时删除回调。为此,课堂上window有“破坏”信号。GtkWidget因此,您应该连接到“destroy”并在该处理程序中删除rotate_cb()(或设置一些rotate_cb()不做任何事情的标志)。

于 2012-06-14T19:24:32.583 回答
3

g_object_weak_ref()对于这些情况也很有用

当对象完成时,弱引用用于通知。

您可以连接一个弱 ref 回调(通过 ex. )以在它被破坏之前与小部件widget_destroy_cb断开连接,因此一旦小部件被破坏就不应调用它。rotate_cbrotate_cb

于 2012-06-15T23:06:32.497 回答