Adding/removing child widgets from within a callback

I am trying to create and remove GtkCheckButton(s) to and from a GtkBox from inside a callback. The adding part works as expected but removing doesn’t quite work.
I create and add them with:

widget = gtk_check_button_new_with_label (name);
gtk_box_append(box, widget);

and remove with

gtk_box_remove (box, widget);

And while I have noticed that the widget object itself is removed and unreferenced, it does not disappear from the “box”, at least not until I resize the window. This will cause the callback to be called again but it will not touch the box again. Just redraw a texture inside a GtkGlArea widget.

Is there something deep I have missed regarding altering containers from inside callbacks? Should the child widget have disappeared from the GtkBox when I issue the gtk_box_remove command?

Do you try to remove a widget from a callback that is connected to that widget, e.g. connect a callback to the “clicked” signal of a button, and then remove the button itself in the callback? And is it GTK3 or GTK4?

Recently someone said that he has to call gtk_show_all() for an update, which I had not expected, see [GTK] [C] Attach to grid dynamically - #3 by sharkattack. But that may be for old GTK3.

No. The “callback” in question is a callback for GtkGLArea and the widget (the GtkCheckButton) is added from the callback to a GtkBox. I will try the show_all you suggested. Will update this thread if that works. Thanks for the suggestion anyway.

Sorry, forgot to add. It is Gtk4. And I just saw that gtk_widget_gtk_show_all is not part of Gtk4.

OK, i I may need a similar behavior myself for one of my apps, so I may test it later this evening. Will tell you if it works.

OK, here is a minimal test, in Nim. Works fine.

import gintro/[gtk4, gobject, gio, pango]

proc removeAnotherOne(b: Button; data: tuple[box: Box; toRemove: Button]) =
  data.box.remove(data.toRemove)

proc activate(app: gtk4.Application) =
  let window = newApplicationWindow(app)
  let box = newBox(Orientation.vertical)
  let b1 = newButton("B1")
  let b2 = newButton("B2")
  let b3 = newButton("Remove B2")
  b3.connect("clicked", removeAnotherOne, (box, b2))
  box.append(b1)
  box.append(b2)
  box.append(b3)
  window.setChild(box)
  window.present

proc main =
  let app = newApplication("org.gtk.example")
  app.connect("activate", activate)
  let status = app.run
  quit(status)

main()

I assume you are using plain C, so it should be easy for you to convert my Nim code to C, just use the long C names like gtk_button_new(). I passed the box and the button to delete asd an tuple to the callback, in C you may use a struct or global data. Well, you said that you are using check buttons in your original code…
If it still does not work for you, please post your C test code.

Of course, we should press the button with label " Remove B2" only once! For me display updates immediately.

I have noticed that just moving the mouse pointer (no clicking) over other GtkCheckButton(s) I have in the application window will remove the check button from the Box that was supposed to be removed from the callback. The (other) checkbuttons are not related to the callback that adds and tries to remove checkbuttons.

I just saw your last post while writing. Yes, this is pretty much what I am trying to do. Obviously, “activate” is a callback but maybe GtkApplication handles something differently. I don’t know. Obviously, I am quite confused atm.

I am doing this in C.

That is a bit strange. I am using Wayland and GTK 4.8 here.

I see now that this is not quite what I am doing. My remove is not associated with a callback like in your case.

This is the exact code (cpy-paste) that adds,

aw->id = a->id;
aw->is_active = TRUE;
aw->widget = gtk_check_button_new_with_label (a->name);
g_hash_table_insert(active_render_widgets_hash, GUINT_TO_POINTER (aw->id), aw);
aw->handler_id = g_signal_connect_after (aw->widget, "toggled", G_CALLBACK (acive_widgets_checkbutton_cb), GUINT_TO_POINTER(a->id));
gtk_box_append(render_objects_box, aw->widget);
gtk_check_button_set_active (GTK_CHECK_BUTTON (aw->widget), aw->is_active);

and this code removes,

if (g_hash_table_lookup_extended (active_render_widgets_hash, GUINT_TO_POINTER(a->id), &orig_key, &aw))
{
	g_signal_handler_disconnect (aw->widget, aw->handler_id);
	gtk_box_remove (render_objects_box, aw->widget);
	g_slice_free1(sizeof *aw, aw);
	g_hash_table_remove(active_render_widgets_hash, GUINT_TO_POINTER(a->id));
}

the “a->id” is just an incoming (let’s call it) object that contains the unique id of the object to be removed (or added in the case a->id is something new to be handled). “aw” refers to a widget to be added or removed.

I have tried both Wayland (my default desktop) and Xorg. Same behavior. Xorg has some weird rendering problems that eventually go away but thy are a bit annoying which is why I am back on Wayland.

Have you tried a
“gtk_widget_queue_redraw(render_object_box)” after your gtk_box_remove()"?

That’s definitely not needed.

There is a gtk_widget_queue_draw() in gtk4 but it has no effect.

What does have an effect is to do all that I do in my remove section inside a g_idle_add(). I don’t know why it makes a difference and it feels like the wrong solution but I have to take it until it blows up something else.

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