Как прокрутить до выбранного элемента в GtkListView?

Я хочу использовать виджет 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 не происходит прокрутка.

Просто добавьте printf в функцию обратного вызова scroll_set. Вы обнаружите, что он никогда не звонил!

Ghorban M. Tavakoly 04.09.2024 20:02

Да, я это заметил. Не получив никакого решения.

dibyendu 04.09.2024 21:56

Многие из ваших приведения типов не нужны.

Ghorban M. Tavakoly 04.09.2024 23:24

Это основная причина проблемы? Можете ли вы сказать мне, где проблема? Я тестирую это; Очень многое приходится редактировать. Я добавил приведение типов, чтобы избежать массажа ошибок.

dibyendu 05.09.2024 03:35

Приведения типов не являются основной причиной, но большинство из них в вашем коде избыточны. например в функции обратного вызова scroll_set тип scrinfoGtkScrollInfo *. Поэтому вам не нужно транслировать его на (GtkScrollInfo *) в gtk_scroll_info_set_enable_vertical вызове. В следующей строке приведение к (GtkListView *) является избыточным, поскольку listview равно GtkListView *. Надеюсь, сегодня я просмотрю и отлажу ваш код.

Ghorban M. Tavakoly 05.09.2024 09:28

Если вы новичок в GTK и Gnome, я предлагаю использовать Workbench и сначала создать прототип вашего GTK-приложения на языке высокого уровня.

Ghorban M. Tavakoly 05.09.2024 09:34
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот слегка измененное приложение на 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, вам следует изучить его внутреннюю структуру.

Когда приложение запускается, я хочу, чтобы оно прокрутилось до выбранного элемента. Но кнопка прокрутки в вашем скрипте была использована. Поэтому автоматической прокрутки при запуске приложения не произошло. На самом деле я пытаюсь создать виджет выбора шрифта. Спасибо.

dibyendu 05.09.2024 13:02

Я удалил кнопку прокрутки и добавил selection_model.set_selected(9) перед window.present(). Сигнал был отправлен, и его обратный вызов был выполнен, но прокрутка не работала. Я думаю, что в этом состоянии размер и расположение виджетов еще не рассчитаны. Лучшее место, где можно получить ответ на свой вопрос — это Группа матриц Gtk, где существуют основные разработчики gtk.

Ghorban M. Tavakoly 05.09.2024 14:19

Документация Gtk4: gtk_list_view_scroll_to — «Эта функция работает независимо от того, отображается ли представление списка или находится в фокусе».

dibyendu 05.09.2024 17:12

За свою жизнь GtkWidget имеет много состояний, например realize, map, ... . Я видел эту фразу в документе Gtk, но в обработчике activate виджеты не достигают этих состояний (показать, ...) и еще не полностью созданы. Размеры зависят от иерархических вычислений (Gtk должен знать размеры всех дочерних виджетов, чтобы рассчитать размер родительского виджета). Мы устанавливаем выделенную строку и хотим прокрутить ее до нее, но Gtk не знает размеров ListView и ScrolledWindow. Прочтите это и это.

Ghorban M. Tavakoly 05.09.2024 19:03

Я отредактировал свой пост и добавил новую программу. Это работает так, как вы хотели.

Ghorban M. Tavakoly 05.09.2024 19:20

Другие вопросы по теме