使用 Glade 和 GTK+ 将切换按钮相互绑定(在 C 中)

Binding Toggle Buttons to each other using Glade & GTK+ (in C)

我有两个切换按钮,分别名为 运行 和 KILL。当我打开(即按下)运行 按钮时,我只想通过按下 KILL 切换按钮(而不是关闭 运行 按钮)来关闭它。我希望随后在 运行 按钮上的任何鼠标单击都不会执行任何操作,除非按下 KILL 按钮。同样,当 KILL 按钮打开时,我只希望通过按下 运行 按钮将其关闭(同样,不是通过关闭 KILL 按钮)。我不确定如何构建将这两个切换按钮的操作绑定在一起的事件处理程序。我将 GTK+ 与 Glade 一起使用,并在 C 中编程。

由于 GtkToggleButton 是一个通用按钮,它会随所有用户交互进行切换,我们必须防止它在特定条件下切换。在这种情况下,切换后的按钮不能再次切换自身,而是通过另一个切换按钮。为此,您必须:

  1. 防止按钮事件在特定条件下切换按钮
  2. 用反向逻辑绑定两个按钮(互斥)

为了实现 1) 我们可以根据需要的条件将回调连接到 GtkWidget "button-press-event" 信号和 return,TRUE 阻止信号传播,否则为 FALSE。

然后,为了处理 2),我们可以将 g_object_bind_property 用于带有 G_BINDING_INVERTED_BOOLEAN 标志的 GtkToggleButton "active" 属性 以获得所需的行为。请注意,我们必须将其中之一设为活动状态作为启动条件。​​

所以,一个简单的例子可以是:

#include <gtk/gtk.h>

gboolean on_toggle_button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer user_data) {
   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)) == TRUE) {
      return TRUE;
   }

   return FALSE;
}

void on_run_toggle_active(GObject *obj, GParamSpec *pspec, gpointer user_data) {
   g_return_if_fail (user_data != NULL);

   GtkLabel *label = GTK_LABEL(user_data);
   GtkToggleButton *button = GTK_TOGGLE_BUTTON(obj);

   if (gtk_toggle_button_get_active(button) == TRUE) {
      gtk_label_set_text (label, "Running...");
   } else {
      gtk_label_set_text (label, "Idle");
   }
}

gint main(gint argc, gchar **argv) {
   GtkLabel *status;
   GtkWindow *window;
   GtkBuilder *builder;
   GtkToggleButton *run_toggle;
   GtkToggleButton *kill_toggle;

   gtk_init(&argc, &argv);

   builder = gtk_builder_new_from_file("gui.ui");

   window      = GTK_WINDOW(gtk_builder_get_object(builder, "window1"));
   status      = GTK_LABEL(gtk_builder_get_object(builder, "status"));
   run_toggle  = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "toggle"));
   kill_toggle = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "kill"));

   g_object_bind_property (G_OBJECT(run_toggle), "active", G_OBJECT(kill_toggle), "active", G_BINDING_INVERT_BOOLEAN);
   g_object_bind_property (G_OBJECT(kill_toggle), "active", G_OBJECT(run_toggle), "active", G_BINDING_INVERT_BOOLEAN);

   g_signal_connect(G_OBJECT(run_toggle), "button-press-event", G_CALLBACK(on_toggle_button_press_event), NULL);
   g_signal_connect(G_OBJECT(kill_toggle), "button-press-event", G_CALLBACK(on_toggle_button_press_event), NULL);
   g_signal_connect(G_OBJECT(run_toggle), "notify::active", G_CALLBACK(on_run_toggle_active), status);
   g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);

   gtk_widget_show_all(GTK_WIDGET(window));

   gtk_main();

   return 0;
}

为了简化,我用 glade (gui.ui) 概述了用户界面:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.2 -->
<interface>
  <requires lib="gtk+" version="3.18"/>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkGrid">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="halign">center</property>
        <property name="valign">center</property>
        <property name="row_spacing">20</property>
        <child>
          <object class="GtkLabel" id="status">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="label" translatable="yes">Idle</property>
          </object>
          <packing>
            <property name="left_attach">0</property>
            <property name="top_attach">1</property>
            <property name="width">2</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkToggleButton" id="toggle">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="focus_on_click">False</property>
                <property name="receives_default">True</property>
                <child>
                  <object class="GtkBox">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="spacing">3</property>
                    <child>
                      <object class="GtkImage">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="icon_name">media-playback-start-symbolic</property>
                      </object>
                      <packing>
                        <property name="expand">False</property>
                        <property name="fill">True</property>
                        <property name="position">0</property>
                      </packing>
                    </child>
                    <child>
                      <object class="GtkLabel" id="label1">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="label" translatable="yes">Run</property>
                      </object>
                      <packing>
                        <property name="expand">False</property>
                        <property name="fill">True</property>
                        <property name="position">1</property>
                      </packing>
                    </child>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkToggleButton" id="kill">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="active">True</property>
                <child>
                  <object class="GtkBox">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="spacing">3</property>
                    <child>
                      <object class="GtkImage">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="icon_name">user-trash-symbolic</property>
                      </object>
                      <packing>
                        <property name="expand">False</property>
                        <property name="fill">True</property>
                        <property name="position">0</property>
                      </packing>
                    </child>
                    <child>
                      <object class="GtkLabel" id="label2">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="label" translatable="yes">Kill</property>
                      </object>
                      <packing>
                        <property name="expand">False</property>
                        <property name="fill">True</property>
                        <property name="position">1</property>
                      </packing>
                    </child>
                  </object>
                </child>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
            <style>
              <class name="linked"/>
            </style>
          </object>
          <packing>
            <property name="left_attach">0</property>
            <property name="top_attach">0</property>
            <property name="width">2</property>
          </packing>
        </child>
      </object>
    </child>
    <child type="titlebar">
      <placeholder/>
    </child>
  </object>
  <object class="GtkSizeGroup">
    <widgets>
      <widget name="label1"/>
      <widget name="label2"/>
    </widgets>
  </object>
</interface>

编译:

gcc -o main main.c `pkg-config --cflags --libs gtk+-3.0`

输出应该是这样的:

切换按钮是相互排斥的,在激活时按下它们不会有任何视觉效果。

PS: 假设没有键盘处理,否则我们必须处理这些信号并采取相应的行动。