Я работаю над приложением, которое использует привязки gtk для элементов пользовательского интерфейса, и не могу понять, как сделать что-то, что кажется простым. Я хочу иметь возможность перемещать информацию, содержащуюся на странице блокнота, в другой блокнот (например, переместить страницу 1 из блокнота_1 в блокнот_2).
Я попытался сделать это с помощью следующего фрагмента кода:
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)
Когда я пытаюсь запустить это, я получаю ошибку в консоли:
(:87313): Gtk-CRITICAL **: 19:58:29.515: gtk_notebook_append_page: утверждение «GTK_IS_WIDGET (дочерний)» не выполнено
И nb равно -1, что означает, что операция не удалась.
Я подумал, что, возможно, страница удаления уничтожает дочерний объект, поэтому пропустил эту строку. Но затем я получаю следующую ошибку в консоли
(:87293): Gtk-WARNING **: 19:56:50.684: Повторяющееся дочернее имя в GtkStack: (ноль)
(:87293): Gtk-CRITICAL **: 19:56:50.684: gtk_widget_set_parent: assertion '_gtk_widget_get_parent (widget) == NULL'
не удалось
Когда я запускаю этот параметр, значение nb не равно -1, но информация в дочернем блоке не отображается в блокноте_2.
Я чувствую, что мне здесь не хватает чего-то основного, но я не знаю, чего именно. Я попробовал еще несколько вещей, таких как прикрепление дочернего элемента к прокручиваемому окну, а также воспроизвел ошибку, используя код C (вместо привязок). Если у кого-нибудь есть какие-либо советы о том, как это сделать или указания по дальнейшему устранению неполадок, я буду признателен.
Обновление - вот полное приложение C, которое вызывает у меня проблему, которое я собрал из старого gtk2? Пример блокнота, который я нашел в сети. Когда я запускаю его и нажимаю кнопку перемещения страницы, он не работает. Я действительно не знаю c, так что извините, если это беспорядок, но это воспроизводит проблему FWIW.
Кажется, то, что я хочу сделать, невозможно: когда я удаляю старую страницу, она, вероятно, стирает ссылку на дочернюю страницу, но когда я не удаляю старую страницу, она жалуется, что дочерняя страница существует где-то еще.
#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;
}
Добро пожаловать!
Сообщение об ошибке:
"gtk-notebook-append-page: assertion 'GTK-IS-WIDGET (child)' failed" indicates that a GtkWidget cannot be cast into a GtkWidget.
Поэтому проверьте, все ли GtkWidget созданы как GtkWidget.
GtkWidget *child = gtk_....._new();
GtkWidget *label = gtk_label_new(); ect
Виджеты должны быть преобразованы соответствующим образом, если они будут использоваться в функции.
Пример:
gtk_label_set_label(GTK_LABEL(label),"my Text");
Для более детального устранения неполадок потребуется больше кода.
Получайте удовольствие от программирования.
Итак, я нашел способ заставить эту работу работать по большей части. Если я подключусь к сигналу «page_closed» и перемещу страницу в обратном вызове, это сработает.
К сожалению, я теряю информацию о метке, но в консоли нет предупреждений/ошибок, а информация о странице не перемещается. Обновленное решение в C ниже:
#include <gtk/gtk.h>
GtkWidget *notebook_1;
GtkWidget *notebook_2;
void remove_page (GtkNotebook *nbook, GtkWidget *child, gint page)
{
GtkWidget *label;
GtkWidget *scroll_win;
label = gtk_notebook_get_tab_label(nbook, child);
gtk_notebook_append_page(GTK_NOTEBOOK (notebook_2), GTK_WIDGET(child), GTK_LABEL(label));
}
/* 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);
}
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);
g_signal_connect (notebook_1, "page_removed",
G_CALLBACK (remove_page),
NULL);
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;
}
Прежде чем перейти к ответу, я хотел бы еще раз отметить, что постоянство актерского состава, похоже, является проблемой данной программы!! Пожалуйста, проверьте еще раз тщательно.
Другая проблема заключается в том, что функция
gtk-notebook-remove-page (GTK-NOTEBOOK(notebook), page);
Также удаляет подключенный «дочерний» GtkWidget или уменьшает указатель ссылки на 1.
Я решил образец следующим образом:
/* 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));
}
Вот как это сработало для меня.
Комментировать можно g_object_ref(G_OBJECT(child));
раз, можно, вы тоже видите, как я к этому пришел.
Получайте удовольствие от программирования.
Еще раз спасибо за ответ! Я подтвердил, что эта строка кода устраняет мою первоначальную проблему, и я вижу, что «основной» вещью, которой мне не хватало, была концепция g_object_ref/unref. Я не встречал этого в примерах, которые использовал в качестве основы для своего кода, но, просматривая исходный код gtk, он есть повсюду.
Приятно, что я смог решить вашу проблему, но почему бы вам не подтвердить это кнопкой?
Спасибо за прием и предложение! Я использовал привязки Фортрана в своем приложении GTK, поэтому я не привык к кастингу. К лучшему или худшему, но в Фортране все однотипно (c_ptr), поэтому облажаться сложно. В любом случае, я обновил свой пост полным примером на языке C — перед публикацией я взломал его, чтобы убедиться, что это не проблема с привязкой. Попробовал кастовать как вы советовали, но проблему это не решило. Я дважды подтвердил это, добавив в блокнот_2 пустое окно. Думаю, если бы у меня была проблема с кастингом, это тоже пошло бы не так, но это не так.