使用Glade(和C语言)为GTK+消息对话框设置信号处理程序

3

我在使用GLADE创建消息对话框的C代码中遇到了编写信号处理程序的困难。如果我不使用GLADE,信号处理程序将包含构建消息对话框所需的必要信息。例如,“你确定要退出吗?”消息对话框的形式如下:

void show_question(GtkWidget *widget, gpointer window) {

  GtkWidget *dialog;
  dialog = gtk_message_dialog_new(GTK_WINDOW(window),
          GTK_DIALOG_DESTROY_WITH_PARENT,
          GTK_MESSAGE_QUESTION,
          GTK_BUTTONS_YES_NO,
          "Are you sure to quit?");
  gtk_window_set_title(GTK_WINDOW(dialog), "Question");
  gtk_dialog_run(GTK_DIALOG(dialog));
  gtk_widget_destroy(dialog);
}

同样地,一个“关于窗口”应该有如下形式:
void show_about(GtkWidget *widget, gpointer data) {

  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file("battery.png", NULL);

  GtkWidget *dialog = gtk_about_dialog_new();
  gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog), "Battery");
  gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), "0.9"); 
  gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), "(c) Jan Bodnar");
  gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), "Battery is a simple tool for battery checking.");
  gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "http://www.batteryhq.net");
  gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf);
  g_object_unref(pixbuf), pixbuf = NULL;
  gtk_dialog_run(GTK_DIALOG (dialog));
  gtk_widget_destroy(dialog);
}

现在我完全使用GLADE创建这些窗口,但我不确定信号处理程序(用C语言编写)应该采用什么形式,以便将“单击Help->About”连接到在GLADE中创建的关于窗口,或者将“单击File->Quit”连接到在GLADE中创建的“您确定要退出”的消息对话框。我对GTK+和Glade都很陌生,似乎找不到任何有助于解决这个问题的东西。非常感谢您的帮助。
编辑:下面是一个示例GLADE XML文件,其中包含两个顶级窗口。第一个是带有单个菜单栏的主窗口。第二个是关于对话框。我正在寻求的是编写信号处理程序的帮助,在菜单栏中单击“File->About”后打开关于窗口。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
  <requires lib="gtk+" version="3.12"/>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkBox" id="box1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkMenuBar" id="menubar1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkMenuItem" id="menuitem1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_File</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu1">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem1">
                        <property name="label">gtk-new</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                       <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                     <object class="GtkImageMenuItem" id="imagemenuitem2">
                        <property name="label">gtk-open</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem3">
                        <property name="label">gtk-save</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem4">
                        <property name="label">gtk-save-as</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem5">
                        <property name="label">gtk-quit</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem" id="menuitem2">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_Edit</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu2">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem6">
                        <property name="label">gtk-cut</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem7">
                        <property name="label">gtk-copy</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                     <object class="GtkImageMenuItem" id="imagemenuitem8">
                        <property name="label">gtk-paste</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem9">
                        <property name="label">gtk-delete</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
           <child>
              <object class="GtkMenuItem" id="menuitem3">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_View</property>
                <property name="use_underline">True</property>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem" id="menuitem4">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_Help</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu3">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem10">
                        <property name="label">gtk-about</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                       <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
  <object class="GtkAboutDialog" id="window_about">
    <property name="can_focus">False</property>
    <property name="type_hint">dialog</property>
    <property name="program_name">Glade</property>
    <property name="logo_icon_name">image-missing</property>
    <child internal-child="vbox">
      <object class="GtkBox" id="aboutdialog-vbox1">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox" id="aboutdialog-action_area1">
            <property name="can_focus">False</property>
            <property name="layout_style">end</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <placeholder/>
        </child>
      </object>
    </child>
  </object>
</interface>

你不打算尝试在信号处理程序中显示消息对话框之类的东西,是吧?唯一可以安全地从信号处理程序中调用的函数是异步信号安全函数 - Andrew Henle
我并不是“试图”这样做。看起来似乎是在上面定义的函数中发生了这种情况。我只是想要澄清,考虑到 Glade XML 文件预定义了消息对话框窗口,信号处理程序应该如何构建。 - Leigh K
2个回答

4

如果我理解正确,您想通过使用Glade而不是以编程方式创建对话框。

使用Glade必须使用GtkBuilder

假设您使用Glade创建了一个简单的Yes,No对话框(yesno_dialog.ui):

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.2 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkDialog" id="dialog1">
    <property name="can_focus">False</property>
    <property name="modal">True</property>
    <property name="default_width">275</property>
    <property name="default_height">130</property>
    <property name="type_hint">dialog</property>
    <child internal-child="vbox">
      <object class="GtkBox">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox">
            <property name="can_focus">False</property>
            <property name="hexpand">True</property>
            <property name="layout_style">spread</property>
            <child>
              <object class="GtkButton" id="button1">
                <property name="label">gtk-yes</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button2">
                <property name="label">gtk-no</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="halign">center</property>
            <property name="valign">center</property>
            <property name="hexpand">True</property>
            <property name="vexpand">True</property>
            <property name="label" translatable="yes">Are you sure to quit?</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
    <action-widgets>
      <action-widget response="1">button1</action-widget>
      <action-widget response="2">button2</action-widget>
    </action-widgets>
    <child>
      <placeholder/>
    </child>
  </object>
</interface>

下面是正确的格式:

enter image description here

请注意响应id为1,以备后续参考。

让我们测试一个简单的窗口,其中包含一个按钮,通过其clicked信号处理程序/回调来触发对话框:

#include <gtk/gtk.h>

void on_button_clicked (GtkButton *button, gpointer user_data) {
   int response;
   GtkWidget  *dialog;
   GtkBuilder *builder;

   g_return_if_fail (user_data != NULL);

   builder = gtk_builder_new_from_file("yesno_dialog.ui");

   dialog = GTK_WIDGET(gtk_builder_get_object (builder, "dialog1"));
   gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(user_data));

   gtk_widget_show_all(dialog);

   response = gtk_dialog_run(GTK_DIALOG(dialog));

   gtk_widget_destroy(dialog);

   g_object_unref(G_OBJECT(builder));

   g_print ("Response is %s\n", response == 1 ? "Yes" : "No");
}


int main(int argc, char *argv[]) {
   GtkWidget *button;
   GtkWidget *window;

   gtk_init(&argc,&argv);

   button = gtk_button_new_with_label("Press for dialog");
   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

   gtk_container_add(GTK_CONTAINER(window), button);

   g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(on_button_clicked), window);
   g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);

   gtk_widget_show_all(window);

   gtk_main();

   return 0;
}

当您使用标准响应ID以编程方式构建对话框时,可以使用预定义的枚举器来检查响应。即使使用Glade定义了库存按钮,我们仍必须定义(用户定义的)响应ID,以便gtk_dialog_run函数理解用户已选择响应。
使用以下命令进行编译:
gcc -o dialog main.c `pkg-config --cflags --libs gtk+-3.0`

应该得到类似于这样的结果:

enter image description here


Jose,非常感谢您的详细回复,但是这并没有回答原始问题。请查看对原始问题的编辑以获得澄清。谢谢! - Leigh K
@LeighK 这个例子可以应用于你的目标。这里的基本步骤是通过GtkBuilder从Glade XML定义中检索自动实例化的小部件。GtkBuilder函数get_object允许您按名称获取glade小部件。因此,在您的回调中,您可以传递builder对象或对话框。稍后我会尝试补充我的答案。 - José Fonte
Jose,我刚刚运行了你的例子,似乎你是正确的。然而,仍有一些不同。首先,我习惯在main函数中引用GtkBuilder。你没有在main函数中引用它,而是在“on_button_clicked”信号处理程序中引用(然后取消引用)。我想我可以在main函数和我感兴趣编写的信号处理程序中都引用它对吧? - Leigh K
最大的问题是你的“on_button_clicked”信号处理程序被提供了一个指向主函数中创建的GtkButton的指针。在我的情况下,它需要链接到GtkImageMenuItem“gtk-about”。我不确定应该如何完成这个任务。除此之外,我认为你编写的信号处理程序可以胜任这项工作。 - Leigh K
2
@LeighK 这很简单。你只需要理解“Gtk”框架即可。愿意加入Gtk聊天室,以便我们可以尝试帮助您,全面了解问题并详细阐述答案。聊天室链接:https://chat.stackoverflow.com/rooms/160557/gtk - José Fonte
显示剩余4条评论

2

经过与@Jose Fonte的讨论,我的问题的解决方案如下:

// Signal Handler for Clicking File -> Quit
void file_quit_clicked (__attribute__((unused)) GtkMenuItem *item, gpointer user_data) 
{
    GtkBuilder *builder = (GtkBuilder *) user_data;

    GtkDialog *dialog = GTK_DIALOG(gtk_builder_get_object(builder, "quit_dialog"));

    gtk_widget_show_all(GTK_WIDGET(dialog));

    // This switch statement and gtk_widget_hide is needed so that the dialog window can be closed 
    // and reopened again without causing errors (i.e. if the user clicks "no" when asked "are you
    // sure you want to quit." Without these, the window errors out the second time opened.
    gint result = gtk_dialog_run (dialog);
    switch (result)
    {
        case GTK_RESPONSE_ACCEPT:
           // do_application_specific_something (); - Nothing Required
           break;
        default:
           // do_nothing_since_dialog_was_cancelled (); - Nothing Required
           break;
    }
    gtk_widget_hide(GTK_WIDGET(dialog));

}

通过将以下代码片段插入主函数中:

    // So the "Are you sure you want to quit?" Dialog Box is hidden after clicking "x"
    GtkDialog *quit_dialog = GTK_DIALOG(gtk_builder_get_object(builder, "quit_dialog"));
    gtk_widget_hide_on_delete (GTK_WIDGET(quit_dialog));

1
gint result = gtk_dialog_run (GTK_DIALOG (dialog)); 不需要 GTK_DIALOG() 宏,因为它已经被定义为 GtkDialog。我会编辑答案以反映这一点。 - José Fonte
2
不错的交易,Jose。非常感谢你在解决方案方面提供的所有帮助! - Leigh K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接