如何使用回调修改 GtkWidget 数组的元素

How to modify the elements of an array of GtkWidget's with a callback

我需要编写一个回调来更改存储在 GtkWidget 数组中的标签的字体颜色。

当前状态的代码在这里https://github.com/dustynine/colta-gtk/blob/struct/src/main.c

在初始化接口的函数中,我的数组如下所示:

GtkWidget *cells[CELLS];
for (int i=0; i<CELLS; i++) {
    cells[i] = gtk_label_new("██");
}

连接到按钮的回调:

g_signal_connect(
    G_OBJECT(button),
    "clicked",
    G_CALLBACK(encode),
    make_data(plaintext, key, cells)
);

make_data() 是一个函数,returns 一个带有回调参数的结构:

struct passed_widgets {
    GtkWidget *plaintext;
    GtkWidget *key;
    GtkWidget **cls;
};

gpointer make_data(GtkWidget *text, GtkWidget *key, GtkWidget *cells[CELLS]) {
    static struct passed_widgets pw;
    pw.plaintext = text;
    pw.key = key;
    pw.cls = cells;
    return (gpointer) &pw;
}

在回调本身内部我有这个:

static void encode(GtkButton *button, gpointer *data) {
    struct passed_widgets *pw;
    GdkColor color;
    pw = (struct passed_widgets *) data;

    gdk_color_parse("#07d9d9", &color);
    gtk_widget_modify_fg(GTK_WIDGET(pw -> cls[0]), GTK_STATE_NORMAL, &color);
}

编译器没有抱怨(除了颜色相关内容已被弃用的警告之外)但是当我实际按下按钮时 Gtk 报告:

(colta-gtk:3993): GLib-GObject-WARNING **: invalid uninstantiatable type 'GInterface' in cast to 'GtkWidget'
(colta-gtk:3993): Gtk-CRITICAL **: gtk_widget_modify_fg: assertion 'GTK_IS_WIDGET (widget)' failed

我在 gdb 中挑选了一下,根据 explore pw -> cls[0]

'pw -> cls[0]' is a pointer to a value of type 'GtkWidget'

那么,我该如何使它按预期工作?我怀疑我错误地将数组分配给结构(但这是程序工作的唯一方式)或者在回调结束时尝试修改它时错误地访问它。


编辑: 我按照 liberforce 的建议重写了程序。为我的图形用户界面创建了一个结构:

struct template {
    GtkWidget *window;
    ...
    GtkWidget *plaintext;
    GtkWidget *cells[CELLS];
};

现在我只是将我实例化为 gui 的结构传递给回调:

g_signal_connect(
    G_OBJECT(gui.button),
    "clicked",
    G_CALLBACK(encode),
    &gui
);

但是我再也无法在 encode() 函数中使用任何东西了:

static void encode(GtkButton *button, gpointer data) {
    gchar *plaintext;
    struct template *pw;

    pw = (struct template *) data;

    plaintext = gtk_entry_get_text(GTK_ENTRY(pw -> plaintext));
    printf("   plaintext contents: %s\n", plaintext);

我又一次不明白哪里出了问题。编译器没有发出警告,根据 gdb 类型匹配,但是当我 运行 程序并使用回调时,它给我:

(colta-gtk:21589): Gtk-CRITICAL **: gtk_entry_get_buffer: assertion 'GTK_IS_ENTRY (entry)' failed

(colta-gtk:21589): Gtk-CRITICAL **: gtk_entry_buffer_get_text: assertion 'GTK_IS_ENTRY_BUFFER (buffer)' failed

地址如下:

gui.plaintext address: 0x55babcfc6290
gui address: 0x7ffd13fe8a88
&gui address: 0x7ffd13fe8910
data address: 0x7ffd13fe8910
pw address: 0x7ffd13fe8910
*pw address: 0x7ffd13fe8a88
pw -> plaintext address: (nil)

*pw -> plaintext 导致分段错误

encode原型错误。应该是 gpointer data,而不是 gpointer *datagpointer 已经是 void *.

的类型定义

此外,您的 make_data 函数应该使用指向您的结构参数的指针,然后填充它。不要使用静态结构,因为它会降低代码的可重用性。

通常我只是在我的 main 中创建一个代表我的 gui 的 struct,填充它,然后将堆栈分配的结构(或其子元素)的地址作为最后一个传递g_signal_connect.

的参数

还有一件事是没有必要将指针显式转换为 gpointer,因为每个指针都可以隐式转换为 gpointer

我想我明白了。我在 main()

之外创建结构原型
struct template {
    GtkWidget *window;
    ...
    GtkWidget *plaintext;
    GtkWidget *cells[CELLS];
};

然后在应用程序的 "activate" 信号上调用的回调中,我这样做:

static void activate(GtkApplication* app, gpointer user_data) {
    static struct template gui;

    gui.plaintext = gtk_entry_new();
    printf("gui.plaintext address: %p\n", gui.plaintext);

    g_signal_connect(
        G_OBJECT(gui.button),
        "clicked",
        G_CALLBACK(encode),
        &gui
    );

在编码回调中我有这个

static void encode(GtkButton *button, gpointer data) {
    gchar *plaintext;
    struct template *pw;

    pw = (struct template *) data;

    plaintext = gtk_entry_get_text(GTK_ENTRY(pw -> plaintext));
    printf("   plaintext contents: %s\n", plaintext);

    GdkColor color;
    gdk_color_parse("#ff0000", &color);
    gtk_widget_modify_fg(GTK_WIDGET(pw -> cells[0]), GTK_STATE_NORMAL, 
    &color);
}

现在一切正常。基本上我通过在 activate 回调中将 static 添加到 struct template gui 声明来解决我的问题。我会将 link 留给 github 上文件的工作版本以供将来参考。感谢 liberforce 为我指明了正确的方向。