Home  Contents

Menus and Toolbars in GTK+

في هذه الجزء من دورة ال GTK+  , سوف نعمل علي القوائم وشرائط الادوات .

شريط القائمه هو احد الاجزاء العامه في تطبيق ذو واجهة رسوميه . حيث انها مجموعه من الاوامر تقع في شرائط قوائم مختلفه . بينما في برنامج الكونسول يجب عليك ان تتذكر هذه الاوامر الغامضه , هنا لدينا معظم الاوامر مجمعه في اجزاء منطقيه.  



Simple menu example

في مقالنا الاول , سوف نقوم بانشاء شريط قائمه جديد يحتوي علي قائمة file   واحده . سوف تحتوي القائمه علي عنصر واحد . ينغلق البرنامج بمجرد اختيار هذه العنصر من القائمه .

#include <gtk/gtk.h>


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

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *filemenu;
  GtkWidget *file;
  GtkWidget *quit;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "menu");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  filemenu = gtk_menu_new();

  file = gtk_menu_item_new_with_label("File");
  quit = gtk_menu_item_new_with_label("Quit");

  gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(quit), "activate",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

انشاء شريط القوائم مربك قليلا. لابد ان نضع في اذهاننا ان كل من شريط القوائم والقوائم نفسها منشقين من نفس ال widget , مسمي menu shell  . عناصر القوائم صالحة فقط لكي تكون child  للقوائم . ويتم استخدامها ايضا لكي نقوم بعمل قوائم فرعيه

 menubar = gtk_menu_bar_new();
 filemenu = gtk_menu_new();

في هذه الشفره نقوم بانشاء شريط قوائم جديد ونقوم بانشاء قائمه ايضا .

 gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);

في هذه الشفره نقوم بتنفيذ قائمة file menu  . المنطق في هذه الامر هو ان ال menubar  هي menu shell  . وايضا file menu  يعد menu shell . لهذا ننظر علي ال file menu علي انها submenu  او subshell .

 gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
 gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);

عنصار القائمه تطبق بواسطة استدعاء الداله  gtk_menu_shell_append() . عناصر القائمه تربط بال menu shells  . في حالتنا , العنصر "quit menu"  يتم ربطه بقائمة file   "file menu " وايضا ال "file menu" يتم ربطها بشريط القائمه "menu bar "  .

  g_signal_connect(G_OBJECT(quit), "activate",
        G_CALLBACK(gtk_main_quit), NULL);

بواسطة اختيار عنصر القائمه , نخرج من البرنامج ويتم غلقه.


Simple menu

Figure: Simple menu

Image menus, mnemonics & accelerators

في المثال التالي , سوف نتقدم في اكتشاف الوظائف والخصائص التي نستطيع استخدامها في GTK+  .  اختصارات القوائم التي تدعي (Accelerators )  هي اختصارات من لوحة المفاتيح لتفعيل عنصر من قائمه. بينما اختصارات الاحداث والتي تدعي (Mnemonics) فهي اختصارات من لوحة المفاتيح ايضا ولكن للعناصر الخاصه بالوجهه الرسوميه . وهم مقدمين من المستويات المنخفضه .

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>


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

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *filemenu;
  GtkWidget *file;
  GtkWidget *new;
  GtkWidget *open;
  GtkWidget *quit;

  GtkWidget *sep;

  GtkAccelGroup *accel_group = NULL;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "menu");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  filemenu = gtk_menu_new();

  accel_group = gtk_accel_group_new();
  gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);

  file = gtk_menu_item_new_with_mnemonic("_File");
  new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
  open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
  sep = gtk_separator_menu_item_new();
  quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);

  gtk_widget_add_accelerator(quit, "activate", accel_group, 
      GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 


  gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), new);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), open);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), sep);
  gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
      G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(quit), "activate",
      G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

هذه المثال يوضح كيف تقوم باضافة صوره لعنصر من القائمه . وكيف تقوم بانشاء اختصار للقوائم واختصار للعناصر الرسوميه في تطبيق GTK+  .

  accel_group = gtk_accel_group_new();
  gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
  ...
  quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel_group);
  gtk_widget_add_accelerator(quit, "activate", accel_group, 
      GDK_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); 

مجموعة الاختصارات هي عباره عن مجموعه من اختصارات في لوحة المفاتيح ,  نموذجيا مربوطين بنافذه من النوع toplevel . هنا قمنا بانشاء اختصار لقائمه من المفاتيح Ctrl+q .



 file = gtk_menu_item_new_with_mnemonic("_File");

كي نقوم بانشاء اختصار لعناصر رسوميه , نقوم باستدعاء الداله gtk_menu_item_new_with_mnemonic() . ثم نقوم  بتحديد ال file menu  بواسطة ضغط Alt + F  .

 new = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
 open = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);

هنا نقوم بانشاء صورتين لعنصرين في قائمه . بوساطة ضبط ال parameter الثاني للداله ب NULL  , نقوم تلقائيا بعمل اختصار قائمي . ونقدم ايضا صورة واسم للعنصر من المصادر الداخليه ل GTK+  .

 sep = gtk_separator_menu_item_new();

عناصر القائمه يمكن ان تفصل عن بعضها بواسطة فاصل افقي . بهذه الطريقه يمكن ان نضع عناصر القائمه في مجموعات منطقيه توضح هدف واحد . واضح من الصوره .


Menu example

Figure: Menu example

Check menu item

GtkCheckMenuItem  هو عنصر قائمه بمربع لوضع علامة (Check box)  .

#include <gtk/gtk.h>


void toggle_statusbar(GtkWidget *widget, gpointer statusbar) 
{
  if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
    gtk_widget_show(statusbar);
  } else {
    gtk_widget_hide(statusbar);
  }
}


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

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *menubar;
  GtkWidget *viewmenu;
  GtkWidget *view;
  GtkWidget *tog_stat;
  GtkWidget *statusbar;
  

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "view statusbar");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  menubar = gtk_menu_bar_new();
  viewmenu = gtk_menu_new();

  view = gtk_menu_item_new_with_label("View");
  tog_stat = gtk_check_menu_item_new_with_label("View Statusbar");
  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(tog_stat), TRUE);

  gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), viewmenu);
  gtk_menu_shell_append(GTK_MENU_SHELL(viewmenu), tog_stat);
  gtk_menu_shell_append(GTK_MENU_SHELL(menubar), view);
  gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);

  statusbar = gtk_statusbar_new();
  gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect(G_OBJECT(tog_stat), "activate", 
        G_CALLBACK(toggle_statusbar), statusbar);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

في المثال السابق عرضنا عنصر قائمه علامي (Check menu item) . اذا كان الصندوق مفعل , يتم اظهار شريط الحالة , اذا لم يكن مفعل فان شريط الحاله يختفي .

 tog_stat = gtk_check_menu_item_new_with_label("View Statusbar");

الداله gtk_check_menu_item_new_with_label()  استدعائها ينشئ عنصر قائمه علامي جديد .

 if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
   gtk_widget_show(statusbar);
 } else {
   gtk_widget_hide(statusbar);
 }

اذا كان الصندوق العلامي مفعل , نقوم باظهار شريط الحاله . غير ذلك فأن شريط الحاله يختفي .


Check menu item

Figure: Check menu item

A toolbar

القوائم تقوم بتجميع الاوامر التي يمكننا استخدامها في تطبيقنا . اما شريط الادوات فهو يوفر حصول سريع علي معظم الاوامر التي نستخدمها بشكل متكرر .

#include <gtk/gtk.h>


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

  GtkWidget *window;
  GtkWidget *vbox;
  
  GtkWidget *toolbar;
  GtkToolItem *new;
  GtkToolItem *open;
  GtkToolItem *save;
  GtkToolItem *sep;
  GtkToolItem *exit;


  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "toolbar");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);


  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

  gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

  new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);

  open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), open, -1);

  save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), save, -1);

  sep = gtk_separator_tool_item_new();
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1); 

  exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

  g_signal_connect(G_OBJECT(exit), "clicked", 
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

هذا المثال يقوم بانشاء شريط ادوات بسيط.

  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS)

الان نقوم بانشاء شريط ادوات جديد . نقوم بالتحديد ان الزرائر الموجوده في شريط الادوات تظهر فقط صور وليس كتابات.

 new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new, -1);

نقوم بعمل زرار لشريط الادوات من صوره موجوده في المخزن . زرائر شريط الادوات يتم اضافتها للشريط بواسطة استدعاء الداله gtk_toolbar_insert()  .

 sep = gtk_separator_tool_item_new();
 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1); 

هنا نقوم بوضع فاصل في شريط الادوات , كما هو واضح من الصوره .


Toolbar

Figure: Toolbar

Undo redo

المثال الاتي يشرح , كيف يمكننا ان تعطيل زرائر موجوده في شريط الادوات . هذا اسلوب مستخدم بكثره في برمجة الواجهات الرسوميه . كمثال زرار الحفظ , اذا قمنا بحفظ تغيرات علي مستند لدينا , فان زرار الحفظ سوف يتم تعطيله . بهذه الطريقه يبلغ التطبيق للمستخدم ان كل التغيرات قد تم حفظها بالفعل .

#include <gtk/gtk.h>
#include <string.h>


void undo_redo(GtkWidget *widget,  gpointer item) 
{
  static int count = 2;
  const char *name = gtk_widget_get_name(widget);

  if ( strcmp(name, "undo") ) {
    count++;
  } else {
    count--;
  }
 
  if (count < 0) {
     gtk_widget_set_sensitive(widget, FALSE);
     gtk_widget_set_sensitive(item, TRUE);
  } 

  if (count > 5) {
     gtk_widget_set_sensitive(widget, FALSE);
     gtk_widget_set_sensitive(item, TRUE);
  }
}


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

  GtkWidget *window;
  GtkWidget *vbox;

  GtkWidget *toolbar;
  GtkToolItem *undo;
  GtkToolItem *redo;
  GtkToolItem *sep;
  GtkToolItem *exit;


  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
  gtk_window_set_title(GTK_WINDOW(window), "undoredo");

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);


  toolbar = gtk_toolbar_new();
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

  gtk_container_set_border_width(GTK_CONTAINER(toolbar), 2);

  undo = gtk_tool_button_new_from_stock(GTK_STOCK_UNDO);
  gtk_widget_set_name(GTK_WIDGET(undo), "undo");
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), undo, -1);

  redo = gtk_tool_button_new_from_stock(GTK_STOCK_REDO);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), redo, -1);

  sep = gtk_separator_tool_item_new();
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1); 

  exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);


  g_signal_connect(G_OBJECT(undo), "clicked", 
        G_CALLBACK(undo_redo), redo);

  g_signal_connect(G_OBJECT(redo), "clicked", 
        G_CALLBACK(undo_redo), undo);

  g_signal_connect(G_OBJECT(exit), "clicked", 
        G_CALLBACK(gtk_main_quit), NULL);

  g_signal_connect_swapped(G_OBJECT(window), "destroy",
        G_CALLBACK(gtk_main_quit), NULL);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

مثالنا يقوم بإنشاء زرارين اعد / الغي الفعل undo/redo  من المصادر المخزنه في GTK+  (stock resources )  . بعد عدة ضغطات علي الزرار سوف يتم تعطيله . ويتغير لونها الي اللون الرمادي .

 if (count < 0) {
    gtk_widget_set_sensitive(widget, FALSE);
    gtk_widget_set_sensitive(item, TRUE);
 } 

 if (count > 5) {
    gtk_widget_set_sensitive(widget, FALSE);
    gtk_widget_set_sensitive(item, TRUE);
 }

الدالة gtk_widget_set_sensitive()  يتم استخدامها لتفعيل/تعطيل زرائر موجوده في شريط الادوات .


Undo redo

Figure: Undo redo



Home ‡ Contents ‡ Top of Page