Функция обратного вызова не изменяет виджет

Я пытаюсь создать приложение GTK 4, написанное на C. Я хочу создать функцию обратного вызова, которая реагирует на вновь выбранный элемент из раскрывающегося виджета и обновляет виджет ввода значением выбранного элемента.

Моя функция работает только в том случае, если я делаю виджет ввода глобальной переменной и использую его непосредственно в функции. Я бы предпочел передать указатель на этот виджет и заставить функцию обратного вызова изменить его таким образом. Другие функции, которым передавались указатели на виджеты, работали, а эта не хочет.

//main.c
#include <gtk/gtk.h>
#include <windows.h>
#include <stdio.h>
#include <stdint.h>

#include "uiElements.h"


static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, void *data[]);
static void activateApp(GApplication *app);


int main (int argc, char *argv[]) {

  //starting application
  return startApp("exe.GTKforSO", G_APPLICATION_DEFAULT_FLAGS, activateApp, argc, argv);
}

static void activateApp (GApplication *app) {

  static GtkWidget *win;
  static GtkWidget *box;

  //window
  win=initWindow(app, "GTKforSO", 400, 200);

  //box
  box=initBox(GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_window_set_child(GTK_WINDOW(win), box);

  static GtkWidget *baudrateDropdown;
  static GtkWidget *baudratesBox;
  static GtkWidget *baudrateEntry;
  static GtkEntryBuffer *baudrateEntryBuffer;


    /** the important part **/
      baudratesBox=initBox(GTK_ORIENTATION_HORIZONTAL, 10);
        gtk_box_append(GTK_BOX(box), baudratesBox);

      char *baudrates[] = {"50", "75", "110", "134",
          "150", "200", "300", "600", "1200", "1800",
          "2400", "4800", "9600", "19200", "28800",
          "38400", "57600", "76800", "115200", "230400",
          "460800", "576000", "921600", NULL};
      baudrateDropdown=initDropDown(baudrates, 18); //dropdown widget with 'baudrates' list of items and 18th item selected
        gtk_box_append(GTK_BOX(baudratesBox), baudrateDropdown);

      void *selectedItem=gtk_drop_down_get_selected_item(GTK_DROP_DOWN(baudrateDropdown)); //getting selected item from dropdown widget
      const char *selectedBaudrate=gtk_string_object_get_string(GTK_STRING_OBJECT(selectedItem)); //getting item value as string
      baudrateEntryBuffer=gtk_entry_buffer_new((const char*)selectedBaudrate, strlen(selectedBaudrate)); //new entry buffer widget
      baudrateEntry=gtk_entry_new_with_buffer(GTK_ENTRY_BUFFER(baudrateEntryBuffer)); //new entry widget with buffer 'baudrateEntryBuffer'

      void *entryAndBuffer[2] = {baudrateEntry, baudrateEntryBuffer}; //data to pass to callback
      g_signal_connect(baudrateDropdown, "notify::selected-item", G_CALLBACK(baudrateDropdownSelectCallback), entryAndBuffer); //connection to callback
        gtk_box_append(GTK_BOX(baudratesBox), baudrateEntry);
    /**/


  gtk_window_present(GTK_WINDOW(win));
}

static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, void *data[]) {

    void *newlySelectedItem=gtk_drop_down_get_selected_item(GTK_DROP_DOWN(dropdown)); //getting selected item from dropdown widget
    char *selected=gtk_string_object_get_string(GTK_STRING_OBJECT(newlySelectedItem)); //getting item value as string

    GtkEntryBuffer *buffer;
    buffer=gtk_entry_buffer_new(selected, strlen(selected));
    data[1]=buffer; //replacing old buffer with new one

    gtk_entry_set_buffer(GTK_ENTRY(data[0]), data[1]);       //this doesn't work
  //gtk_entry_set_buffer(GTK_ENTRY(baudrateEntry), data[1]); //this works if static GtkWidget *baudrateEntry; is global variable
}
//uiElements.h
#ifndef UI_ELEMENTS_H
#define UI_ELEMENTS_H

#include <gtk/gtk.h>

typedef enum {
    NO_ENTRY=0,
    WITH_ENTRY=1
} withEntry;

int startApp(char *id, GApplicationFlags flags, void (*activateFunc), int argc, char *argv[]);
GtkWidget *initWindow(GApplication  *app, char *windowTitle, int windowWidth, int windowHeight);
GtkWidget *initBox(GtkOrientation orientation, int spacing);
GtkWidget *initDropDown(char *list[], uint8_t selectedByDefault);


#endif /* UI_ELEMENTS_H */
//uiElements.c
#include "uiElements.h"

int startApp(char *id, GApplicationFlags flags, void (*activateFunc), int argc, char *argv[]) {
    GtkApplication *app;
    int stat;

    app=gtk_application_new(id, flags);
    g_signal_connect(app, "activate", G_CALLBACK(activateFunc), NULL);
    stat=g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);

    return stat;
}

GtkWidget *initWindow(GApplication  *app, char *windowTitle, int windowWidth, int windowHeight) {
    GtkWidget *win;
    win=gtk_application_window_new(GTK_APPLICATION(app));
    gtk_window_set_title(GTK_WINDOW(win), windowTitle);
    gtk_window_set_default_size(GTK_WINDOW(win), windowWidth, windowHeight);

    return win;
}

GtkWidget *initBox(GtkOrientation orientation, int spacing) {
    GtkWidget *box;
    box=gtk_box_new(orientation, spacing);
    gtk_box_set_homogeneous(GTK_BOX(box), TRUE);

    return box;
}

GtkWidget *initDropDown(char *list[], uint8_t selectedByDefault) {
    GtkStringList *stringList=gtk_string_list_new((const char * const *)list);
    GtkWidget *dropdown;
    dropdown=gtk_drop_down_new(G_LIST_MODEL(stringList), NULL);
    gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), selectedByDefault);

    return(dropdown);
}

Стоит ли изучать 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
0
82
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Во-первых: Большое спасибо yvs2014 из discourse.gnome.org за помощь!


Очевидно, функцию обратного вызова следует объявить как:

static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, void *unused, void *data[])

Решение, вероятно, связано с реализацией этой функции из GNOME GTK Repository:

static void
selected_item_changed (GtkDropDown *self,
                       GParamSpec  *pspec,
                       GtkListItem *list_item)
{
  GtkWidget *box;
  GtkWidget *icon;
  box = gtk_list_item_get_child (list_item);
  icon = gtk_widget_get_last_child (box);
  if (gtk_drop_down_get_selected_item (self) == gtk_list_item_get_item (list_item))
    gtk_widget_set_opacity (icon, 1.0);
  else
    gtk_widget_set_opacity (icon, 0.0);
}


Он также работает со структурами. Вот рабочий код, который заменяет void *data[] на WidgetStruct *data.

//main.c
#include <gtk/gtk.h>
#include <windows.h>
#include <stdio.h>
#include <stdint.h>

#include "uiElements.h"


typedef struct {

   GtkWidget *win;
   GtkWidget *box;
   GtkWidget *baudrateDropdown;
   GtkWidget *baudratesBox;
   GtkWidget *baudrateEntry;
   GtkEntryBuffer *baudrateEntryBuffer;

} WidgetStruct;

WidgetStruct widgetStr;
WidgetStruct *widgetStrPtr=&widgetStr;

static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, void *unused, WidgetStruct *data);
static void activateApp(GApplication *app);


int main (int argc, char *argv[]) {

  widgetStrPtr=g_new0(WidgetStruct, 1);

  //starting application
  return startApp("exe.GTKforSO", G_APPLICATION_DEFAULT_FLAGS, activateApp, argc, argv);
}

static void activateApp (GApplication *app) {

  static GtkWidget *win;
  static GtkWidget *box;

  //window
  win=initWindow(app, "GTKforSO", 400, 200);

  //box
  box=initBox(GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_window_set_child(GTK_WINDOW(win), box);

  static GtkWidget *baudrateDropdown;
  static GtkWidget *baudratesBox;
  static GtkWidget *baudrateEntry;
  static GtkEntryBuffer *baudrateEntryBuffer;


    /** the important part **/
      baudratesBox=initBox(GTK_ORIENTATION_HORIZONTAL, 10);
        gtk_box_append(GTK_BOX(box), baudratesBox);

      char *baudrates[] = {"50", "75", "110", "134",
          "150", "200", "300", "600", "1200", "1800",
          "2400", "4800", "9600", "19200", "28800",
          "38400", "57600", "76800", "115200", "230400",
          "460800", "576000", "921600", NULL};
      baudrateDropdown=initDropDown(baudrates, 18); //dropdown widget with 'baudrates' list of items and 18th item selected
        gtk_box_append(GTK_BOX(baudratesBox), baudrateDropdown);

      void *selectedItem=gtk_drop_down_get_selected_item(GTK_DROP_DOWN(baudrateDropdown)); //getting selected item from dropdown widget
      const char *selectedBaudrate=gtk_string_object_get_string(GTK_STRING_OBJECT(selectedItem)); //getting item value as string
      baudrateEntryBuffer=gtk_entry_buffer_new((const char*)selectedBaudrate, strlen(selectedBaudrate)); //new entry buffer widget
      baudrateEntry=gtk_entry_new_with_buffer(GTK_ENTRY_BUFFER(baudrateEntryBuffer)); //new entry widget with buffer 'baudrateEntryBuffer'

    widgetStrPtr->box=box;
    widgetStrPtr->baudrateDropdown=baudrateDropdown;
    widgetStrPtr->baudratesBox=baudratesBox;
    widgetStrPtr->baudrateEntry=baudrateEntry;
    widgetStrPtr->baudrateEntryBuffer=baudrateEntryBuffer;

      g_signal_connect(baudrateDropdown, "notify::selected-item", G_CALLBACK(baudrateDropdownSelectCallback), widgetStrPtr); //connection to callback
        gtk_box_append(GTK_BOX(baudratesBox), baudrateEntry);
    /**/


  gtk_window_present(GTK_WINDOW(win));
}

static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, void *unused, WidgetStruct *data) {

    WidgetStruct *_widgetStr=data;

    void *newlySelectedItem=gtk_drop_down_get_selected_item(GTK_DROP_DOWN(dropdown)); //getting selected item from dropdown widget

    const char *selected=gtk_string_object_get_string(GTK_STRING_OBJECT(newlySelectedItem)); //getting item value as string

    gtk_editable_set_text(GTK_EDITABLE(_widgetStr->baudrateEntry), selected);
}

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