GTK4 findChild returns NULL

Hello,
I want rewrite my old app (gtk2) to gtk4 (written in C).
I am using there function
GtkWidget* FindChild(GtkWidget* parent, const gchar* name) { … }

which I have before many years found at:

for gtk2 it works fine but for gtk4 it is neccesary update it…

It should explore a GTK GUI’s structure programmatically and return widget with entered name.

I can see in output log that correct widget is found but during returning from recursive function it is rewriten to NULL.

Pleace could anybody help how to improve my function to work.

I have prepared simple gui app to test it. You can enter searched name to entry to test it (e.g. “vert1”, “vert2”, “hor1” ect.)

Thank you a lot

#include <gtk/gtk.h>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <ctime>
#include <vector>

using namespace std;

GtkWidget* gVysledekHledani = NULL;
GtkWidget* entry;

//vrati vektor sourozencu
vector<GtkWidget*> GetWidgetSiblings (GtkWidget* widget) {
    cout << "siblings for: " << gtk_widget_get_name(widget) << endl;
    vector<GtkWidget*> tmpVektorSiblings;

    tmpVektorSiblings.push_back(widget);
    GtkWidget* nextSiblingWidget = gtk_widget_get_next_sibling(widget);
    string jmeno;

    do {
        if (nextSiblingWidget != NULL ) {
            jmeno = gtk_widget_get_name(nextSiblingWidget);
            if (g_ascii_strcasecmp(jmeno.c_str(), "GtkTooltipWindow") == 0) {
                cout << "skipping GtkTooltipWindow" << endl;
            } else if  (g_ascii_strcasecmp(jmeno.c_str(), "GtkHeaderBar") == 0) {
                cout << "skipping GtkHeaderBar" << endl;
            } else {
                tmpVektorSiblings.push_back(nextSiblingWidget);
                cout << "added to vektor: " << jmeno << "; vektor size:" << tmpVektorSiblings.size() << endl;
            }
        }
        nextSiblingWidget = gtk_widget_get_next_sibling(nextSiblingWidget);
    } while (nextSiblingWidget != NULL);

    cout << "searching siblings finished! count="<< tmpVektorSiblings.size() << endl;
    return tmpVektorSiblings;
}

GtkWidget* FindChild(GtkWidget* parent, const gchar* name) {
    //najde a vrati ukazatel na pojmenovaneho potomka widgetu
    cout << "findChild - we are looking for:" << name << endl;

    if (GTK_IS_WIDGET(parent)) {
        string jmenoRodice = gtk_widget_get_name(parent);
        cout << "parrent name: " << jmenoRodice << endl;
        if (g_ascii_strcasecmp(gtk_widget_get_name(parent), name) == 0) {
            cout << "parrent is the SEARCHED widget !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
            return parent;
        }

        vector<GtkWidget*> tmpVektorSourozencu = GetWidgetSiblings (parent);
        GtkWidget* sourozenec;
        GtkWidget* potomek;
        string sourozenecJmeno;
        for (unsigned int cnt = 0; cnt < tmpVektorSourozencu.size(); ++cnt) {
            sourozenec = tmpVektorSourozencu.at(cnt);
            sourozenecJmeno = gtk_widget_get_name(sourozenec);
            cout << cnt << "(" << tmpVektorSourozencu.size() << "): testing " << sourozenecJmeno << endl;

            potomek = gtk_widget_get_first_child(sourozenec);
            if (potomek != NULL) {
                cout << sourozenecJmeno << " has child - using recursion" << endl;
                FindChild(potomek, name);
            } else {
                cout << sourozenecJmeno << " is without child" << endl;
                if (g_ascii_strcasecmp(sourozenecJmeno.c_str(), name) == 0) {
                    cout << "sibling is SEARCHED widget !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
                    return sourozenec;
                }
            }
        }
        cout << "finishing with: " << jmenoRodice << endl;
    }
    cout << "finishing proc" << endl;
    return NULL;
}

void NecoUdelej (GtkWidget* wid, GtkWidget* data) {
    cout << endl << endl <<endl <<endl << "called function"<< endl;

    string hledaneJmeno = gtk_editable_get_text(GTK_EDITABLE(entry));
    cout << "i have read from enty name: " << hledaneJmeno << endl;


    GtkWidget* widget = FindChild(wid, hledaneJmeno.c_str());
    //GtkWidget* widget = FindChild(wid, "vert1");

    if (widget != NULL) {
        cout << "name of returned widget: " << gtk_widget_get_name(widget) << endl;
    } else {
        cout << "find child returned NULL" << endl;
    }

}


static void appActivate (GApplication *app, gpointer user_data) {
    GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
    gtk_window_set_title (GTK_WINDOW (win), "Test");
    gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
    gtk_widget_set_name(win,"hlavni okno apky");


    GtkWidget  *vBox = gtk_box_new (GTK_ORIENTATION_VERTICAL,10);
    gtk_widget_set_name(vBox,"kontak vbox");

    gtk_widget_set_margin_start(GTK_WIDGET(vBox),10);
    gtk_widget_set_margin_bottom(GTK_WIDGET(vBox),10);
    gtk_widget_set_margin_top(GTK_WIDGET(vBox),10);
    gtk_widget_set_margin_end(GTK_WIDGET(vBox),10);

    gtk_window_set_child (GTK_WINDOW (win), vBox);

    GtkWidget *labText;

    vector<string> vektorTextu;
    vektorTextu.push_back("pokus 1");
    vektorTextu.push_back("pokus 2");
    vektorTextu.push_back("pokus 3");
    vektorTextu.push_back("pokus 4");
    vektorTextu.push_back("pokus 5");

    string jmeno="";

    if (vektorTextu.size() > 0) {
        labText = gtk_label_new("");
        gtk_label_set_markup(GTK_LABEL(labText), "<b>nadpis:</b>");
        gtk_box_append (GTK_BOX (vBox), labText);

        GtkWidget  *hBox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL,10);
        gtk_widget_set_name(hBox,"kontak hbox");
        for (int i = 0; i < vektorTextu.size(); ++i) {
            labText = gtk_label_new(vektorTextu.at(i).c_str());
            gtk_box_append (GTK_BOX (hBox), labText);
            jmeno = "hor" + to_string(i);
            gtk_widget_set_name(labText,jmeno.c_str());
        }

        gtk_box_append (GTK_BOX (vBox), hBox);

        for (int i = 0; i < vektorTextu.size(); ++i) {
            labText = gtk_label_new(vektorTextu.at(i).c_str());
            gtk_box_append (GTK_BOX (vBox), labText);
            jmeno = "vert" + to_string(i);
            gtk_widget_set_name(labText,jmeno.c_str());
        }
    } else {
        labText = gtk_label_new("vektor je prazdnej");
        gtk_box_append (GTK_BOX (vBox), labText);
    }

    labText = gtk_label_new("");
    gtk_box_append (GTK_BOX (vBox), labText);

    entry = gtk_entry_new ();
    gtk_box_append  (GTK_BOX (vBox), entry);
    gtk_widget_set_name(entry,"entry");
    g_object_set_data(G_OBJECT(win), "entry",entry);



    GtkWidget *btnNeco = gtk_button_new_with_label("find widget with entered name");
    gtk_box_append  (GTK_BOX (vBox), btnNeco);
    g_signal_connect_swapped(GTK_BUTTON(btnNeco), "clicked", G_CALLBACK(NecoUdelej), win);

    GtkWidget *btnBack = gtk_button_new_with_label("Close");
    gtk_box_append  (GTK_BOX (vBox), btnBack);
    g_signal_connect_swapped(GTK_BUTTON(btnBack), "clicked", G_CALLBACK(gtk_window_destroy), win);

    gtk_widget_show (win);
}


int main(int argc, char **argv) {
    GtkApplication *app;
    app = gtk_application_new ("testing.app", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (appActivate), NULL);
    return g_application_run (G_APPLICATION (app), NULL, NULL);
    g_object_unref (app);

    return 0;
}

This is, in general, a very poor idea. There is no toolkit-level guarantee that a widget name is unique, so there is no way for your code to validate that you aren’t using a duplicate identifier, and accessing the wrong widget.

You should keep around pointers to widgets of interest; you should also use composite templates and custom composite widget classes to simplify the overall structure of your code, instead of doing potentially costly widget hierarchy traversals every time you need a widget.

GTK2 and GTK4 are very different toolkits, reflecting the 20 years that passed between them. You should adapt your code following the best idiomatic practices of the toolkit, instead of doing a mechanical port.

To answer you question, writing a widget hierarchy traversal with GTK4 is a lot easier, as there is no GtkContainer any more, and widgets contain the full list of their children:

GtkWidget *
find_child (GtkWidget *parent,
            const char *name)
{
  // Bail out early
  if (parent == NULL)
    return NULL;

  // Start iterating over every child of the parent widget
  GtkWidget *child = gtk_widget_get_first_child (parent);
  while (child != NULL)
    {
      // Direct descendants
      if (g_strcmp0 (gtk_widget_get_name (child), name) == 0)
        return child;

      // Recurse into the current child
      GtkWidget *grandchild = find_child (child, name);
      if (grandchild != NULL)
        return grandchild;

      // Move to the next child
      child = gtk_widget_get_next_sibling (child);
    }

  return NULL;
}

A C++ port should be pretty similar.

Thank you a lot - it works how I need!

So please which better solution suggest you?

As I said above:

There’s a tutorial for composite widget templates available on the GNOME developers documentation: Widget Templates - GNOME Developer Documentation

ok, thanks. I’ll go study it.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.