2013-07-31 80 views
3

我注意到Glade只允許您設置要在GTK回調的用戶數據部分內傳遞的對象。GTK將用戶數據傳遞給使用Glade的回調

有沒有什麼辦法可以傳遞一個整數值呢?

我有一組菜單項,我想指向相同的回調函數,但是對於我需要確定哪個菜單項是調用回調函數的代碼的一小段代碼。

說明: 我所有的信號都是用glade_xml_signal_autoconnect自動設置的,我會盡量保持這種方式。

回答

4

在我看來,關於林間空地和回傳,人們必須告別將一個元素傳遞給回調的概念。因此,請停止使用glade以配置傳遞給特定回調的用戶數據。

我習慣於將一個名爲App的結構傳遞給所有的回調函數,這些回調函數都包含一個指針,指向從UI定義文件加載並已由GtkBuilder實例化的每個UI元素。這也停止了在小部件之間點擊來設置回調的用戶數據的glade的繁瑣任務。除了ui元素之外,大多數情況下,此結構還包含在應用程序級別運行時非常重要的其他元素。

這種方法的一個好處是,你不會想到如何實現一個單獨的回調,該回調應該對多於一個元素進行操作,而這往往是這種情況。有些人通過將感興趣的小部件分組到一個容器中來解決這個問題。爲了將所有小部件傳遞給回調函數,只需傳遞容器。然後在回調中,他們通過調用gtk_container_get_children或類似函數來獲取小部件。這種方法使得回調難以辨認,並且在編輯代碼時減少了樂趣。

如果每個回調都有可用的所有元素,應該在運行時操作,您不必關心實現單個回調,因爲每個回調共享相同的結構。

此外,我創建了一個輔助宏,它定義了一個指向已經實例化的元素的指針,它的glade id的名字。通過這種方式,代碼中的元素定義始終與顯示在空地中的元素定義保持同步。這使得小部件的重命名變得非常簡單(用所有來源中的名稱替換glade文件)。

爲了說明這種方法,我附加了一個示例程序的文件。不要害怕文件數量。將程序劃分爲邏輯單元/模塊使編程變得更簡單。爲了獲得整個項目的快速查看我創建一個Git倉庫在GitHub上:

https://github.com/o8i12398z12h9h/gtk-sample-app

只是比較我callbacks.c與你的回調文件(S)。我很想知道他們在可讀性和結構方面的比較,並考慮到您可能仍然記得glade的元素ID。


callbacks.c

#include "app.h" 

void 
button1_clicked_cb (GtkButton * button, App * app) 
{ 
    GET_UI_ELEMENT (GtkEntry, entry1); 

    if (gtk_entry_get_text_length (entry1) == 0) 
     gtk_entry_set_text (entry1, "test"); 
    else 
     gtk_entry_set_text (entry1, ""); 
} 

void 
button2_clicked_cb (GtkButton * button, App * app) 
{ 
    gboolean active; 

    GET_UI_ELEMENT (GtkSpinner, spinner1); 
    GET_UI_ELEMENT (GtkWidget, eventbox1); 

    g_object_get (G_OBJECT (spinner1), "active", &active, 
        NULL); 

    if (active) { 
     gtk_spinner_stop (spinner1); 
     gtk_widget_override_background_color (eventbox1, 
               GTK_STATE_FLAG_NORMAL, 
               app-> 
               active_color); 
    } 
    else { 
     gtk_spinner_start (spinner1); 
     gtk_widget_override_background_color (eventbox1, 
               GTK_STATE_FLAG_NORMAL, 
               app-> 
               inactive_color); 
    } 
} 

void 
button3_clicked_cb (GtkButton * button, App * app) 
{ 
    GdkRGBA bg = { 0, 0, 1, 1 }; 

    GET_UI_ELEMENT (GtkWidget, eventbox1); 

    gtk_widget_override_background_color (eventbox1, 
              GTK_STATE_FLAG_NORMAL, 
              &bg); 
} 

void 
button4_clicked_cb (GtkButton * button, App * app) 
{ 
    const gchar *str; 

    GET_UI_ELEMENT (GtkLabel, label1); 

    str = gtk_label_get_text (label1); 

    if (strcmp (str, "label") == 0) { 
     gtk_label_set_text (label1, "NewText"); 
    } 
    else { 
     gtk_label_set_text (label1, "label"); 
    } 
} 

void 
button5_clicked_cb (GtkButton * button, App * app) 
{ 
    GET_UI_ELEMENT (GtkWidget, button1); 
    GET_UI_ELEMENT (GtkWidget, button2); 
    GET_UI_ELEMENT (GtkWidget, button4); 

    g_signal_emit_by_name (button1, "clicked", app); 
    g_signal_emit_by_name (button2, "clicked", app); 
    g_signal_emit_by_name (button4, "clicked", app); 
} 

main.c

#include "app.h" 

int 
main (int argc, char *argv[]) 
{ 
    App *app; 

    app = (App *) g_new (App, 1); 

    gtk_init (&argc, &argv); 

    app_init (app); 

    GET_UI_ELEMENT (GtkWidget, window1); 

    gtk_widget_show_all (window1); 

    gtk_main(); 

    return 0; 
} 

app.c

#include "app.h" 

GObject * 
app_get_ui_element (App * app, const gchar * name) 
{ 
    const gchar *s; 
    GSList *list; 

    list = app->objects; 

    do { 
     s = gtk_buildable_get_name (list->data); 

     if (strcmp (s, name) == 0) { 
      return list->data; 
     } 

    } while (list = g_slist_next (list)); 

    return NULL; 
} 

void 
app_init_colors (App * app) 
{ 
    GdkRGBA active_color = { 1, 0, 0, 1 }; 
    GdkRGBA inactive_color = { 0, 1, 0, 1 }; 

    app->active_color = g_new0 (GdkRGBA, 1); 
    app->inactive_color = g_new0 (GdkRGBA, 1); 

    app->active_color = gdk_rgba_copy (&active_color); 
    app->inactive_color = gdk_rgba_copy (&inactive_color); 
} 


void 
app_init (App * app) 
{ 
    GError *err = NULL; 

    app->definitions = gtk_builder_new(); 

    gtk_builder_add_from_file (app->definitions, 
           UI_DEFINITIONS_FILE, &err); 

    if (err != NULL) { 
     g_printerr 
      ("Error while loading app definitions file: %s\n", 
      err->message); 
     g_error_free (err); 
     gtk_main_quit(); 
    } 

    gtk_builder_connect_signals (app->definitions, app); 

    app->objects = gtk_builder_get_objects (app->definitions); 

    app_init_colors (app); 
} 

app.h

#ifndef __APP__ 
#define __APP__ 

#include <gtk/gtk.h> 

#define UI_DEFINITIONS_FILE "ui.glade" 

#define GET_UI_ELEMENT(TYPE, ELEMENT) TYPE *ELEMENT = (TYPE *) \ 
              app_get_ui_element(app, #ELEMENT); 

typedef struct app_ 
{ 
    GtkBuilder *definitions; 
    GSList *objects; 

    GdkRGBA *active_color; 
    GdkRGBA *inactive_color; 

} App; 

void app_init (App *); 
GObject * app_get_ui_element (App * , const gchar *); 

#endif 

ui.glade

<?xml version="1.0" encoding="UTF-8"?> 
<interface> 
    <!-- interface-requires gtk+ 3.0 --> 
    <object class="GtkWindow" id="window1"> 
    <property name="can_focus">False</property> 
    <property name="border_width">20</property> 
    <signal name="destroy" handler="gtk_main_quit" swapped="no"/> 
    <child> 
     <object class="GtkGrid" id="grid1"> 
     <property name="visible">True</property> 
     <property name="can_focus">False</property> 
     <property name="row_spacing">10</property> 
     <property name="column_spacing">20</property> 
     <child> 
      <object class="GtkSpinner" id="spinner1"> 
      <property name="visible">True</property> 
      <property name="can_focus">False</property> 
      </object> 
      <packing> 
      <property name="left_attach">1</property> 
      <property name="top_attach">1</property> 
      <property name="width">2</property> 
      <property name="height">1</property> 
      </packing> 
     </child> 
     <child> 
      <object class="GtkEventBox" id="eventbox1"> 
      <property name="height_request">50</property> 
      <property name="visible">True</property> 
      <property name="can_focus">False</property> 
      <child> 
       <placeholder/> 
      </child> 
      </object> 
      <packing> 
      <property name="left_attach">1</property> 
      <property name="top_attach">2</property> 
      <property name="width">2</property> 
      <property name="height">1</property> 
      </packing> 
     </child> 
     <child> 
      <object class="GtkEntry" id="entry1"> 
      <property name="visible">True</property> 
      <property name="can_focus">True</property> 
      <property name="invisible_char">•</property> 
      <property name="invisible_char_set">True</property> 
      </object> 
      <packing> 
      <property name="left_attach">1</property> 
      <property name="top_attach">0</property> 
      <property name="width">2</property> 
      <property name="height">1</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">label</property> 
      </object> 
      <packing> 
      <property name="left_attach">1</property> 
      <property name="top_attach">3</property> 
      <property name="width">2</property> 
      <property name="height">1</property> 
      </packing> 
     </child> 
     <child> 
      <object class="GtkButtonBox" id="buttonbox1"> 
      <property name="visible">True</property> 
      <property name="can_focus">False</property> 
      <property name="spacing">10</property> 
      <property name="layout_style">center</property> 
      <child> 
       <object class="GtkButton" id="button1"> 
       <property name="label" translatable="yes">toggle entry</property> 
       <property name="use_action_appearance">False</property> 
       <property name="visible">True</property> 
       <property name="can_focus">True</property> 
       <property name="receives_default">True</property> 
       <property name="use_action_appearance">False</property> 
       <signal name="clicked" handler="button1_clicked_cb" swapped="no"/> 
       </object> 
       <packing> 
       <property name="expand">False</property> 
       <property name="fill">True</property> 
       <property name="position">0</property> 
       </packing> 
      </child> 
      <child> 
       <object class="GtkButton" id="button2"> 
       <property name="label" translatable="yes">toggle spinner + bg</property> 
       <property name="use_action_appearance">False</property> 
       <property name="visible">True</property> 
       <property name="can_focus">True</property> 
       <property name="receives_default">True</property> 
       <property name="use_action_appearance">False</property> 
       <signal name="clicked" handler="button2_clicked_cb" swapped="no"/> 
       </object> 
       <packing> 
       <property name="expand">False</property> 
       <property name="fill">True</property> 
       <property name="position">1</property> 
       </packing> 
      </child> 
      <child> 
       <object class="GtkButton" id="button3"> 
       <property name="label" translatable="yes">set bg</property> 
       <property name="use_action_appearance">False</property> 
       <property name="visible">True</property> 
       <property name="can_focus">True</property> 
       <property name="receives_default">True</property> 
       <property name="use_action_appearance">False</property> 
       <signal name="clicked" handler="button3_clicked_cb" swapped="no"/> 
       </object> 
       <packing> 
       <property name="expand">False</property> 
       <property name="fill">True</property> 
       <property name="position">2</property> 
       </packing> 
      </child> 
      <child> 
       <object class="GtkButton" id="button4"> 
       <property name="label" translatable="yes">toggle label</property> 
       <property name="use_action_appearance">False</property> 
       <property name="visible">True</property> 
       <property name="can_focus">True</property> 
       <property name="receives_default">True</property> 
       <property name="use_action_appearance">False</property> 
       <signal name="clicked" handler="button4_clicked_cb" swapped="no"/> 
       </object> 
       <packing> 
       <property name="expand">False</property> 
       <property name="fill">True</property> 
       <property name="position">3</property> 
       </packing> 
      </child> 
      <child> 
       <object class="GtkButton" id="button5"> 
       <property name="label" translatable="yes">toggle everything</property> 
       <property name="use_action_appearance">False</property> 
       <property name="visible">True</property> 
       <property name="can_focus">True</property> 
       <property name="receives_default">True</property> 
       <property name="use_action_appearance">False</property> 
       <signal name="clicked" handler="button5_clicked_cb" swapped="no"/> 
       </object> 
       <packing> 
       <property name="expand">False</property> 
       <property name="fill">True</property> 
       <property name="position">4</property> 
       </packing> 
      </child> 
      </object> 
      <packing> 
      <property name="left_attach">0</property> 
      <property name="top_attach">5</property> 
      <property name="width">4</property> 
      <property name="height">1</property> 
      </packing> 
     </child> 
     <child> 
      <object class="GtkSeparator" id="separator1"> 
      <property name="visible">True</property> 
      <property name="can_focus">False</property> 
      </object> 
      <packing> 
      <property name="left_attach">0</property> 
      <property name="top_attach">4</property> 
      <property name="width">4</property> 
      <property name="height">1</property> 
      </packing> 
     </child> 
     <child> 
      <placeholder/> 
     </child> 
     <child> 
      <placeholder/> 
     </child> 
     <child> 
      <placeholder/> 
     </child> 
     <child> 
      <placeholder/> 
     </child> 
     <child> 
      <placeholder/> 
     </child> 
     <child> 
      <placeholder/> 
     </child> 
     <child> 
      <placeholder/> 
     </child> 
     <child> 
      <placeholder/> 
     </child> 
     </object> 
    </child> 
    </object> 
</interface> 
+1

感謝您對這樣一個完整的答案。我甚至沒有考慮過這樣做,正如你所說的,與你的相比,我的callback.c文件是一團糟。我已經下載了你提供的代碼,我將用它作爲編寫一些更乾淨的代碼和重新思考我的回調的基礎。再次,非常感謝。 – Scott

+0

雖然你的答案非常複雜,但我看不到它比簡單的'gtk_builder_connect_signals()'更好。 – TechZilla