GTK3 C程序键盘按键仅更新左侧按钮图片问题求助
问题描述
使用C语言结合GTK3编写程序,期望按下左、上、右方向键时,分别更新对应左侧、上方、右侧按钮的图片,但无论按下哪个方向键,仅左侧按钮的图片会更新。
错误原因分析
- 信号连接逻辑错误:给窗口的
key-press-event和key-release-event重复连接了三次回调,分别传入button_l、button_r、button_up作为user_data。但GTK信号回调按连接顺序执行,第一个回调(传入button_l)处理所有按键事件后返回TRUE,导致后续回调永远不会触发,因此无论按哪个键,只会用button_l更新图片。 - 回调逻辑不匹配:回调函数原本依赖
user_data动态指定按钮,但实际需求是特定按键对应固定按钮,逻辑设计不符合预期。 - 窗口焦点问题:原Glade中窗口的
can-focus属性设为False,可能导致窗口无法接收按键事件。 - 内存泄漏:
set_button_icon函数中创建的GdkPixbuf未手动释放,会造成内存泄漏。
解决方案
1. 修改C代码逻辑
调整回调函数,直接根据按键对应全局变量的按钮,同时修复内存泄漏和窗口焦点问题:
#include <gtk/gtk.h> GtkWidget *button_l; GtkWidget *button_r; GtkWidget *button_up; gboolean left_arrow_pressed = FALSE; gboolean right_arrow_pressed = FALSE; gboolean up_arrow_pressed = FALSE; void set_button_icon(GtkWidget *button, const gchar *icon_name) { GdkPixbuf *icon = gdk_pixbuf_new_from_file(icon_name, NULL); if (icon == NULL) { g_printerr("Error loading image: %s\n", icon_name); return; } GtkWidget *image_widget = gtk_image_new_from_pixbuf(icon); gtk_button_set_image(GTK_BUTTON(button), image_widget); // 释放Pixbuf对象,避免内存泄漏 g_object_unref(icon); } gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { if (event->keyval == GDK_KEY_Left) { if (!left_arrow_pressed) { left_arrow_pressed = TRUE; g_print("Left arrow key pressed.\n"); } set_button_icon(button_l, "pressed_icon_l.png"); return TRUE; } else if (event->keyval == GDK_KEY_Right) { if (!right_arrow_pressed) { right_arrow_pressed = TRUE; g_print("Right arrow key pressed.\n"); } set_button_icon(button_r, "pressed_icon_r.png"); return TRUE; } else if (event->keyval == GDK_KEY_Up) { if (!up_arrow_pressed) { up_arrow_pressed = TRUE; g_print("Up arrow key pressed.\n"); } set_button_icon(button_up, "pressed_icon_up.png"); return TRUE; } return FALSE; } gboolean on_key_release(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { if (event->keyval == GDK_KEY_Left) { left_arrow_pressed = FALSE; set_button_icon(button_l, "initial_icon_l.png"); return TRUE; } else if (event->keyval == GDK_KEY_Right) { right_arrow_pressed = FALSE; set_button_icon(button_r, "initial_icon_r.png"); return TRUE; } else if (event->keyval == GDK_KEY_Up) { up_arrow_pressed = FALSE; set_button_icon(button_up, "initial_icon_up.png"); return TRUE; } return FALSE; } int main(int argc, char *argv[]) { GtkBuilder *builder; GtkWidget *window; gtk_init(&argc, &argv); builder = gtk_builder_new(); gtk_builder_add_from_file(builder, "GladeUI_2.glade", NULL); gtk_builder_connect_signals(builder, NULL); window = GTK_WIDGET(gtk_builder_get_object(builder, "window1")); button_l = GTK_WIDGET(gtk_builder_get_object(builder, "button_l")); button_r = GTK_WIDGET(gtk_builder_get_object(builder, "button_r")); button_up = GTK_WIDGET(gtk_builder_get_object(builder, "button_up")); // 设置窗口可获取焦点并主动获取焦点,确保能接收按键事件 gtk_widget_set_can_focus(window, TRUE); gtk_widget_grab_focus(window); set_button_icon(button_l, "initial_icon_l.png"); set_button_icon(button_r, "initial_icon_r.png"); set_button_icon(button_up, "initial_icon_up.png"); // 仅连接一次信号,无需重复绑定 g_signal_connect(window, "key-press-event", G_CALLBACK(on_key_press), NULL); g_signal_connect(window, "key-release-event", G_CALLBACK(on_key_release), NULL); gtk_widget_show(window); gtk_main(); g_object_unref(builder); return 0; }
2. 修改Glade界面文件
将窗口的can-focus属性改为True,确保窗口能接收按键事件:
<?xml version="1.0" encoding="UTF-8"?> <!-- Generated with glade 3.38.2 --> <interface> <requires lib="gtk+" version="3.24"/> <object class="GtkWindow" id="window1"> <property name="can-focus">True</property> <!-- 修改此处为True --> <child> <object class="GtkFixed" id="fixed1"> <property name="visible">True</property> <property name="can-focus">False</property> <child> <object class="GtkButton" id="button_l"> <property name="width-request">50</property> <property name="height-request">50</property> <property name="visible">True</property> <property name="can-focus">True</property> <property name="receives-default">True</property> <signal name="key-press-event" handler="on_button_l_pressed" swapped="no"/> <signal name="pressed" handler="on_button_l_pressed" swapped="no"/> </object> <packing> <property name="x">10</property> <property name="y">100</property> </packing> </child> <child> <object class="GtkButton" id="button_r"> <property name="width-request">50</property> <property name="height-request">50</property> <property name="visible">True</property> <property name="can-focus">True</property> <property name="receives-default">True</property> <signal name="key-press-event" handler="on_button_r_pressed" swapped="no"/> <signal name="pressed" handler="on_button_r_pressed" swapped="no"/> </object> <packing> <property name="x">200</property> <property name="y">100</property> </packing> </child> <child> <object class="GtkButton" id="button_up"> <property name="label" translatable="yes"></property> <property name="width-request">50</property> <property name="height-request">50</property> <property name="visible">True</property> <property name="can-focus">True</property> <property name="receives-default">True</property> <signal name="key-press-event" handler="on_button_up_pressed" swapped="no"/> <signal name="pressed" handler="on_button_up_pressed" swapped="no"/> </object> <packing> <property name="x">100</property> <property name="y">50</property> </packing> </child> </object> </child> </object> </interface>
额外说明
- 若不想使用全局变量,可创建结构体存储三个按钮指针,将结构体指针作为
user_data传入回调,更符合模块化设计。 - 确保图片文件路径正确,程序运行时能读取到
initial_icon_l.png、pressed_icon_l.png等文件。
内容的提问来源于stack exchange,提问作者Vido
相关产品推荐
相关产品推荐

