我正在尝试制作一个用 C 编写的 GTK 4 应用程序。我想制作一个回调函数,该函数对下拉小部件中新选择的项目做出反应,并使用所选项目的值更新条目小部件。
仅当我将条目小部件设置为全局变量并直接在函数中使用它时,我的函数才有效。我更愿意传递一个指向该小部件的指针,并让回调函数以这种方式修改它。其他传递给小部件的指针的函数可以工作,但这个函数不想这样做。
//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);
}
首先:非常感谢来自 discourse.gnome.org 的 yvs2014 的帮助!
显然回调函数应该声明为:
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);
}
它也适用于结构。这是一个用 WidgetStruct *data 替换 void *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);
}