GTK 4 C语言按键事件处理报错求助:'key-press-event'信号无效
GTK4处理方向键按键事件的问题与解决
问题现象
使用C语言结合GTK4处理方向键按键事件时,尝试绑定key-press-event或key_pressed信号均无效,出现以下警告:
(
:10924): GLib-GObject-WARNING **: 15:26:27.129: ../gobject/gsignal.c:2613: signal 'key-press-event' is invalid for instance '0x7faa268722f0' of type 'GtkApplicationWindow'
项目目标是通过按下/按住方向键移动方块,当前因按键事件处理问题停滞,原代码如下:
#include <gtk/gtk.h> static void draw_function (GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) { // int square_size = 80.0; cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */ cairo_paint (cr); cairo_set_source_rgb(cr, 0.2, 0.3, 0.8); cairo_rectangle(cr, 10, 10, 90, 90); cairo_fill(cr); cairo_save(cr); cairo_scale(cr, 0.6, 0.6); cairo_set_source_rgb(cr, 0.8, 0.3, 0.2); cairo_rectangle(cr, 30, 30, 90, 90); cairo_fill(cr); cairo_restore(cr); cairo_save(cr); cairo_scale(cr, 0.8, 0.8); cairo_set_source_rgb(cr, 0.8, 0.8, 0.2); cairo_rectangle(cr, 50, 50, 90, 90); cairo_fill(cr); cairo_restore(cr); } // Need to keep track of both key press and key release to distinguish unwanted terminal Return from others. // The terminal Return that started the app won't have a key press event. static gboolean key_press_event_cb (GtkWidget *widget,GdkEvent *event,gpointer data) { g_print("GTK Application is activated\n"); return FALSE; //keep processing event } static void activate (GtkApplication *app, gpointer user_data) { GtkWidget *window; GtkWidget *drawingarea; static gboolean key_pressed = FALSE, *p_key_pressed = &key_pressed; window = gtk_application_window_new(app); drawingarea = gtk_drawing_area_new(); button = gtk_button_new_with_label("Hello World"); gtk_window_set_title (GTK_WINDOW(window), "Window"); gtk_window_set_default_size(GTK_WINDOW(window), 400, 400); // gtk_widget_set_events(window, GDK_KEY_PRESS); gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(drawingarea), draw_function, NULL, NULL); g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK (key_press_event_cb), p_key_pressed); // p_key_pressed will be the "data" in cb function // g_signal_connect(button, "clicked", G_CALLBACK (print_hello), NULL); gtk_window_set_child(GTK_WINDOW(window), drawingarea); gtk_widget_show (window); } int main (int argc, char **argv) { GtkApplication *app; int status; app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); status = g_application_run (G_APPLICATION (app), 0, NULL); g_object_unref (app); return status; }
解决思路与修正方案
GTK4彻底弃用了旧的key-press-event这类GDK事件信号,改用**事件控制器(Event Controller)**机制处理输入事件。针对你的需求,需要使用GtkEventControllerKey来捕获按键事件,同时修正代码中的其他错误:
关键修正点
- 移除无效的
key-press-event信号绑定,改用GtkEventControllerKey - 为
GtkDrawingArea添加焦点能力,确保能接收按键事件 - 维护方块位置的状态变量,按键事件触发时更新位置并触发重绘
- 修正原代码中未声明的
button变量、g_application_run参数错误等问题
修改后的完整代码
#include <gtk/gtk.h> // 方块位置状态 typedef struct { int x; int y; } SquarePos; static void draw_function(GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer user_data) { SquarePos *pos = (SquarePos*)user_data; // 绘制背景 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); cairo_paint(cr); // 绘制可移动的蓝色方块 cairo_set_source_rgb(cr, 0.2, 0.3, 0.8); cairo_rectangle(cr, pos->x, pos->y, 90, 90); cairo_fill(cr); // 绘制其他固定方块 cairo_save(cr); cairo_scale(cr, 0.6, 0.6); cairo_set_source_rgb(cr, 0.8, 0.3, 0.2); cairo_rectangle(cr, 30, 30, 90, 90); cairo_fill(cr); cairo_restore(cr); cairo_save(cr); cairo_scale(cr, 0.8, 0.8); cairo_set_source_rgb(cr, 0.8, 0.8, 0.2); cairo_rectangle(cr, 50, 50, 90, 90); cairo_fill(cr); cairo_restore(cr); } // 按键事件回调 static gboolean key_pressed_cb(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, gpointer user_data) { SquarePos *pos = (SquarePos*)user_data; const int step = 5; // 每次移动的步长 // 处理方向键 switch (keyval) { case GDK_KEY_Up: pos->y = MAX(pos->y - step, 0); break; case GDK_KEY_Down: pos->y = MIN(pos->y + step, 310); // 窗口高400,方块高90,限制边界 break; case GDK_KEY_Left: pos->x = MAX(pos->x - step, 0); break; case GDK_KEY_Right: pos->x = MIN(pos->x + step, 310); // 窗口宽400,方块宽90,限制边界 break; default: return FALSE; // 非方向键,不处理 } // 触发重绘 gtk_widget_queue_draw(GTK_WIDGET(gtk_event_controller_get_widget(controller))); return TRUE; // 标记事件已处理 } static void activate(GtkApplication *app, gpointer user_data) { GtkWidget *window; GtkWidget *drawingarea; SquarePos *pos = g_new(SquarePos, 1); pos->x = 10; pos->y = 10; // 初始位置 window = gtk_application_window_new(app); gtk_window_set_title(GTK_WINDOW(window), "Direction Key Move"); gtk_window_set_default_size(GTK_WINDOW(window), 400, 400); drawingarea = gtk_drawing_area_new(); gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(drawingarea), draw_function, pos, g_free); // 让DrawingArea可以获得焦点,接收按键事件 gtk_widget_set_focusable(drawingarea, TRUE); gtk_widget_grab_focus(drawingarea); // 创建按键事件控制器并绑定回调 GtkEventController *key_controller = gtk_event_controller_key_new(); g_signal_connect(key_controller, "key-pressed", G_CALLBACK(key_pressed_cb), pos); gtk_widget_add_controller(drawingarea, key_controller); gtk_window_set_child(GTK_WINDOW(window), drawingarea); gtk_widget_show(window); } int main(int argc, char **argv) { GtkApplication *app; int status; app = gtk_application_new("org.gtk.demo", G_APPLICATION_FLAGS_NONE); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); return status; }
编译运行说明
使用以下命令编译(需安装GTK4开发库):
gcc -o key_move key_move.c `pkg-config --cflags --libs gtk4`
运行后,按下方向键即可移动蓝色方块,方块会被限制在窗口边界内。
内容的提问来源于stack exchange,提问作者Yeezus
相关产品推荐
相关产品推荐

