如何确定GTK小部件是否已被销毁

时间:2012-06-14 17:04:54

标签: c gtk widget

我在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是否仍然有效且有效?

3 个答案:

答案 0 :(得分:10)

你的问题非常微妙。这通常是因为GObject / GtkObject的所有权和破坏规则而发生的。让我提醒他们:

  • GObject只是计数参考。当计数达到0时,它们被销毁。新创建的对象计数为1.
  • GInitiallyUnowned也被计数引用,并且当计数达到0时它们也被销毁。但是新创建的对象具有浮动计数。这意味着第一次递增计数时它实际上没有递增,但浮动计数是 sunk ,即转换为普通参考。

GtkObject 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信号的处理程序中:停​​止计时器。

但是当你实际保持对象的指针时,增加对象的ref计数器被认为是一种很好的做法。

答案 1 :(得分:3)

g_object_weak_ref()对这些案例也很有用

  

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

你可以连接一个弱引用回调(ex。widget_destroy_cb),以便在它被销毁之前从小部件中断开rotate_cb,因此一旦小部件被销毁就不应该调用rotate_cb

答案 2 :(得分:2)

没有办法,因为你查询对象的状态不再存在于内存中,即一些未定义的内存块。这种功能可能会失败,例如如果在同一个内存地址创建了另一个小部件。原则上,您可以使用GTK_IS_WIDGET()宏,但无法保证正常工作,即使它最常见。

解决问题的正确方法是在rotate_cb()被销毁时删除window回调。有" destroy" GtkWidget类中的信号。所以,你应该连接到" destroy"并在该处理程序中删除rotate_cb()(或设置一些使rotate_cb()无效的标志。)