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

GTK3 C程序键盘按键仅更新左侧按钮图片问题求助

问题描述

使用C语言结合GTK3编写程序,期望按下左、上、右方向键时,分别更新对应左侧、上方、右侧按钮的图片,但无论按下哪个方向键,仅左侧按钮的图片会更新。

错误原因分析
  1. 信号连接逻辑错误:给窗口的key-press-event和key-release-event重复连接了三次回调,分别传入button_l、button_r、button_up作为user_data。但GTK信号回调按连接顺序执行,第一个回调(传入button_l)处理所有按键事件后返回TRUE,导致后续回调永远不会触发,因此无论按哪个键,只会用button_l更新图片。
  2. 回调逻辑不匹配:回调函数原本依赖user_data动态指定按钮,但实际需求是特定按键对应固定按钮,逻辑设计不符合预期。
  3. 窗口焦点问题:原Glade中窗口的can-focus属性设为False,可能导致窗口无法接收按键事件。
  4. 内存泄漏: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 02:17:02