You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 01:03:03