Я хочу использовать виджет GtkListView в своем приложении. Я добавил в него несколько элементов. И я установил элемент в качестве выбранного по умолчанию элемента GtkListView. Но проблема в том, что при запуске приложения я не могу автоматически перейти к выбранному по умолчанию элементу. Кто-нибудь может это проверить, пожалуйста? Демо-скрипт приведен ниже ---
#include <gtk/gtk.h>
static void
setup_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data)
{
GtkWidget *lb = gtk_label_new (NULL);
gtk_list_item_set_child (listitem, lb);
}
static void
bind_cb (GtkSignalListItemFactory *self, GtkListItem *listitem, gpointer user_data) {
GtkWidget *lb = gtk_list_item_get_child (listitem);
GtkStringObject *strobj = gtk_list_item_get_item (listitem);
gtk_label_set_text (GTK_LABEL (lb), gtk_string_object_get_string (strobj));
}
static void
scroll_set(GtkListView *listview, gpointer user_data)
{
GtkScrollInfo *scrinfo = gtk_scroll_info_new ();
gtk_scroll_info_set_enable_vertical ((GtkScrollInfo *)scrinfo, TRUE);
gtk_list_view_scroll_to ((GtkListView *)listview, 10, GTK_LIST_SCROLL_FOCUS, scrinfo); //avilable 4.12
}
static void
app_activate (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkWidget *win = gtk_application_window_new (app);
gtk_window_set_default_size (GTK_WINDOW (win), 200, 100);
GtkWidget *scr = gtk_scrolled_window_new ();
gtk_window_set_child (GTK_WINDOW (win), scr);
char *array[] = {
"one", "two", "three", "four", "five" ,"six"," seven",
"eight" ,"nine", "ten", "eleven", "twelve", "thirteen",NULL
};
GtkWidget *lv = gtk_list_view_new(NULL,NULL);
GtkStringList *sl = gtk_string_list_new ((const char * const *) array);
GtkSingleSelection *selection = gtk_single_selection_new(G_LIST_MODEL(sl));
gtk_single_selection_set_autoselect(selection, TRUE);
gtk_list_view_set_model(GTK_LIST_VIEW(lv),GTK_SELECTION_MODEL(selection));
GtkListItemFactory *factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", G_CALLBACK (setup_cb), NULL);
g_signal_connect (factory, "bind", G_CALLBACK (bind_cb), NULL);
gtk_list_view_set_factory (GTK_LIST_VIEW (lv),factory);
gtk_single_selection_set_selected ((GtkSingleSelection *)selection, 10);
gtk_widget_set_visible(lv, TRUE);
/*scroll_set cb not working*/
g_signal_connect (lv, "show", G_CALLBACK (scroll_set), NULL);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scr), lv);
gtk_window_present (GTK_WINDOW (win));
}
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new ("com.gtk.listview.test", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}
Примечание. Здесь номер позиции 10 установлен как выбранный элемент. При запуске приложения до пункта №10 не происходит прокрутка.
Да, я это заметил. Не получив никакого решения.
Многие из ваших приведения типов не нужны.
Это основная причина проблемы? Можете ли вы сказать мне, где проблема? Я тестирую это; Очень многое приходится редактировать. Я добавил приведение типов, чтобы избежать массажа ошибок.
Приведения типов не являются основной причиной, но большинство из них в вашем коде избыточны. например в функции обратного вызова scroll_set
тип scrinfo
— GtkScrollInfo *
. Поэтому вам не нужно транслировать его на (GtkScrollInfo *)
в gtk_scroll_info_set_enable_vertical
вызове. В следующей строке приведение к (GtkListView *)
является избыточным, поскольку listview
равно GtkListView *
. Надеюсь, сегодня я просмотрю и отлажу ваш код.
Если вы новичок в GTK и Gnome, я предлагаю использовать Workbench и сначала создать прототип вашего GTK-приложения на языке высокого уровня.
Вот слегка измененное приложение на Python:
import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk
def on_list_item_factory_setup(signal_list_item_factory, objekt):
objekt.set_child(Gtk.Label())
def on_list_item_factory_bind(signal_list_item_factory, objekt):
objekt.get_child().set_label(objekt.get_item().get_string())
def on_selection_model_selection_changed(selection_model, position, n_items, user_data):
list_view = user_data
scroll_info = Gtk.ScrollInfo.new()
scroll_info.set_enable_vertical(True)
list_view.scroll_to(selection_model.get_selected(), Gtk.ListScrollFlags.FOCUS, scroll_info)
def on_spin_button_value_changed(spin_button, user_data):
selection_model = user_data
selection_model.set_selected(spin_button.get_value_as_int() - 1)
def on_application_activate(app):
arr = [
'one', 'two', 'three', 'four', 'five' ,'six',' seven',
'eight' ,'nine', 'ten', 'eleven', 'twelve', 'thirteen'
]
window = Gtk.ApplicationWindow(application=app)
window.set_default_size(200, 100)
header_bar = Gtk.HeaderBar.new()
window.set_titlebar(header_bar)
adjustment = Gtk.Adjustment.new(1, 1, len(arr), 1, 1, 1)
spin_button = Gtk.SpinButton.new(adjustment, 0, 0)
header_bar.pack_end(spin_button)
scrolled = Gtk.ScrolledWindow.new()
window.set_child(scrolled)
list_model = Gtk.StringList.new(arr)
selection_model = Gtk.SingleSelection.new(list_model)
selection_model.set_autoselect(True)
spin_button.connect('value_changed', on_spin_button_value_changed, selection_model)
list_item_factory = Gtk.SignalListItemFactory.new()
list_item_factory.connect('setup', on_list_item_factory_setup)
list_item_factory.connect('bind', on_list_item_factory_bind)
list_view = Gtk.ListView.new(selection_model, list_item_factory)
selection_model.connect('selection-changed', on_selection_model_selection_changed, list_view)
scrolled.set_child(list_view)
window.present()
if __name__ == '__main__':
import sys
app = Gtk.Application(application_id='com.example.ScrollTestApp')
app.connect('activate', on_application_activate)
app.run(sys.argv)
и его версия C:
#include <gtk/gtk.h>
static void
on_list_item_factory_setup (GtkSignalListItemFactory *item_factory, GObject* objekt, gpointer user_data)
{
GtkListItem *list_item = GTK_LIST_ITEM (objekt);
GtkWidget *label = gtk_label_new (NULL);
gtk_list_item_set_child (list_item, label);
}
static void
on_list_item_factory_bind (GtkSignalListItemFactory *item_factory, GObject *objekt, gpointer user_data)
{
GtkListItem *list_item = GTK_LIST_ITEM (objekt);
GtkWidget *label = gtk_list_item_get_child (list_item);
GtkStringObject *string_object = gtk_list_item_get_item (list_item);
gtk_label_set_text (GTK_LABEL (label), gtk_string_object_get_string (string_object));
}
static void
on_selection_model_selection_changed (GtkSelectionModel* selection_model, guint position, guint n_items, gpointer user_data)
{
GtkListView *list_view = GTK_LIST_VIEW (user_data);
GtkScrollInfo *scroll_info = gtk_scroll_info_new ();
gtk_scroll_info_set_enable_vertical (scroll_info, TRUE);
int selected = gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (selection_model));
gtk_list_view_scroll_to (list_view, selected, GTK_LIST_SCROLL_FOCUS, scroll_info);
}
static void
on_spin_button_value_changed (GtkSpinButton *spin_button, gpointer user_data)
{
GtkSingleSelection *selection_model = user_data;
int pos = gtk_spin_button_get_value_as_int(spin_button) - 1;
gtk_single_selection_set_selected (selection_model, pos);
}
static void
on_application_activate (GApplication *application, gpointer user_data)
{
char *array[] = {
"one", "two", "three", "four", "five" , "six", "seven",
"eight" ,"nine", "ten", "eleven", "twelve", "thirteen",
NULL
};
GtkWidget *window = gtk_application_window_new (GTK_APPLICATION (application));
gtk_window_set_default_size (GTK_WINDOW (window), 200, 100);
GtkWidget *header_bar = gtk_header_bar_new ();
gtk_window_set_titlebar (GTK_WINDOW (window), header_bar);
GtkAdjustment *adjustment = gtk_adjustment_new (1, 1, G_N_ELEMENTS (array), 1, 1, 1);
GtkWidget *spin_button = gtk_spin_button_new(adjustment, 0, 0);
gtk_header_bar_pack_end (GTK_HEADER_BAR (header_bar), spin_button);
GtkWidget *scrolled = gtk_scrolled_window_new ();
gtk_window_set_child (GTK_WINDOW (window), scrolled);
GtkStringList *list_model = gtk_string_list_new ((const char * const *) array);
GtkSingleSelection *selection_model = gtk_single_selection_new (G_LIST_MODEL (list_model));
gtk_single_selection_set_autoselect(selection_model, TRUE);
g_signal_connect(spin_button, "value_changed", G_CALLBACK (on_spin_button_value_changed), selection_model);
GtkListItemFactory *list_item_factory = gtk_signal_list_item_factory_new ();
g_signal_connect (list_item_factory, "setup", G_CALLBACK (on_list_item_factory_setup), NULL);
g_signal_connect (list_item_factory, "bind", G_CALLBACK (on_list_item_factory_bind), NULL);
GtkWidget *list_view = gtk_list_view_new (GTK_SELECTION_MODEL (selection_model), list_item_factory);
g_signal_connect(selection_model, "selection-changed", G_CALLBACK (on_selection_model_selection_changed), list_view);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled), list_view);
gtk_window_present (GTK_WINDOW (window));
}
int
main (int argc, char **argv)
{
GtkApplication *application = gtk_application_new ("com.example.ScrollTestApp", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (application, "activate", G_CALLBACK (on_application_activate), NULL);
int stat = g_application_run (G_APPLICATION (application), argc, argv);
g_object_unref (application);
return stat;
}
Я не знаю, почему при инициализации приложения некоторые сигналы не излучаются. Требуется эксперт GTK, чтобы ответить. Но как видите, сигнальная система после этого работает корректно.
Как видите, для небольших целей тестирования может помочь прототипирование на Python, Javascript и даже на Vala.
В комментариях я писал о ранней установке выбранной строки и прокрутке до выбранной строки, при этом GTK не знает размеров виджета. Здесь я добавил простой цикл после gtk_window_present
, чтобы попросить Gtk завершить все операции по обработке событий (например, вычисление размеров, обработку событий ввода, рисунков и т. д.), а затем установить строку. Он работает так, как задумано, и представление списка прокручивается правильно и показывает выбранную строку:
#include <gtk/gtk.h>
static void
on_list_item_factory_setup (GtkSignalListItemFactory *item_factory, GObject* objekt, gpointer user_data)
{
GtkListItem *list_item = GTK_LIST_ITEM (objekt);
GtkWidget *label = gtk_label_new (NULL);
gtk_list_item_set_child (list_item, label);
}
static void
on_list_item_factory_bind (GtkSignalListItemFactory *item_factory, GObject *objekt, gpointer user_data)
{
GtkListItem *list_item = GTK_LIST_ITEM (objekt);
GtkWidget *label = gtk_list_item_get_child (list_item);
GtkStringObject *string_object = gtk_list_item_get_item (list_item);
gtk_label_set_text (GTK_LABEL (label), gtk_string_object_get_string (string_object));
}
static void
on_selection_model_selection_changed (GtkSelectionModel* selection_model, guint position, guint n_items, gpointer user_data)
{
GtkListView *list_view = GTK_LIST_VIEW (user_data);
GtkScrollInfo *scroll_info = gtk_scroll_info_new ();
gtk_scroll_info_set_enable_vertical (scroll_info, TRUE);
int selected = gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (selection_model));
gtk_list_view_scroll_to (list_view, selected, GTK_LIST_SCROLL_FOCUS, scroll_info);
}
static void
on_application_activate (GApplication *application, gpointer user_data)
{
char *array[] = {
"one", "two", "three", "four", "five" , "six", "seven",
"eight" ,"nine", "ten", "eleven", "twelve", "thirteen",
NULL
};
GtkWidget *window = gtk_application_window_new (GTK_APPLICATION (application));
gtk_window_set_default_size (GTK_WINDOW (window), 200, 100);
GtkWidget *scrolled = gtk_scrolled_window_new ();
gtk_window_set_child (GTK_WINDOW (window), scrolled);
GtkStringList *list_model = gtk_string_list_new ((const char * const *) array);
GtkSingleSelection *selection_model = gtk_single_selection_new (G_LIST_MODEL (list_model));
gtk_single_selection_set_autoselect(selection_model, TRUE);
GtkListItemFactory *list_item_factory = gtk_signal_list_item_factory_new ();
g_signal_connect (list_item_factory, "setup", G_CALLBACK (on_list_item_factory_setup), NULL);
g_signal_connect (list_item_factory, "bind", G_CALLBACK (on_list_item_factory_bind), NULL);
GtkWidget *list_view = gtk_list_view_new (GTK_SELECTION_MODEL (selection_model), list_item_factory);
g_signal_connect(selection_model, "selection-changed", G_CALLBACK (on_selection_model_selection_changed), list_view);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled), list_view);
gtk_window_present (GTK_WINDOW (window));
while (g_main_context_pending (g_main_context_default ()))
g_main_context_iteration (NULL, TRUE);
gtk_single_selection_set_selected (selection_model, 10);
}
int
main (int argc, char **argv)
{
GtkApplication *application = gtk_application_new ("com.example.ScrollTestApp", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (application, "activate", G_CALLBACK (on_application_activate), NULL);
int stat = g_application_run (G_APPLICATION (application), argc, argv);
g_object_unref (application);
return stat;
}
Gtk, а также его объектная система GObject, GType... просто потрясающие. Я настоятельно рекомендую каждому разработчику C изучить его внутренности и коды. Если вы разрабатываете виджеты, библиотеки и т. д. на основе Gtk, вам следует изучить его внутреннюю структуру.
Когда приложение запускается, я хочу, чтобы оно прокрутилось до выбранного элемента. Но кнопка прокрутки в вашем скрипте была использована. Поэтому автоматической прокрутки при запуске приложения не произошло. На самом деле я пытаюсь создать виджет выбора шрифта. Спасибо.
Я удалил кнопку прокрутки и добавил selection_model.set_selected(9)
перед window.present()
. Сигнал был отправлен, и его обратный вызов был выполнен, но прокрутка не работала. Я думаю, что в этом состоянии размер и расположение виджетов еще не рассчитаны. Лучшее место, где можно получить ответ на свой вопрос — это Группа матриц Gtk, где существуют основные разработчики gtk.
Документация Gtk4: gtk_list_view_scroll_to
— «Эта функция работает независимо от того, отображается ли представление списка или находится в фокусе».
За свою жизнь GtkWidget
имеет много состояний, например realize
, map
, ... . Я видел эту фразу в документе Gtk, но в обработчике activate
виджеты не достигают этих состояний (показать, ...) и еще не полностью созданы. Размеры зависят от иерархических вычислений (Gtk должен знать размеры всех дочерних виджетов, чтобы рассчитать размер родительского виджета). Мы устанавливаем выделенную строку и хотим прокрутить ее до нее, но Gtk не знает размеров ListView
и ScrolledWindow
. Прочтите это и это.
Я отредактировал свой пост и добавил новую программу. Это работает так, как вы хотели.
Просто добавьте
printf
в функцию обратного вызоваscroll_set
. Вы обнаружите, что он никогда не звонил!