【问题标题】:Finding children of a GtkWidget寻找 GtkWidget 的孩子
【发布时间】:2011-07-21 01:19:36
【问题描述】:

我需要能够以编程方式探索 GTK GUI 的结构。我有 GtkWidget,我想找到该小部件的任何子级。现在我知道 GtkContainer 有一个查找孩子的功能,而且 GtkContainer 是从 GtkWidget 派生的。

无论如何我可以检查一个小部件是否是一个 GtkContainer 然后执行转换?如果没有,有没有其他方法可以发现我所拥有的 GtkWidget 的孩子?

【问题讨论】:

标签: gtk ui-automation


【解决方案1】:

是的,每个 GObject 类型都有宏来检查对象是否属于该类型:

if(GTK_IS_CONTAINER(widget)) {
    GList *children = gtk_container_get_children(GTK_CONTAINER(widget));
    ...
}

如果小部件是GtkBin,它只有一个孩子。在这种情况下,以下操作比处理GList 更简单:

if(GTK_IS_BIN(widget)) {
    GtkWidget *child = gtk_bin_get_child(GTK_BIN(widget));
    ...
}

【讨论】:

    【解决方案2】:

    在下面的代码中,我实现了find_child 函数,它按名称递归搜索小部件。想法建立在@ptomato 的答案和following PHP 示例的基础上:

        #include <gtk/gtk.h>
    
        GtkWidget*
        find_child(GtkWidget* parent, const gchar* name)
        {
                if (g_strcasecmp(gtk_widget_get_name((GtkWidget*)parent), (gchar*)name) == 0) { 
                        return parent;
                }
    
                if (GTK_IS_BIN(parent)) {
                        GtkWidget *child = gtk_bin_get_child(GTK_BIN(parent));
                        return find_child(child, name);
                }
    
                if (GTK_IS_CONTAINER(parent)) {
                        GList *children = gtk_container_get_children(GTK_CONTAINER(parent));
                        while ((children = g_list_next(children)) != NULL) {
                                GtkWidget* widget = find_child(children->data, name);
                                if (widget != NULL) {
                                        return widget;
                                }
                        }
                }
    
                return NULL;
        }
    
        int
        main(int argc, char *argv[])
        {
                gtk_init(&argc, &argv);
    
                GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
                GtkWidget *frame = gtk_frame_new(NULL);
                GtkWidget *vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
                GtkWidget *textView = gtk_text_view_new();
                GtkWidget *hbox1 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
                GtkWidget *button1 = gtk_button_new_with_label("button1");
                gtk_widget_set_name(button1, "btn1");
                GtkWidget *button2 = gtk_button_new_with_label("button2");
                gtk_widget_set_name(button2, "btn2");
    
                gtk_window_set_title(GTK_WINDOW(window), "Hello");
                gtk_container_set_border_width(GTK_CONTAINER(window), 10);
                gtk_window_set_default_size(GTK_WINDOW(window), 450, 400);
    
                gtk_container_add(GTK_CONTAINER(window), frame);
                gtk_container_add(GTK_CONTAINER(frame), vbox1);
    
                gtk_box_pack_start(GTK_BOX(vbox1), textView, 1, 1, 0);
                gtk_box_pack_start(GTK_BOX(vbox1), hbox1, 0, 1, 0);
                gtk_box_pack_start(GTK_BOX(hbox1), button1, 0, 1, 0);
                gtk_box_pack_start(GTK_BOX(hbox1), button2, 0, 1, 0);
    
                gtk_widget_show_all(window);
    
                g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
    
                GtkWidget* child = find_child(window, "btn2");
                if (child == button2) {
                        g_print("found it!\n");
                } else {
                        g_print("not found it!\n");
                }
    
                gtk_main();
                return 0;
        }
    

    【讨论】:

    • 感谢代码,它非常有帮助,帮助我解决了我的问题。然而,我发现了一个我想与使用它的人分享的错误:因为在 find_child 中你调用 gtk_container_get_children() 然后立即调用 g_list_next_children(),你永远不会探索树中的第一个节点。我用 do/while 循环修复了这个问题,尽管您仍然需要确保第一项不是 NULL。
    • 这段代码有一个微妙的错误:在 find_child 的 GTK_IS_CONTAINER 部分,你在 "while ((children == g_list_next()" 循环中迭代孩子。这是错误的,它跳过了第一个孩子!
    【解决方案3】:

    我创建了这些函数,它们基于与接受的答案相同的原则。

    find_child_by_index 中,depth 等于您需要传递的索引数量。第一个索引是针对parent 的孩子,第二个索引是针对孙辈,依此类推。

    void print_children_helper(GtkWidget* parent, int indent_size, int depth)
    {
        for (int i = 0; i < depth * indent_size; i++)
            printf(" ");
        printf("%s\n", gtk_widget_get_name(parent));
    
        GList* children = NULL;
        if (GTK_IS_CONTAINER(parent))
            children = gtk_container_get_children(GTK_CONTAINER(parent));
    
        while (children != NULL)
        {
            print_children(children->data, indent_size, depth + 1);
            children = children->next;
        }
    }
    
    void print_children(GtkWidget* parent, int indent_size)
    {
        print_children_helper(parent, indent_size, 0);
    }
    
    GtkWidget* find_child_by_name(GtkWidget* parent, const gchar* name)
    {
        if (g_strcmp0(gtk_widget_get_name(parent), name) == 0)
            return parent;
    
        GList* children = NULL;
        if (GTK_IS_CONTAINER(parent))
            children = gtk_container_get_children(GTK_CONTAINER(parent));
    
        while (children != NULL)
        {
            GtkWidget* widget = find_child_by_name(children->data, name);
    
            if (widget != NULL)
                return widget;
    
            children = children->next;
        }
    
        return NULL;
    }
    
    GtkWidget* find_child_by_index(GtkWidget* parent, int depth, ...)
    {
        va_list argp;
        va_start(argp, depth);
    
        for (int i = 0; i < depth; i++)
        {
            int index = va_arg(argp, int);
    
            GList* children = NULL;
            if (GTK_IS_CONTAINER(parent))
                children = gtk_container_get_children(GTK_CONTAINER(parent));
    
            for (int j = 0; j < index; j++)
                if (children != NULL)
                    children = children->next;
    
            if (children != NULL)
                parent = children->data;
            else
                return NULL;
        }
    
        va_end(argp);
        return parent;
    }
    

    【讨论】:

      【解决方案4】:

      如果使用 GTK4 作为 GtkContainer 不再存在,则需要一种不同的方法。见migration guide

      一个遍历小部件层次结构的简单函数:

      void print_widget_names(GtkWidget* parent, int level) {
        for (int i=0; i < level; i++) { 
          g_print("    ");
        }
        level++;
        g_print("%s\n", gtk_widget_get_name(GTK_WIDGET(parent)));
        GtkWidget *widget = gtk_widget_get_first_child(GTK_WIDGET(parent));
        GtkWidget *next;
        if (widget != NULL) {
          print_widget_names(widget, level);
          while ((next = gtk_widget_get_next_sibling(widget)) != NULL) {
            widget = next;
            print_widget_names(widget, level);
          }
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多