مكتبة GTK وبرنامج glade | كتاب لينكس الشامل | >> |
تعتبر مكتبة gtk أي Gimp tool kit من أقوى المكتبات لعمل برامج ذات واجهة رسومية وتفاعلية. نعم هي التي صمم gimp و كل برامج غنوم عليها وهي سهلة ومتوفرة لأكثر من نظام تشغيل منها لينكس و MacOS و ويندوز ولكن اصدار لينكس هو الأشهر والأنشط وهو الإصدار المركزي لها ، أما اصدار ويندوز فهو أقل نشاطاً وتوفراً ودعماً لكنه موجود. وبدأً من الإصدار الثاني كانت هذه المكتبة تدعم الكثير من اللغات ومنها اللغة العربية وتوفر الكثير من المزايا التي لا ينافسها فيها إلا مكتبة QT الخاصة ب KDE والفرق الأهم بين gtk و QT هو الرخصة فالأولى مرخصة وفقاً ل lgpl أي يمكنك استعمالها في عمل برامج تجارية أو مجانية أما QT فهي مجانية إذا عملت برامج مفتوحة و حرة وتجارية وعليك شراء رخصة إذا عملت برامج تجارية.
تتوفر مكتبة gtk في أكثر من لغة أهمها C/C++
وهناك perl و python و php
بالنسبة ل php حتى الإصدار الرابع توفر gtk1 التي لا تدعم العربية
وعليك الحصول على php 5 التي توفر gtk2 ويمكن الحصول عليها على كل
CVS وتركيبها من المصدر
تجد المزيد من التفاصيل على موقع
www.gtk.org/bindings
سنشرح كل شيء بلغة سي ثم نتحدث عن اللغات الأخرى
ولا داعي أن أذكر أن perl و غيرها أسهل مليون مرة من لغة سي
لذا يمكنك أن تبدأ بلغة perl أو python
أو غيرهما ، أعرف أن لغة سي تشكل صدمة للجدد ولكنها اللغة
المركزية ومنها يحول إلى اللغات الأخرى.
ومكتبة gtk هي تركيب من أكثر من مكتبة فهي بنيت فوق gdk أي gimp drawing kit للتعامل مع الرسم ومع مكتبة pango بدءاً من الإصدار الثاني ل gtk من أجل العالمية ودعم اللغات المختلفة مثل لغتنا العربية لذا عليك التعامل مع تلك المكتبات بين الحين والآخر
المرجع الأساسي يأتي مع حزمة تطوير gtk ويتم تركيبه في مجلد
/usr/share/gtk-doc/
حيث تجد مجلد باسم html يحتوي مجلدات فرعية
عن gtk وغيرها
وهو مكان يحتوي على معلومات مفصلة تفصيلاً مملاً
يكون مزعجا في البداية لكثرة التفصيل والمعلومات الدقيقة والمتشابكة
ولكنه سيكون المكان الأفضل كمرجع ،
أهم صفحة فيه هي Object Hierarchy التي توضح الخريطة الوراثية لكائنات gtk!
في البداية ربما تحب أن تلقي نظرة على
tutotial الموجود على موقع www.gtk.org/tutorial
وهناك الكثير من الوثائق على موقع
http://developer.gnome.org/doc
من أهم الوثائق التي تهمنا كعرب وهي
http://developer.gnome.org/dotplan/proting
التي تتحدث عن كيفية تحويل البرامج من gtk1 التي لا تدعم العربية إلى gtk2 التي تدعم العربية
ويمكنك تشغيل برنامج gtk-demo
قد تعتبر هذا غريباً بعض الشيء ولكن يمكنك تعلم gtk من ملفات headers أو من الملف المصدري ل gtk وطبعا هذا هو المكان الآخير ، ويمكن تعلمها من قراءة الملفات المصدرية للكثير من البرامج المفتوحة ومن الملفات المولدة بواسطة glade
يمكنك استعمال برنامج مثل glade و glade2 لتوليد البرنامج وتصميمه باللغة التي تريد ،أي أنهما يكتبان البرنامج عنك ، ويقومان بترتيبه بحث يتم استعمال أدوات مثل auto-config ويتم وضع ملفات المصدر في مجلد src والملف الوحيد الذي عليك التعديل فيه هو ملف ال callbacks في حالة لغة C هو callbacks.c الذي يحتوي على وظائف تتبع الأحداث للتفاعل مع المستخدم ولتصنيف البرنامج كل ما عليك هو تنفيذ النص البرمجي autogen.sh (على ما أذكر!!) وأكثر من ذلك يمكنك تحميل ملف ال glade وتشغيله مباشرة دون أن تصنفه مثلاً هذا الكود بلغة السي يقوم بتحميل ملف اسمه filename.glade بعد تصنيف البرنامج (انظر الطريقة أدناه) قم بتصميم ملف على glade وخزنه باسم filename.glade ثم شغل برنامجنا الآن أغلقه وعدل التصميم ثم شغله مرة أخرى وانظر النتيجة
#include <gtk/gtk.h> #include <glade/glade.h> void some_handler(GtkWidget *widget) { /* a handler referenced by the glade file. Must not be static * so that it appears in the global symbol table. */ } int main(int argc, char **argv) { GladeXML *xml; GtkWidget *widget; gtk_init(&argc, &argv); xml = glade_xml_new("filename.glade", NULL, NULL); /* get a widget (useful if you want to change something) */ widget = glade_xml_get_widget(xml, "widgetname"); /* connect signal handlers */ glade_xml_signal_autoconnect(xml); gtk_main(); return 0; }
ولكن تصميم برنامج مباشرة ليس صعباً ولن تكون بحاجة لتحديد حجم وموقع كل زر و كل جسم بالبيكسل ورسمها ، في مكتبة gtk نستخدم نظام تكديس packing يقوم على وضع الأجسام في حاويات وترتيبها وكأنها في جدول ونحدد الحد الأدنى لحجمها إذا أردنا ونحدد فيما إذا كنا نريد أن تكبر وتصغر عند تحجيمها أم تبقى كما هي ويتغير الهامش وهكذا نكون حصلنا على التصميم الذي نريد
لتصنيف برنامج مكتوب على gtk عليك تحديد أسماء المجلدات التي تحتوي ملفات headers ذات الإمتداد h وأيضاً ربط برنامجك بمكتبة gtk أي ملفات ذات الإمتداد a اذا كنت تستعمل gcc فإليك الخيارات التي يجب أن تمررها له لمكتبة gtk2
-I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/pango-1.0 -I/usr/X11R6/include -I/usr/include/freetype2 -I/usr/include/atk-1.0
-L/usr/lib -L/usr/X11R6/lib -lgtk-x11-2.0 -lgdk-x11-2.0 -lXi -lgdk_pixbuf-2.0 -lm -lpangox -lpangoxft -lXft -lXrender -lXext -lX11 -lfreetype -lpango -latk -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0
ويمكن أن تختلف المجلدات من توزيعة لأخرى وأيضا تختلف من اصدار gtk إلى آخر مثلاً في gtk1 هناك القليل من المكتبات الإضافية فهي لا تعتمد على pango وغيرها مما يجعل تصنيف البرمج مزعجاً ، ولكن لا تظن أنك يجب أن تعرف شيء عن هذه الطلاسم كل ما عليك هو استدعاء الأداة pkg-config وأخذ ناتج تنفيذها ، التي حلت مكان أداة gtk-config القديمة التي كانت تستعمل في gtk1
# the old method الطريقة القديمة bash$ gcc myfile.c -o myfile `gtk-config --cflags` `gtk-config --libs` # the new method الطريقة الحديثة bash$ gcc myfile.c -o myfile `pkg-config --cflags --libs gtk+-2.0`
برنامج gtk العادي يبدو مثل هذا
/* البداية التقليدية */ #include <gtk/gtk.h> int main(int argc, char *argv[]) { /* نعرف متغيرات تمثل النافذة والأزرار */ GtkWidget *win, *btn; /* نقوم بالعمليات الإستهلالية */ gtk_init(&argc, &argv); /* نبدأ برسم النوافذ والأزرار */ /* نحجز نافذة جديدة */ win = gtk_window_new(GTK_WINDOW_TOPLEVEL); /* نحجز زر جديد */ btn = gtk_button_new_with_label("Hello World !!"); /* نضع الزر في النافذة */ gtk_container_add (GTK_CONTAINER (win), btn); /* نحدد حجم النافذة الإبتدائي */ gtk_window_set_default_size(GTK_WINDOW(win), 200, 200); /* نحدد وظائف التفاعل وتتبع الأحداث */ /* مثلاُ نربط حدث إغلاق النافذة بالخروج */ g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(gtk_main_quit), NULL); /* نظهر النافذة ومحتوياتها */ gtk_widget_show_all(win); /* النهاية التقليدية */ /* تبقي البرنامج عاملاً وتتبع الأحداث إلى أن يتم إغلاق البرنامج */ gtk_main(); return 0; }
هذا البرنامج عبارة عن نافذة تحتوي زر اسمه btn مكتوب عليه Hello World
عند النقر على زر الإغلاق من مدير النوافذ يتم استدعاء الوظيفة
gtk_main_quit الموجودة ضمن مكتبة gtk والتي تقوم بإغلاق البرنامج
والخروج ، أما الزر btn فإن النقر عليه لا يقوم بشيء
لربط وظيفة معينة به
g_signal_connect(G_OBJECT(win), "clicked", G_CALLBACK(gtk_main_quit), NULL);
ويمكن وضع أي وظيفة للقيام بها كرد على ضغط الزر
وتكون صيغتها أنها تأخذ المعامل الأول هو مؤشر على الزر نفسه
ويكون من نوع GtkWidget*
والأخير من نوع gpointer *
حيث يكون مؤشراً على بيانات اضافية
يتم تمريرها له بدلاً من NULL المعامل الأخير في g_signal_connect
ويمكن أن يأخذ معاملات أخرى بينهما قد تمثل مكان حدوث النقر أو النص الذي أضيف
... هذا يعتمد على صنف الكائن وتجدها مفصلة في مرجع gtk
الذي تحدثنا عنه.
في الإصدارات السابقة من gtk ماقبل gtk2 استعمل gtk_signal_connect مكان g_signal_connect كما في المثالين
gtk_signal_connect(GTK_OBJECT(win),"destroy", GTK_SIGNAL_FUNC(gtk_main_quit),NULL); gtk_signal_connect(GTK_OBJECT(win),"clicked", win->destroy(),NULL);
إذا كنت تريد استدعاء التحديث أثناء قيامك بعمليات معقدة وتأخذ وقتاً حتى لا يبدو برنامج متوقف عن الإستجابة ضع هذا السطر وسط تلك الحسابات
/* ... */ while(gtk_events_pendings()) gtk_main_iteration(); /* ... */
أسماء الوظائف في gtk هي عبارة عن سلسلة
تفصل بين مقاطعها شرطة تحتية "_" تبدأ باسم المكتبة مثل gtk و gdk ... إلخ
ثم يأتي الصنف -إن وجد- مثل button أو window ثم الوظيفة
مثل new فتصبح مثلاً gtk_button_new
وأول المعاملات التي يأخذها يكون في الغالب مؤشر على
الكائن الذي نريد تطبيق الوظيفة عليه
مثلاً gtk_window_set_title((GtkWindow*)win,"The brand new title");
لاحظ أن وظائف حجز كائن جديد تعيد مؤشراً لكائن من نوع GtkWidget
وتأخذ معظم وظائف تعديل خصائصها
هذا النوع من المعاملات أي مؤشر إلى نوع GtkWidget أو
نوع الكائن الذي تعود له هذه الصفة كما لاحظنا
gtk_window_set_title تأخذ مؤشر إلى GtkWindow .
لقد كانت وظائف الكائنات في الإصدارة الأولى تأخذ
معاملاتها من نفس نوعها مثل GtkButton للأزرار وليس من نوع GtkWidget
مما يوجب عليك القيام دائماً بالتحويل إما بالطريقة التقليدية
(GtkButton*)btn1
أو بالإختصار/الوظيفة المناسبة
GTK_BUTTON(btn1)
ولكن في الإصدار الثانية من Gtk فقط عليك التحويل
في حالات نادرة لها علاقة بالنوافذ والحاويات
أما أغلب الوظائف تأخذ GtkWidget. على أي حال إذا لم تقم بالتحويل فإن
أكثر ما يمكن أن تحصل عليه هو تنبيه warning ولا يشكل خطأ.
أما أنواع المتغيرات وأسماء الصنوف فهي تكون بأحرف استهلالية كبيرة ودون فاصل ، اسم المكتبة ثم النوع مثل GtkButton وكل تعاملاتنا تكون بمؤشرات ، أضف إلى ذلك ما قلناه عن أننا في الغالب سنستعمل فقط GtkWidget الثوابت والإختصارات تكون بأحرف كبيرة ويفصل بين مقاطعها "_" مثل GTK_WINDOW_TOPLEVEL
بعد حجز نافذة يمكنك تحديد حجمها
gtk_window_set_default_size(GTK_WINDOW(win), 600, 400);
وعنوانها
gtk_window_set_title(GTK_WINDOW(win),"My Title");
ثم نقسم النافذة إلى
صفوف ونضع في الصف الأول القوائم
ثم في الآخر سطر الأدوات
والذي يليه ربما نقسمه إلى عدة أعمدة
نضع فيها مثلاً شريطاً جانبياً
قابلاً للتحجيم مع مساحة للنص
وصف جديد يحتوي بعض الأزرار وسطر آخر يحتوي
سطر الحالة
إذا كنت تفكر لتصميم
برنامجك بهذه الطريقة فتطبيق ذلك لا يحتاج
أي احداثيات وطلاسم كل ما عليك هو استعمال
GtkVBox و GtkHBox
حيث V و H ترمز للعامودي والأفقي على الترتيب
vbox1=gtk_vbox_new(FALSE,3);
تقوم بعمل صندوق لوضع الصفوف فيه بحيث لا تكون هذه
الصناديق متجانسة ، إذا وضعنا TRUE مكان FALSE يصبح متجانس
أي كل الصفوف بنفس الحجم
والرقم ثلاثة يشير إلى عرض الخط الفاصل.
بعد ذلك نضيفه إلى النافذة
gtk_container_add(GTK_CONTIANER(win),vbox1);
ثم نبدأ بإضافة الكائنات (الصفوف)
في مثالنا نريد سطر القوائم جديد
menubar1 = gtk_menu_bar_new();
ثم نضيفه على شكل صف في الصندوق
gtk_box_pack_start(GTK_BOX(vbox1),menubar1, FALSE, FALSE,0);
حيث ال FALSE هي أننا لا نريده أن يكبر عند تكبير النافذة
والثانية أنه عند تصغيره يتم إخفاء جزء منه وليس تصغير حجمه على الترتيب.
أما الصفر فهي الهامش.
الوظيفة gtk_box_pack_start تضيف الكائنات من البداية إلى النهاية
في مثالنا من فوق لتحت إذا أردت أن تعكس العملية استعمل
gtk_box_pack_end
بنفس المعاملات. ويمكنك ايضاً اضافتها باستعمال
gtk_box_pack_start_defaults(GTK_BOX(vbox1),menubar1);
أو حتى الطريقة الأولى لإضافة العناصر وهي
gtk_container_add(GTK_BOX(vbox1),menubar1);
ولأنهما تأخذان معاملين فقط فإنك لن تستطيع تحديد طريقة التكديس
وسيتم استخدام التلقائية.
لعمل قائمة ملف مثلاً نحجزه هكذا
filemenu=gtk_menu_item_new_with_label("File");
ثم نضيفه إلى سطر القوائم بالطريقة المعتادة
gtk_container_add(GTK_CONTIANER(menubar1),filemenu);
لنضيف خيار جديد إلى قائمة ملف
نحجر
newmenu=gtk_menu_item_new_with_label("new");
ثم نضيف
gtk_container_add(GTK_CONTIANER(filemenu),newmenu);
ولنفرض أننا نريد قائمة جديد أن تتفرع إلى قائمة بها
اختياران مثلاً نافذة جديدة و ملف جديد
نحجزها ب
newwinmenu=gtk_menu_item_new_with_label("new window");
و
newfilemenu=gtk_menu_item_new_with_label("new file");
ونضيفها لقائمة "جديد" ب
gtk_container_add(GTK_CONTIANER(newmenu),newwinmenu);
و
gtk_container_add(GTK_CONTIANER(newmenu),newfilemenu);
في الصف التالي نريد إضافة عمودين
الثاني مكان لتحرير النص والأول فارغ ربما للاستخدامات
المستقبلية سنضع فيه نص ثابت مكتوب عليه قريباً soon
نقوم بحجز صندوق يحتويها ب
hbox1=gtk_hbox_new(FALSE,3);
ونضيفه إلى الصفوف
gtk_box_pack_start(GTK_BOX(vbox1),hbox1, TRUE, FALSE,0);
الآن لكي نضيف العامود الأول وهو عبارة عن نص من سطر واحد
نحجزه أولاً
entry1=gtk_entry_new();
ثم نضيفه
gtk_box_pack_start(GTK_BOX(hbox1),entry1, TRUE, FALSE,0);
ونكتب فيه كلمة soon
gtk_entry_set_text("soon!");
ولأننا لا نريد أن تكون هذه للإدخال بل نص ثابت
gtk_editable_set_editable(entry1,FALSE);
ثم نضيف العامود الثاني الذي يحتوي على مكان لتحرير نص
متعدد الأسطر
text1=gtk_text_view_new();
ثم اضافته بعد حجزه
gtk_box_pack_start(GTK_BOX(hbox1),text1, TRUE, FALSE,0);
وهو ليس لعرض النص كما قد يخطر ببالك فقط
بل يمكن استخدامه لتحرير النص
وذلك بتغير الخاصية "editble" إلى TRUE
وذلك باستدعاء
gtk_text_view_set_editable(text1,TRUE);
وأيضاً يمكن استخدامه ليس فقط لتحرير النصوص العادية بل وأيضاً
التي تحتوي على ألوان وخصائص مختلفة
بل وأيضا صور مدرجة وروابط و... الكثير من الأشياء التي
يمكنك أن تفكر فيها.
ولأن منطقة تحرير النص هي الكائن الأساسي نقوم بإعطائها حجم كبير نسبياً
بل ونجعله الحد الأدنى
للحجم (أي يمكن أن يزيد عنه ولكن لا يجوز أن يقل عنه ) وذلك
باستدعاء
gtk_widget_set_size_request(text1,400,400);
ولا تستخدم الطريقة القديمة
gtk_widget_set_usize(text1,400,400);
فهي موجودة فقط للتوافق مع الإصدارات القديمة فقط.
نتابع إضافة سطر (صندوق أفقي) يحتوي بعض الأزرار
hbox2=gtk_hbox_new(TRUE,3);
نحجز زرين بـ
btn1=gtk_button_new_with_label("OK");
و
btn2=gtk_button_new_with_label("Clear");
ثم نضيفهما بـ
gtk_box_pack_start(GTK_BOX(hbox2),btn1, FALSE, FALSE,0);
و
gtk_box_pack_start(GTK_BOX(hbox2),btn2, FALSE, FALSE,0);
وفي النهاية نضيف الصندوق الكبير الذي يحتوي كل شيء vbox1 إلى النافذة
gtk_container_add(GTK_CONTIANER(win),vbox1);
ونظهر النافذة وما فيها
gtk_widget_show_all(win);
يفترض أن تحدد أنك تريد اظهار الكائن ب
gtk_widget_show(my_widget_name);
لكل الكائنات في النافذة ولكن هذا سيكون مملاً الأجدى من ذلك أن تستعمل
gtk_widget_show_all(my_window_name);
لإظهار النافذة وكل شيء داخلها
سنحصل من البرنامج على ما نريد غير أن
الحاجز الذي يفصل بين منطقة تحرير النص وصندوق soon لا يمكن تحريكه
فإذا أردناه أن يتحرك نستبدل الصندوق الأفقي hbox1 ب
hpaned
وذلك باستعمال
hpaned1=gtk_hpaned_new();
وهو كائن له ولدين يفصل بينهما حاجز متحرك يمكن جره
ويكون ذلك بوضع الكود التالي
/* ... */ hpaned=gtk_hpaned_new(); gtk_box_pack_start(GTK_BOX(vbox1),hpaned1, TRUE, FALSE,0); gtk_container_set_border_width(GTK_CONTAINER(hpaned1),3); entry1=gtk_entry_new(); gtk_paned_pack1(GTK_PANED(hpaned1),entry1, TRUE,TRUE); gtk_entry_set_text("soon!"); gtk_editable_set_editable(entry1,FALSE); text1=gtk_text_view_new(); gtk_paned_pack2(GTK_PANED(hpaned1),text1, TRUE,TRUE); gtk_text_view_set_editable(text1,TRUE); /* ... */
/* ... */ hbox1=gtk_hbox_new(FALSE,3); gtk_box_pack_start(GTK_BOX(vbox1),hbox1, TRUE, FALSE,0); entry1=gtk_entry_new(); gtk_box_pack_start(GTK_BOX(hbox1),entry1, TRUE, FALSE,0); gtk_entry_set_text("soon!"); gtk_editable_set_editable(entry1,FALSE); text1=gtk_text_view_new(); gtk_box_pack_start(GTK_BOX(hbox1),text1, TRUE, FALSE,0); gtk_text_view_set_editable(text1,TRUE); /* ... */
gtk_paned_pack1
لإضافة الإين الأول و
gtk_paned_pack2
الأول سيظهر على اليسار والثاني على اليمين
إذا كانت اللغة الإنجليزية هي لغة النظام
أما إذا كانت العربية فيكون الأول على اليمين .
بدء من الإصدار الثاني ل gtk فهي تدعم العربية بشكل ممتاز بسبب pango لهذا ليس عليك أن تقلق أو تغير في حساباتك اكتب البرنامج وكأنه إنجليزية وعند اختيار العربية سترى برنامجك وقد أصبحت قوائمه من اليمين وتغير ترتيب الأشياء لتصبح من اليمين لليسار لا داعي لإعادة تصميم البرنامج. وأكثر من ذلك فهي تدعم اللغات التي تكتب من فوق إلى تحت ومن اليمين إلى اليسار و التي تكتب من فوق إلى تحت ومن اليسار إلى اليمين
g_object_set_property
ومعرفة قيمتها بالوظيفة
g_object_get_property
هاتان الوظيفتان تأخذان 3 معاملات هي مؤشر للكائن من نوع GObject
وسلسلة نصية اسم الخاصية مثلاً "editable" أو "font"
وأخيراً مؤشر من نوع GValue وهو يمثل القيمة.
/* I'm not sure if it works or correct */ GValue v; g_value_init(&v,G_TYPE_STRING); g_value_set_instance(&v,"monospace 10"); g_object_set_property((GObject*)entry1,"font",&v);
أول تطبيق سنصممه هو عبارة عن برنامج بسيط يأخذ رقمين ويجمعهما شكل البرنامج عبارة عن ثلاث حقول لإدخال رقمي يقابلها عناوينها التي هي First Number و Second Number و Sum موضوعة في ثلاث صفوف بشكل عامودي فوق بعضها البعض ، يأتي تحتها زر OK. إليكم نص البرنامج:
/* البداية التقليدية */ #include <stdio.h> /* من أجل sprintf */ #include <stdlib.h> /* للمستقبل */ #include <math.h> /* للمستقبل */ #include <gtk/gtk.h> /* نعرف متغيرات تمثل النافذة والأزرار */ GtkWidget *win, *btn1; GtkWidget *vbox1,*hbox1,*hbox2,*hbox3,*hbox4; GtkWidget *label1,*label2,*label3; GtkWidget *entry1,*entry2,*entry3; /* نخبر سي عن نموذج الوظيفة */ void calc_result(GtkButton *btn,gpointer *ptr); int main(int argc, char *argv[]) { gtk_init(&argc, &argv); /* نحجز الكائنات */ win = gtk_window_new(GTK_WINDOW_TOPLEVEL); vbox1 = gtk_vbox_new(TRUE,3); hbox1 = gtk_hbox_new(FALSE,3); hbox2 = gtk_hbox_new(FALSE,3); hbox3 = gtk_hbox_new(FALSE,3); hbox4 = gtk_hbox_new(FALSE,3); label1 = gtk_label_new("First number:"); label2 = gtk_label_new("Second number:"); label3 = gtk_label_new("Sum :"); entry1 = gtk_entry_new(); entry2 = gtk_entry_new(); entry3 = gtk_entry_new(); /* الثالث يمكن أن يكون label */ btn1 = gtk_button_new_from_stock(GTK_STOCK_OK); /* نضع كل واحد في مكانه */ gtk_container_add (GTK_CONTAINER(win), vbox1); gtk_container_add (GTK_CONTAINER(vbox1), hbox1); gtk_container_add (GTK_CONTAINER(vbox1), hbox2); gtk_container_add (GTK_CONTAINER(vbox1), hbox3); gtk_container_add (GTK_CONTAINER(vbox1), hbox4); gtk_container_add (GTK_CONTAINER(hbox1), label1); gtk_container_add (GTK_CONTAINER(hbox1), entry1); gtk_container_add (GTK_CONTAINER(hbox2), label2); gtk_container_add (GTK_CONTAINER(hbox2), entry2); gtk_container_add (GTK_CONTAINER(hbox3), label3); gtk_container_add (GTK_CONTAINER(hbox3), entry3); gtk_container_add (GTK_CONTAINER(hbox4), btn1); /* نحدد بعض خصائص الكائنات */ gtk_editable_set_editable(GTK_EDITABLE(entry1),TRUE); gtk_editable_set_editable(GTK_EDITABLE(entry2),TRUE); gtk_editable_set_editable(GTK_EDITABLE(entry3),FALSE); /* نحدد وظائف التفاعل وتتبع الأحداث */ g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(G_OBJECT(btn1), "clicked", G_CALLBACK(calc_result), NULL); /* نظهر النافذة ومحتوياتها */ gtk_widget_show_all(win); /* النهاية التقليدية */ gtk_main(); return 0; } /* الوظيفة التي ينفذها عند نقر OK */ void calc_result(GtkButton *btn,gpointer *ptr) { const char *str1,*str2; char str3[40]; float n1,n2,r; /* هنا نأخذ السلسلة النصية التي تمثل الرقم */ str1=gtk_entry_get_text(GTK_ENTRY(entry1)); str2=gtk_entry_get_text(GTK_ENTRY(entry2)); /* هنا نحول من سلسلة نصية إلى رقم نسبي */ n1=atof(str1); n2=atof(str2); r=n1+n2;/* هنا نحسب */ sprintf(str3,"%g",r); /*نحول إلى سلسلة نصية*/ /* يمكن استخدام ftoa(r); * ولكني أفضل sprintf * لأنني أتحكم في الصيغة */ /* نكتبها في خانة النتيجة */ gtk_entry_set_text(GTK_ENTRY(entry3),str3); return TRUE; }
نص البرنامج واضح ولا يحتاج إلى الكثير من الشرح، حجزنا
صندوق عامودي ووضعنا فيه كل شيء
حجزنا أربع صناديق أفقية ووضعنا في أول ثلاث
عنوان ونص وفي الرابع وضعنا زر
وعلى عكس ما هو متوقع لم أستخدم gtk_button_new_with_label("OK");
لعمل زر موافق بل استخدمت
gtk_button_new_from_stock(GTK_STOCK_OK);
لأن هناك زر جاهز (وليس تفصيل) في مخازن Gtk2
وهي محلات عريقة! تحتوي على الكثير من الأزرار
والقوائم الجاهزة والتي تملك صور معبرة وأيضا مترجمة لعدة لغات
منها العربية (أي أنك توفر وقت الترجمة ووقت تصميم الأيقونة).
وهناك الكثير منها يمكنك أن تراها بتنفيذ gtk-demo
هناك ستجد قائمة طويلة بها وهي أيضا موجودة في المرجع.
ويمكن استخدام طريقة مماثلة لعمل القوائم مثلاً
gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN,NULL);
الجزء الذي يمكن أن تراه أكثر تعقيداً هو calc_result
الذي يحتوي على الكثير من الغموض.
لاحظ أنني لم أحجز str1
و str2
والسبب أن gtk_entry_get_text
تقوم بحجز المساحة ويجب أن لا تقوم بتحريرها أو التعديل عليها
ولهذا كانت ثابتة const
وهذه قاعدة عامة
وظيفة gtk التي تعيد متغير ثابت const
يجب أن لا تحرره ب g_free(my_ptr);
كما يفترض أن لا تكون قد حجزته وأعطيته قيمة
بل تتركه NULL حتى تضع الوظيفة فيه القيمة المناسبة
لأن هذه القيمة قد تكون داخلية.
أما الوظائف التي تعيد مؤشرات ليست ثابتة const
فإنه يجوز لك تحريرها متى شئت باستعمال
g_free(my_ptr);
من الأمثلة عليها gtk_something_new();
str3
فحجزت له 40 بايت
- وأظنها كافية -
لأنه يمرر ل sprintf التي لا تحجز ذاكرة.
أما إذا كنت تتسائل عن سبب أخذي للمتغيرات
على شكل سلسلة نصية ثم تحويلها لرقم ثم جمعها
ثم إعادتها على كل سلسلة مرة أخرى
وذلك لأن قيمة GtkEntry عبارة عن سلسلة نصية
لكي نقوم بالحسابات علينا أن نحولها لرقم
وبعد أن نقوم بالحسابات
ولكي نظهر النتيجة في entry3 يجب أن تكون نص
فنحولها مرة أخرى إلى نص
البرنامج التالي عبارة عن ساعة ايقاف أي نافذة بثلاث أزرار واحدة لبدء العد والآخر لإيقاف العد والأخير لتصفير الساعة. يتم إضهار الثواني في صندوق نصي GtkEntry لايسمح بالكتابة فيه.
/* * stopwatch2.c: A stopwatch with Gtk+2 * * by Moayyad Al-Sadi<alsadi[at]gmail.com> * released under the terms of the GNU General Public License * visit www.fsf.org/www.gnu.org */ #include <glib.h> #include <gtk/gtk.h> /* Public variables */ GTimer *timer1; GtkWidget *win, *vbox1,*hbox1; GtkWidget *label1; GtkWidget *timer, *stopbtn,*startbtn,*resetbtn; gchar str[100]; double oldt=-1.0,newt=0.0; /* idle function to update timer */ gint update_timer(gpointer ptr) { g_ascii_formatd(str,99,"%29.3f",newt=g_timer_elapsed(timer1,NULL)); if (oldt!=newt) gtk_entry_set_text(GTK_ENTRY (timer),str); oldt=newt; } /* handlers for click the 3 buttons */ void start_event() { g_timer_start(timer1); update_timer(NULL); } void stop_event() { g_timer_stop(timer1); update_timer(NULL); } void reset_event() { g_timer_reset(timer1); g_timer_stop(timer1); update_timer(NULL); } int main(int argc, char *argv[]) { /* create the timer and put it on 0.0 */ timer1=g_timer_new(); g_timer_reset(timer1); g_timer_stop(timer1); gtk_init(&argc, &argv); /* Build the gui */ win = gtk_window_new(GTK_WINDOW_TOPLEVEL); vbox1 = gtk_vbox_new(FALSE,3); /* homo,spacing */ label1=gtk_label_new("Moayyad al-Sadi Stopwatch"); timer=gtk_entry_new(); hbox1 = gtk_hbox_new(TRUE,3); startbtn=gtk_button_new_with_label("start"); stopbtn=gtk_button_new_with_label("stop"); resetbtn=gtk_button_new_with_label("reset"); /* add each widget to it parent */ gtk_container_add(GTK_CONTAINER (win),vbox1); gtk_box_pack_start(GTK_BOX(vbox1),label1, FALSE, FALSE,0); /* resize=shrink=false */ gtk_box_pack_start(GTK_BOX(vbox1),timer, FALSE, FALSE,0); gtk_box_pack_start(GTK_BOX(vbox1),hbox1, TRUE, FALSE,0); gtk_box_pack_start(GTK_BOX(hbox1),startbtn, TRUE, FALSE,0); gtk_box_pack_start(GTK_BOX(hbox1),stopbtn, TRUE, FALSE,0); gtk_box_pack_start(GTK_BOX(hbox1),resetbtn, TRUE, FALSE,0); /* set some properties */ /* gtk_window_set_default_size(GTK_WINDOW(win), 300, 300); */ gtk_window_set_title(GTK_WINDOW(win),"Moayyad Stopwatch"); gtk_editable_set_editable(GTK_EDITABLE(timer),FALSE); /* connect events */ g_signal_connect(G_OBJECT(win),"destroy",gtk_main_quit,NULL); g_signal_connect(G_OBJECT(startbtn),"clicked", start_event,NULL); g_signal_connect(G_OBJECT(stopbtn),"clicked", stop_event,NULL); g_signal_connect(G_OBJECT(resetbtn),"clicked", reset_event,NULL); gtk_idle_add(update_timer,NULL); /* show all and wait to exit */ gtk_widget_show_all(win); gtk_main(); return 0; }
لتصنيف البرنامج في Gtk1 يجب تعديل بعض الأسطر كما يلي
/* ... */ /* in main(...) */ gtk_signal_connect(GTK_OBJECT(win),"destroy", GTK_SIGNAL_FUNC(gtk_main_quit),NULL); gtk_signal_connect(GTK_OBJECT(startbtn),"clicked", GTK_SIGNAL_FUNC(start_event),NULL); gtk_signal_connect(GTK_OBJECT(stopbtn),"clicked", GTK_SIGNAL_FUNC(stop_event),NULL); gtk_signal_connect(GTK_OBJECT(resetbtn),"clicked", GTK_SIGNAL_FUNC(reset_event),NULL); /* ... */
لاحظ استعمال الوظيفة gtk_idle_add
والتي تعمل على استدعاء الوظيفة التي تحددها له
بشكل متكرر عندما لا يقوم البرنامج بعمل أي شيء
(أي أن يكون في وضع التوقف بانتظار حدث معين)
تعيد هذه الوظيفة رقم يمكن تمريره إلى gtk_idle_remove
لإيقاف تنفيذ تلك الوظيفة.
وظيفة idle هذه تنفذ بشكل متكرر في فترات
غير محددة في المقابل توجد الوظيفة
gtk_timeout_add
التي تمكنك من تحديد الفترات الزمنية بين استدعاءات
المتكررة للوظيفة التي تحددها.
يمكن تركيب حجم النافذة التي ستظهر باستعمال
gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
ليبدو البرنامج منطقياً فلا يمكن تغير حجم النافذة.
تحتوي gtk على الكثير من صناديق الحوار الجاهزة منها صندوق حوار الرسالة ، إذا كنت تريد أن تظهر رسالة بسرعة ودون الكثير من العناء يمكنك ذلك باستعمال GtkMessageDialog فهو ببساطة printf هذه مثال يوضح العملية
/* ... */ GtkWidget *dialog; dialog=gtk_message_dialog_new( win1,GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE, "ERROR: you can't devide %d by zero",i ); gtk_run_dialog( GTK_DIALOG(dialog) ); gtk_widget_destroy(dialog); /* ... */
gtk_message_dialog_new(WINDOW,FLAGS,
MESSAGE_TYPE,BUTTONS,
"a message or a format,just like printf",...);
حيث WINDOW
هي النافذة التي يتبع لها يمكن أن تكون NULL ،
FLAGS
هي خصائص الصندوق وهي موضحة في الجدول أدناه،
MESSAGE_TYPE
هي نوع الرسالة
وهي تحدد الأيقونة التي ترافق الرسالة
الأنواع هي كما في الجدول أدناه ،
أما BUTTONS
فهي تحدد الأزرار الموجودة
في مثالنا زر إغلاق
واطقم الأزرار موضحة في الجدول أدناه
خيارات صندوق الحوار FLAGS | |
---|---|
GTK_DIALOG_MODAL | فوق النافذة الأب ويجمدها |
GTK_DIALOG_DESTROY_WITH_PARENT | يغلق النافذة الأب عند إغلاقه |
GTK_DIALOG_NO_SEPARATOR | لإزالة الخط الفصل بين المنطقة العلوية ومنطقة الأزرار |
نوع الرسالة MESSAGE_TYPE | |
---|---|
GTK_MESSAGE_INFO | معلومات |
GTK_MESSAGE_WARNING | تحذير |
GTK_MESSAGE_QUESTION | سؤال |
GTK_MESSAGE_ERROR | خطأ |
أطقم الأزرار BUTTONS | |
---|---|
GTK_BUTTONS_NONE | دون أي زر |
GTK_BUTTONS_OK | زر موافق |
GTK_BUTTONS_CLOSE | مع زر إغلاق |
GTK_BUTTONS_CANCEL | مع زر إلغاء |
GTK_BUTTONS_YES_NO | مع زرين هما "نعم" و"لا" |
GTK_BUTTONS_OK_CANCEL | مع زرين هما "موافق" و"إلغاء" |
كل صناديق الحوار يمكنك أن تضيف عليها
أزرار كما تريد، ربما تفضل ذلك مع GTK_BUTTONS_NONE
الذي يكون بلا أزرار ، يمكن اضافته كما في المثال
gtk_dialog_add_button(GTK_DIALOG(dialog),"Click me",1);
حيث Click me
هو النص الموجود على الزر
و الرقم هو أي رقم (موجب لما تضيفه أنت وسالب للمسجل في gtk ) ليمكنك أن تستعمله لتعرف أي زر تم الضغط عليه
ويمكنك أن تستعمل مخازن gtk التي تحتوي على الكثير من الأزرار الجاهزرة
مثلاً gtk_dialog_add_button(GTK_DIALOG(dialog),GTK_STOCK_APPLY,GTK_RESPONSE_APPLY);
ويمكنك اضافتها بطريقة أخرى نتحدث عنها لاحقاً.
بعد حجز الصندوق يمكن عرضه على أنه نافذة عادية أو ببساطة يمكن استدعاء
gtk_run_dialog( GTK_DIALOG(dialog) );
هنا تحول هذه الوظيفة الصندوق إلى modal وتعرضه ثم تنتظر
أن تستجيب له وتعيد رقم معيناً يمثل الزر الذي تم ضغطه ويمكن أن يكون من الجدول
أدناه ،ويمكن أن يكون من الأزرار التي أضفتها
القيم التي يعيدها gtk_run_dialog | |
---|---|
GTK_RESPONSE_OK | عند اختيار "موافق" |
GTK_RESPONSE_CANCEL | عند اختيار "إلغاء" |
GTK_RESPONSE_CLOSE | عند اختيار "إغلاق" |
GTK_RESPONSE_YES | عند اختيار "نعم" |
GTK_RESPONSE_NO | عند اختيار "لا" |
GTK_RESPONSE_APPLY | عند اختيار "تطبيق" |
GTK_RESPONSE_HELP | عند اختيار "مساعدة" |
لعمل صندوق حوار عام يمكن استخدام GtkDialog واضافة الكائنات فيه أو يمكن استعمال الصناديق الجاهزة منها GtkMessageDialog الذي تحدثثنا عنه ومنها أيضا GtkColorSelectionDialog و GtkFileSelection و GtkFontSelectionDialog. كل ما تحدثنا عنه في GtkMessageDialog ينطبق على باقي صناديق الحوار في gtk
في أي صندوق حوار فإنه يقسم لمنطقتين
الأولى هي vbox والثانية action_area تحتوي هذه الأخيرة الأزرار
لنفرض أن لديك صندوق حوار اسمه dialog يمكنك أن تصل إليهما بالطريقة التالية
(GtkDialog *)dialog->vbox
و (GtkDialog *)dialog->action_area
أو بالإختصار GTK_DIALOG(dialog)->vbox
و
GTK_DIALOG(dialog)->action_area
مثلاً لإضافة GtkEntry *entry1
إلى
المنطقة العلوية استعمل
gtk_container_add( (GtkContainer *)((GtkDialog *)dialog->vbox),entry1);
أو بكلمات أخرى
gtk_container_add(Gtk_CONTAINER(GTK_DIALOG(dialog)->vbox),entry1);
هناك الكثير من الكائنات يمكنها أن تقوم بمعظم الأشيء التي قد تخطر ببالك
مثلاً GtkImage التي تعرض الصور والرسوم المتحركة
بمختلف أنواعها ،ببساطة استعمل image1=gtk_new_image_from_file("path/to/image.png");
ثم ضعها في النافذة وأظهرها.
لدينا GtkCalendar لعرض التقويم ،
ولدينا GtkCombo و GtkOptionMenu لعرض قائمة إختيارات منسدلة
تظهر عند نقر سهم موجود عليها ، وكما لدينا الزر العادي GtkButton
لدينا زر GtkToggleButton وهو زر متقلب بين وضعين
ولدينا GtkCheckButton الذي يعرض مربع تنقر عليه فيضع صح (يمكن أن يكون أي شيء آخر حسب المؤثر المستخدم)
وهناك GtkRadioButton وهو عبارة عن زر يمكنك من اختيار واحد من مجموعة
منها بحيث يكون واحد مضاء فقط ، يمكن أن تحديد المجموعة ب GtkVBox أو GtkHBox . و يمكنك أن نستخدم GtkFrame لتحيط بهم
لإعطاء شكل أنيق لهم ، ونستعمل GtkSpinButton وهو عبارة عن مكان لإدخال رقم
مع سهمين متعادين لزيادة الرقم وانقاصه.
يمكن استخدام GtkHandleBox ثم وضع حاوية بها مثل GtkToolBar لعمل
جزء طافٍ من النافذة يمكن تحريكه.
نستخدم GtkHSeparator أو GtkVSeparator لرسم خط يفصل بين الكائنات.
ونستخدم GtkProgressBar لتبيان سير العملية على شكل نسبة مئوية
أو مجرد حركة.
لديك مسطرة عامودية وأفقية GtkVRuler و GtkHRuler.
ولإختيار رقم بجر صندوق دحرجة GtkVScrollbar و GtkHScrollbar
أو بتحريك مربع التدرج GtkHScale و GtkVScale .
بل وحتى تحديد منحى مثل الموجود في gimp باستعمال GtkCurve.
لعمل قائمة list أو شجرة tree نستعمل GtkTreeView
لأن القائمة هي شجرة دون أفرع .
ويمكن تلقي ومعالجة أحداث من كائنات لا تملك
أحداث بوضعها داخل GtkEventBox
هناك المزيد من الكائنات ويمكنك أن تعمل كائنات خاصة بك أو استعمال كائنات عملها غيرك مثل GtkOpenGlArea
<< السابق | كتاب لينكس الشامل | التالي >> |