GTK2+ error: GTK_IS_CONTAINER & GTK_IS_WIDGET failed
GTK2+ error: GTK_IS_CONTAINER & GTK_IS_WIDGET failed
这是我的第一个 post,我创建这个帐户是因为我有点难过。
我正在尝试练习将结构作为参数传递给回调,为此我创建了一个简单的程序,在 window 中有一个按钮。在第一次迭代中,按下带有标签 "Button 1" 的按钮会将其更改为带有标签 "Button 2" 的不同按钮,而在第二次迭代中,通过将鼠标悬停在按钮上来完成更改。
下面是第一次迭代的代码
#include <gtk/gtk.h>
typedef struct {
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *window;
} example;
void callback_func (GtkWidget *ignored, example *test) {
GtkWidget *window=test->window;
GtkWidget *changebutton1=test->button1;
GtkWidget *changebutton2=test->button2;
gtk_container_remove(GTK_CONTAINER(window),changebutton1);
gtk_container_add(GTK_CONTAINER(window),changebutton2);
gtk_widget_show_all(window);
}
void callback_func2 (GtkWidget *ignored, example *test) {
GtkWidget *window=test->window;
GtkWidget *changebutton1=test->button1;
GtkWidget *changebutton2=test->button2;
gtk_container_remove(GTK_CONTAINER(window),changebutton2);
gtk_container_add(GTK_CONTAINER(window),changebutton1);
gtk_widget_show_all(window);
}
int main(int argc, char *argv[]) {
example test;
gtk_init(&argc,&argv);
GtkWidget *window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget *changebutton1=gtk_button_new_with_label("Button 1");
GtkWidget *changebutton2=gtk_button_new_with_label("Button 2");
test.window=window;
test.button1=changebutton1;
test.button2=changebutton2;
g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
gtk_container_add(GTK_CONTAINER(window),changebutton1);
g_signal_connect (G_OBJECT (changebutton1), "clicked", G_CALLBACK (callback_func), (gpointer*)&test);
g_signal_connect (G_OBJECT (changebutton2),"clicked",G_CALLBACK(callback_func2),(gpointer*)&test);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
当运行上面的代码,点击一次按钮,按钮切换正常,但是当我再次点击它时(现在调用callback_func2而不是callback_func,这是除按钮切换位置外基本相同)我明白了:
(gtktesting.exe:92024): Gtk-CRITICAL **: gtk_container_add: 断言`GTK_IS_WIDGET (widget)' 失败
如果我用 "enter_notify_event" 和 "leave_notify_event" 替换 "clicked" 事件,第一个更改也会失败,现在会产生更多错误。
有人知道这是怎么回事吗?
另外,加分题。我几乎从另一个 post 复制并粘贴了这段代码的基础,因为我一直试图自己编写它但没有成功,我想测试它是否有效。我注意到回调中的第二个参数是 "example *test"。有人可以解释 "example" 类型吗?这只是结构的名称,我不知道它在那里做什么。
tl;dr
尝试:
test.window = g_object_ref(window);
test.button1 = g_object_ref(changebutton1);
test.button2 = g_object_ref(changebutton2);
完整解释
GTK+ 中的对象(即 GObject
的子类)被引用计数。这意味着,每个对象(例如 Widget)都有 "reference count" - 指向它的指针数。当数字达到 0 - 对象被释放。创建的对象的引用计数为 1。由于 C 没有智能指针,C++ 也没有类似的东西,因此必须手动完成引用计数。用户必须调用 g_object_ref
才能保留引用(我交替使用单词 "pointer" 和 "reference"),并且当用户完成引用时调用 g_object_unref
。这样可以确保在使用时不会破坏任何对象。
GtkWidget
很特别,因为它以 "floating" 引用开始。这意味着第一次引用小部件时,它的引用计数不会增加——它的 "floating" 引用是 "sunk"。之后它的行为就像任何其他 GObject
.
当您创建按钮时,它们是使用引用计数 1 ("floating") 创建的。当它们被添加到它们的容器中时,它们的引用仍然是 1(但 "sunk")。这意味着,按钮由它们添加到的容器拥有。
现在,当您从容器中删除按钮时:
gtk_container_remove(GTK_CONTAINER(window),changebutton1);
changebutton1
的引用计数减少,降至 0,这会强制对象销毁,test.button1
现在是悬空指针。
为了克服这个问题,只要你想存储指向 GObject
的指针,就可以使用 g_object_ref
。这样你就表达了“test
参与了 changebutton1
的所有权”(或者,“test
有兴趣保持 changebutton1
存活)。
完成 window
、button1
和 button2
后,对它们调用 g_object_unref
。
加分题
Also, a bonus question. I pretty much copied and pasted the base of
this code from another post, since I had been trying to write it by
myself unsuccessfully and I wanted to test if it works at all. I
noticed that the second argument in the callbacks is "example *test".
Can anybody explain the "example" type? It's only the name of the
stucture, and I've no idea what it's doing there.
example
定义在这里:
typedef struct {
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *window;
} example; // declares type "example"
GObject
信号系统的设计方式允许将任意指针作为最后一个参数传递给回调,因此程序员可以从创建信号连接的地方传递额外信息(g_signal_connect
), 回调。
这是我的第一个 post,我创建这个帐户是因为我有点难过。 我正在尝试练习将结构作为参数传递给回调,为此我创建了一个简单的程序,在 window 中有一个按钮。在第一次迭代中,按下带有标签 "Button 1" 的按钮会将其更改为带有标签 "Button 2" 的不同按钮,而在第二次迭代中,通过将鼠标悬停在按钮上来完成更改。 下面是第一次迭代的代码
#include <gtk/gtk.h>
typedef struct {
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *window;
} example;
void callback_func (GtkWidget *ignored, example *test) {
GtkWidget *window=test->window;
GtkWidget *changebutton1=test->button1;
GtkWidget *changebutton2=test->button2;
gtk_container_remove(GTK_CONTAINER(window),changebutton1);
gtk_container_add(GTK_CONTAINER(window),changebutton2);
gtk_widget_show_all(window);
}
void callback_func2 (GtkWidget *ignored, example *test) {
GtkWidget *window=test->window;
GtkWidget *changebutton1=test->button1;
GtkWidget *changebutton2=test->button2;
gtk_container_remove(GTK_CONTAINER(window),changebutton2);
gtk_container_add(GTK_CONTAINER(window),changebutton1);
gtk_widget_show_all(window);
}
int main(int argc, char *argv[]) {
example test;
gtk_init(&argc,&argv);
GtkWidget *window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget *changebutton1=gtk_button_new_with_label("Button 1");
GtkWidget *changebutton2=gtk_button_new_with_label("Button 2");
test.window=window;
test.button1=changebutton1;
test.button2=changebutton2;
g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
gtk_container_add(GTK_CONTAINER(window),changebutton1);
g_signal_connect (G_OBJECT (changebutton1), "clicked", G_CALLBACK (callback_func), (gpointer*)&test);
g_signal_connect (G_OBJECT (changebutton2),"clicked",G_CALLBACK(callback_func2),(gpointer*)&test);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
当运行上面的代码,点击一次按钮,按钮切换正常,但是当我再次点击它时(现在调用callback_func2而不是callback_func,这是除按钮切换位置外基本相同)我明白了: (gtktesting.exe:92024): Gtk-CRITICAL **: gtk_container_add: 断言`GTK_IS_WIDGET (widget)' 失败
如果我用 "enter_notify_event" 和 "leave_notify_event" 替换 "clicked" 事件,第一个更改也会失败,现在会产生更多错误。
有人知道这是怎么回事吗?
另外,加分题。我几乎从另一个 post 复制并粘贴了这段代码的基础,因为我一直试图自己编写它但没有成功,我想测试它是否有效。我注意到回调中的第二个参数是 "example *test"。有人可以解释 "example" 类型吗?这只是结构的名称,我不知道它在那里做什么。
tl;dr
尝试:
test.window = g_object_ref(window);
test.button1 = g_object_ref(changebutton1);
test.button2 = g_object_ref(changebutton2);
完整解释
GTK+ 中的对象(即 GObject
的子类)被引用计数。这意味着,每个对象(例如 Widget)都有 "reference count" - 指向它的指针数。当数字达到 0 - 对象被释放。创建的对象的引用计数为 1。由于 C 没有智能指针,C++ 也没有类似的东西,因此必须手动完成引用计数。用户必须调用 g_object_ref
才能保留引用(我交替使用单词 "pointer" 和 "reference"),并且当用户完成引用时调用 g_object_unref
。这样可以确保在使用时不会破坏任何对象。
GtkWidget
很特别,因为它以 "floating" 引用开始。这意味着第一次引用小部件时,它的引用计数不会增加——它的 "floating" 引用是 "sunk"。之后它的行为就像任何其他 GObject
.
当您创建按钮时,它们是使用引用计数 1 ("floating") 创建的。当它们被添加到它们的容器中时,它们的引用仍然是 1(但 "sunk")。这意味着,按钮由它们添加到的容器拥有。
现在,当您从容器中删除按钮时:
gtk_container_remove(GTK_CONTAINER(window),changebutton1);
changebutton1
的引用计数减少,降至 0,这会强制对象销毁,test.button1
现在是悬空指针。
为了克服这个问题,只要你想存储指向 GObject
的指针,就可以使用 g_object_ref
。这样你就表达了“test
参与了 changebutton1
的所有权”(或者,“test
有兴趣保持 changebutton1
存活)。
完成 window
、button1
和 button2
后,对它们调用 g_object_unref
。
加分题
Also, a bonus question. I pretty much copied and pasted the base of this code from another post, since I had been trying to write it by myself unsuccessfully and I wanted to test if it works at all. I noticed that the second argument in the callbacks is "example *test". Can anybody explain the "example" type? It's only the name of the stucture, and I've no idea what it's doing there.
example
定义在这里:
typedef struct {
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *window;
} example; // declares type "example"
GObject
信号系统的设计方式允许将任意指针作为最后一个参数传递给回调,因此程序员可以从创建信号连接的地方传递额外信息(g_signal_connect
), 回调。