gtk4

how to manually move pages between notebooks in gtk4


I am working on an app that uses gtk bindings for ui elements, and I cannot figure out how to so something that seems like it should be simple. I want to be able to move the information contained in a notebook page to another notebook (eg move Page 1 from notebook_1 to notebook_2).

I tried to do this with the following code snippet:

page = gtk_notebook_get_current_page(notebook_1)
child = gtk_notebook_get_nth_page(notebook_1, page)

call gtk_notebook_remove_page(notebook_1, page)
label = gtk_label_new("Test")
nb = gtk_notebook_append_page(notebook_2, child, test)

When I try to run this, I get an error in the console:

(:87313): Gtk-CRITICAL **: 19:58:29.515: gtk_notebook_append_page: assertion 'GTK_IS_WIDGET (child)' failed

And nb is -1, indicating the operation failed.

I thought that maybe the remove page was destroying the child object, so I omitted that line. But then I get the following error in the console

(:87293): Gtk-WARNING **: 19:56:50.684: Duplicate child name in GtkStack: (null)

(:87293): Gtk-CRITICAL **: 19:56:50.684: gtk_widget_set_parent: assertion '_gtk_widget_get_parent (widget) == NULL' failed

When I run this nb is not -1, but the information in the child does not show in notebook_2.

I feel like I am missing something basic here, but I don't know what. I tried a few other things such as attaching the child to a scrolled window, and also reproduced the error using c code (instead of bindings). If anyone has any tips on how to do this or direction on how to further troubleshoot, I'd appreciate it.

Update - here is a full c app that give me the problem, that I hacked together from an old gtk2? notebook example I found on the web. When I run it and click on the move page button, it does not work. I do not really know c, so apologies if this is a mess, but it does reproduce the problem FWIW.

It seems like what I want to do isn't possible - when I remove the old page it is probably wiping the reference to the child, but when I don't remove the old page it complains that the child exists somewhere else.

#include <gtk/gtk.h>

GtkWidget *notebook_1;
GtkWidget *notebook_2;
    
/* move a page from notebook_1 to notebook_2 */
void move_page (GtkButton *button)
{
    gint page;
    GtkWidget *child;
    GtkWidget *label;
    GtkWidget *scroll_win;
    
    
    page = gtk_notebook_get_current_page(notebook_1);
    child = gtk_notebook_get_nth_page(notebook_1, page);
    label = gtk_notebook_get_tab_label(notebook_1, child);
    gtk_notebook_remove_page (notebook_1, page);
    
    
    gtk_notebook_append_page(GTK_NOTEBOOK (notebook_2), GTK_WIDGET(child), label);
    
    /* If I use this instead of child, no errors are thrown in the console and the
       label info is transferred to notebook_2
    scroll_win = gtk_scrolled_window_new();   
    gtk_notebook_append_page(notebook_2, scroll_win, label);
    */ 
    gtk_widget_queue_draw(GTK_WIDGET(notebook_1));
    
}


static void
activate (GtkApplication* app,
          gpointer        user_data)
{
    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *table;

    GtkWidget *frame;
    GtkWidget *label;
    GtkWidget *checkbutton;
    int i;
    char bufferf[32];
    char bufferl[32];
    char istr[32];
    
    
    
    window = gtk_application_window_new (app);
    
    
    gtk_window_set_default_size (GTK_WINDOW (window), 400,400);
    table = gtk_grid_new();
    gtk_window_set_child (window, table);
    
    /* Create a new notebook, place the position of the tabs */
    notebook_1 = gtk_notebook_new ();
    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook_1), GTK_POS_TOP);
    gtk_grid_attach(GTK_GRID (table), notebook_1, 0,0,6,1);
    gtk_widget_show(notebook_1);
    
    /* lets append a bunch of pages to the notebook */
    for (i=0; i < 5; i++) {
        sprintf(bufferf, "Append Frame %d", i+1);
        sprintf(bufferl, "Page %d", i+1);
        
        frame = gtk_frame_new (bufferf);
        gtk_widget_set_margin_start(frame, 10);
        gtk_widget_set_margin_end(frame, 10);
        gtk_widget_set_margin_top(frame, 10);
        gtk_widget_set_margin_bottom(frame, 10);
        gtk_widget_set_size_request (frame, 100, 75);
        gtk_widget_show (frame);
        
        label = gtk_label_new (bufferf);

        label = gtk_label_new (bufferl);
        gtk_notebook_append_page (GTK_NOTEBOOK (notebook_1), frame, label);
    }
    
    
    /* now lets add a page to a specific spot */
    checkbutton = gtk_check_button_new_with_label ("Check me please!");
    gtk_widget_set_size_request(checkbutton, 100, 75);
    gtk_widget_show (checkbutton);
    
    label = gtk_label_new ("Add spot");
    label = gtk_label_new ("Add page");
    gtk_notebook_insert_page (GTK_NOTEBOOK (notebook_1), checkbutton, label, 2);
    
    /* Now finally lets prepend pages to the notebook */
    for (i=0; i < 5; i++) {
        sprintf(bufferf, "Prepend Frame %d", i+1);
        sprintf(bufferl, "PPage %d", i+1);
        
        frame = gtk_frame_new (bufferf);
        gtk_widget_set_margin_start(frame, 10);
        gtk_widget_set_margin_end(frame, 10);
        gtk_widget_set_margin_top(frame, 10);
        gtk_widget_set_margin_bottom(frame, 10);
        gtk_widget_set_size_request (frame, 100, 75);
        gtk_widget_show (frame);
        
        label = gtk_label_new (bufferf);

        
        label = gtk_label_new (bufferl);
        gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook_1), frame, label);
    }
    
    /* Set what page to start at (page 4) */
    gtk_notebook_set_current_page (GTK_NOTEBOOK(notebook_1), 3);
    
    
    button = gtk_button_new_with_label ("move page");
    g_signal_connect (button, "clicked",
                               G_CALLBACK (move_page),
                               NULL);
    gtk_grid_attach(GTK_GRID(table), button, 6,1,1,1);
    gtk_widget_show(button);

    
    notebook_2 = gtk_notebook_new();
    gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook_2), GTK_POS_TOP);
    gtk_grid_attach(GTK_GRID (table), notebook_2, 0,2,6,1);
    gtk_widget_show(notebook_2);
    

    gtk_notebook_set_group_name(notebook_2,"group");


    for (i=0; i < 5; i++) {
      sprintf(istr, "N2 %d", i);
      

        frame = gtk_frame_new (istr);
        gtk_widget_set_margin_start(frame, 10);
        gtk_widget_set_margin_end(frame, 10);
        gtk_widget_set_margin_top(frame, 10);
        gtk_widget_set_margin_bottom(frame, 10);
        gtk_widget_set_size_request (frame, 100, 75);
        gtk_widget_show (frame);
        
        label = gtk_label_new (istr);
        gtk_notebook_prepend_page (GTK_NOTEBOOK(notebook_2), frame, label);
    }

    gtk_notebook_popup_enable(notebook_2);

    gtk_widget_show(table);
    gtk_widget_show(window);


}
int
main (int    argc,
      char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return status;
}
 

Solution

  • Before I come to my reply, I would like to point out once again that consistent cast seems to be a problem of the given programme!! Please check again thoroughly.

    The other problem is that the function

    gtk-notebook-remove-page (GTK-NOTEBOOK(notebook), page);
    

    Also deletes the connected GtkWidget "child" or decrement the reference pointer by 1.

    I solved the sample as follows:

    /* move a page from notebook_1 to notebook_2 */
    void move_page (GtkButton *button)
    {
        gint page;
        GtkWidget *child;
        GtkWidget *label;
        GtkWidget *scroll_win;
    
    
        page = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook_1));
        g_print("Current_page ist %i\n",page);
        child = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook_1), page);
    
        if (GTK_IS_WIDGET(child))
                    g_print("child is GtkWidget %i\n",page);
            else
                    g_print("child is not GtkWidget %i\n",page);
    
        g_object_ref(G_OBJECT(child));
    
        label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook_1), child);
        gtk_notebook_remove_page (GTK_NOTEBOOK(notebook_1), page);
    
        if (GTK_IS_WIDGET(child))
                    g_print("child is GtkWidget %i\n",page);
            else
                    g_print("child is not GtkWidget %i\n",page);
    
    
    
        gtk_notebook_append_page(GTK_NOTEBOOK (notebook_2), child, label);
    
        /* If I use this instead of child, no errors are thrown in the console and the
           label info is transferred to notebook_2
        scroll_win = gtk_scrolled_window_new();
        gtk_notebook_append_page(notebook_2, scroll_win, label);
        */
        gtk_widget_queue_draw(GTK_WIDGET(notebook_1));
    
    }
    
    

    That's how it worked for me.

    You can comment out g_object_ref(G_OBJECT(child)); times, you can do so, you also see how I came to it.

    Have fun programming.