بناء التطبيقات مع Pythonبالعتماد على GTKو تأليف :محمد قاسم حسين
SQLite
الفهرس إهداء ....................................................................................................................................................ص 4
مقدمه ...................................................................................................................................................ص 5
. 1الفصل الول :مدخل إلى .............................................. GTKص 7
.1.1مقدمه .........................................................................................................................................ص 8
.1.1.1ما هي GTK؟ .................................................................................................................................ص 8
.1.1.2نظره سريعه على ماضي ......................................................................................................... GTKص 8 .1.1.3بعض الصور لتطبيقات تعمل على ......................................................................................... GTKص 9
.1.2قبل البدأ .................................................................................................................................ص 11 .1.3المثال الول :مرحباً بالعالم ..................................................................................................ص 12 .1.4الدوات .............................................................................................................. Widgetص 13
.1.4.1ما هي الدوات ............................................................................................................ Widgetص 13
.1.4.2الدوات مع الـ ............................................................................................................. PyGTKص 13
.1.4.2.1البدايه .......................................................................................................................................................ص 13
.1.4.2.2الصناديق ................................................................................................................................................ص 17 .1.4.2.2.1مقدمه ................................................................................................................................................................ص 17 HBox.1.4.2.2.2و ........................................................................................................................................... Vboxص 17 .1.4.2.2.3إستخدام الصناديق عملياً .................................................................................................................................ص 18
2
.1.5الشارات و الدوال ...................................................................................... Callbackص 21
.1.5.1مقدمه .............................................................................................................................................ص 21 .1.5.2الشارات مع .............................................................................................................. PyGTKص 21
.1.6المثال الثاني :برنامج تحويل درجة الحراره ..........................................................................ص 26 .1.7المثال الثالث :آله حاسبه ....................................................................................................ص 32 .1.8مدخل إلى .......................................................................................................... Gladeص 39 .1.8.1مقدمه ....................................................................................................................................ص 39
.1.8.2التصميم مع ................................................................................................................... Gladeص 40 .1.8.3استخدام ما تم تصميمه مع Gladeفي النص البرمجي ..............................................................ص 44
.1.8.4استخدام الشارات مع ما تم تصميمه مع ...................................................................... Gladeص 47 .1.8.5نقل برنامج تحويل درجة الحراره إلى ............................................................................. Gladeص 52 .1.8.6نقل الله الحاسبه إلى ................................................................................................... Gladeص 57
. 2الفصل الثاني :مدخل إلى .................................... SQLiteص 63
.2.1مقدمه .....................................................................................................................................ص 64 .2.2الدوال الساسيه ..................................................................................................................ص 65 .2.3المثال الول :برنامج لتخزين السماء و عرضها .....................................................................ص 67
. 3الفصل الثالث :إنشاء مشروع ...............................................ص 81
.3.1برنامج إدارة بيانات موظفين في شركه ما ................................................................................ص 82
ملحق أ :وصلت .........................................................................................................................ص 101
قائمة المصادر ...............................................................................................................................ص 103 3
إهداء إلى والدّي. إلى اخواني حسين شبلي ،عبدال العصمي و مازن مليباري و إلى من علّمني الكثير عبدال المهيري.
4
مقدمه بسم ال الرحمن الرحيم ،و الصلة و السلم على اشرف المرسلين سيدنا و مولنا ابا القاسم محمد و على آله الطيبين الطاهرين و رضوان ال على الصحابة المنتجبين اما بعد.
منذ فتره و انا افكر بكتاب ينتفع به من يريد التعلم ،و بدأت اقلّب بالمواضيع حتى وصلت إلى لغة البايثون ،دققت النظر في المجتمع العربي على النترنت فوجدت أنّ هناكَ كتابان باللغةِ العربية يتحدثان عن هذه اللغه للمبتدئين،
فقررت أنْ ل اكتب عن نفس الموضوع الذي سبقوني به ،لنه إن حصل ذلك لن يحققَ الكتابُ هدفه ،و في
الحقيقه مللنا من كثرة الكتب التي تتحدث عن الساسيات فقط خصوصاً بالبرمجه ،فما اكثر الكتب التي تشرح
اساسيات العديد من لغات البرمجه متغاضية عن المواضيع المتقدمه فيها ،لذا و بعد كل هذا قررت التحدث عن لغة البايثون و لكن في مواضيع متقدمه فيها ،فأخترت الكتابه عن GTKو SQLiteمع البايثون ،حتى يتمكن
من ينتهي من قراءه هذا الكتاب بناء تطبيقات متكامله ذات واجهات رسوميه بالعتماد على قواعد البيانات من اجل ادارة البيانات.
بالنسبه للمصطلحات ،فحاولت ترجمتها بقدر المكان مع البقاء على المعنى الصحيح و ان كان هذا المعنى ينافي معنى الكلمه حرفياً ،و ارجو أنْ ل يلومني البعض على ترجمتي لهذه المصطلحات كما حدث لي من قبل ،(-: كما إنّي وضحتُ مع كل مصطلح قمتُ بترجمته الكلمه الصليه باللغه النجليزيه ،حتى اسهّل على من اراد التبحّر و البحث اكثر و اقرّب الصوره لمن لم يتقبل ترجمتي للمصطلح.
هناك نقطه هامه جداً ل بد من ذكرها ،و هي أنّ هذا الكتاب يتطلب معرفة سابقه في لغة بايثون ،لن يعلمَك
الكتاب ما هي البرمجه او ما هي بايثون و كيفية التعامل معها ،من الفضل مراجعة الكتب المتخصصه للمبتدئين في هذا المجال ،كذلك يفترض هذا الكتاب أنّ لديك إلمام في لغةِ SQLمن اجل التعامل مع قواعد البيانات.
هذا الكتاب مجاني حُر يقع تحت رخصة ،GNU FDLو تم استخدام البرامج الحرّه من اجل انجازه - 5
محرر LeafPadلتحريره ،برنامج WikkaWikiلتخزينه و التعامل معه اثناء كتابته ،برنامج
OpenOffice Writerمن اجل تنسيقه و وضعه في صورته النهائيه من اجل نشره ،الخطوط المُقدمه من
مشروع Arabeyesو Proggy Fontsلتنسيقه ،و كلها تعمل تحت مظلّة نظام التشغيل
GNU/Linuxتوزيعة ،OpenSuSEاسمحولي برفع القبعه لمجتمع المصادر الحرّه .(-:
لي سؤال حول الكتاب ،او حول احد مواضيع الكتاب و شيفراته ،او تصحيح مهما كان نوعه يمكنك التوجه إلى مدونتي على العنوان التالي http://www.maastaar.comثم اذهب إلى صفحة "راسلني" و
اكتب استفسارك او اقتراحك او تصحيحك و سوف احاول الرد بأسرع وقت ممكن ان شاء ال ،يسعدني أنْ
تراسلوني بأسئله حول مواضيع الكتاب لنها حتماً ستزيد من خبرتي و ستساعدني في الطبعه الثانيه من هذا الكتاب الذي افكّر بوضع ملحق لسئله القُراء و اجوبتها ان شاء ال .(-:
جميع الشيفرات البرمجيه في هذا الكتاب مُجرّبه على الصدار 2.6من بايثون ،و جميعها تخضع للرخصه الحرّه .GNU LGPL
للحصول على الخطوط المُستخدمه في الكتاب راجع العنوان التالي اما الخط المُستخدم في الشيفرات البرمجيه فراجع العنوان التالي :
/http://wiki.arabeyes.org :خطوط /http://www.proggyfonts.com
هذا و نرجوا من ال سبحانه و تعالى ان يتقبل منّا هذا العمل البسيط و الحمدل رب العالمين.
6
الفصل الول مدخل إلى GTK
7
.1.1مقدمه .1.1.1ما هي GTK؟ GTKعباره عن مكتبه برمجيه لتصميم واجهات مستخدم رسوميه ،GUIفبدلً من بناء برامجنا على شكل
سطر اوامر يمكننا بناءه على الشكل الحديث للبرامج عن طريق واجهة مستخدم رسوميه ،بمعنى انه يمكن بناء
واجهات البرامج ووضع الزرار و مربعات النص و غيرها من المور مع ما يناسب برنامجنا ،كلمة GTK
اختصار للجمله ،GIMP Toolkitتقع هذه المكتبة تحت رخصة GNU LGPLو تتميز بكفاءتها مع
اللغه العربيه ،تتوفر مكتبة GTKمع مجموعه كبيره من لغات البرمجه مثل سي و سي + +و جافا و PHPو
بالطبع بايثون و التي سوف نتعامل مع الـ GTKمن خللها ،(-:تُسمى المكتبه التي تدعم GTKفي البايثون بـ ،PyGTKالجدير بالذكر ان مكتبة GTKهي المكتبه الساسيه التي يستخدمها سطح مكتب GNOME
لبناء تطبيقاته وواجهته ،بينما يستخدم سطح المكتب KDEمكتبة Qtلبناء الواجهات الرسوميه ،و من ابرز
البرامج التي تم تصميمها بإستخدام الـ GTKهو برنامج GIMPبرنامج الرسم المشهور ،و AbiWord
معالج النصوص و مجموعه ل يستهان بها من البرامج )راجع الملحق أ للحصول على وصلت لمواقع بهذا الخصوص( ،ول بد من الملحظه ان GTKتعمل على نظام التشغيل غنو\لينكس و آبل ماكنتوش و
مايكروسوفت وندوز ،هذا يعني امكانية كتابة برنامج واحد ليعمل على النظمه الثلث.
.1.1.2نظره سريعه على ماضي GTK بدأت GTKاساساً في عام 1996من اجل برنامج الرسم GIMPعلى يد Spencer Kimball و Peter Mattisو Josh MacDonaldعندما قرروا الستغناء عن ) Motifوهي مكتبة اخرى
لبناء واجهات مستخدم رسوميه( و هذا هو سبب تسمية GTKبـ ،GIMP Toolkitتعتبر مكتبة GTK
احد اجزاء مشروع GNUو تُستخدم الن بشكل اساسي في نُظم يونكس )مثل غنو\لينكس و FreeBSD 8
و غيرها ،و كما ذكرنا مسبقاً انه يعمل على نظم التشغيل الخرى مثل مايكروسوفت وندوز(.
.1.1.3بعض الصور لتطبيقات تعمل على GTK
9
10
.1.2قبل البدأ اولً :قبل ان نبدأ بأي شئ ل بد من تثبيت مكتبة PyGTKعلى حاسوبنا ،يمكنك الحصول على هذه المكتبه من موقعها الرسمي .http://www.pygtk.org
ثانياً :في كل ملف Pythonنود استخدام PyGTKفيه ،ل بد من كتابة الوامر التاليه : import pygtk )'pygtk.require('2.0 import gtk و كما ذكرْت في مقدمة الكتاب ،إنّ من يقرأ هذا الكتاب ل بد أنْ يكون مُلماً بلغة ،Pythonو هذا يعني إنّ الشيفره المذكوره في العلى مفهومه و ما هي إل استدعاء لمكتبين ،أما السطر الثاني فهو يُلزم مكتبة pygtk على اختيار الصدار 2.0فما فوق.
11
مرحباً بالعالم: المثال الول.1.3 حتى نبدأ بالدراسه العمليه للمفاهيم الساسيه و التي سوف تساعدنا على بناء التطبيقات يجب أنْ نبدأ بمثال
كالعاده سوف نبدأ،بسيط يستخدم هذه المفاهيم الساسيه ثم نَلجُ في شرح المفاهيم إستناداً إلى هذا المثال .!Hello World ببرنامج "مرحباً بالعالم!" او كما نعرفه بإسم
# -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; def delete_event(widget,data): False; def destroy(widget,data=None): gtk.main_quit(); window = gtk.Window(gtk.WINDOW_TOPLEVEL); window.set_title("ً بالعالم ;)"مرحبا window.connect('delete_event',delete_event); window.connect('destroy',destroy); hello_world_button = gtk.Button("ً بالعالم ;)"مرحبا hello_world_button.show(); window.add(hello_world_button); window.show(); gtk.main(); . إنتقل للجزء التالي من الكتاب و سوف يتم شرحها،(-: ًل تدقق في الشيفره كثيرا
12
.1.4الدوات Widget .1.4.1ما هي الدوات Widget الدوات هي افضل ترجمه وجدتها من اجل مصطلح ،Widgetتُعتبر الدوات الجزء الساسي من اي مكتبه
لبناء واجهات المستخدم الرسوميه ،و بالطبع من ضمن هذه المكتبات مكتبة ،GTKالدوات هي كُل ما يُعرض بواجهة البرنامج ،مثلً النافذه التي تحتوي على مربع Xللغلق و مربع _ لنزال البرنامج إلى قائمة البرامج
المفتوحه تعتبر من الدوات ،كذلك مربع النص الذي نقوم بتعبئة البيانات في داخله يعتبر من الدوات ،و كل
شئ موجود داخل نافذة البرنامج يعتبر من الدوات مربع النص ،الزرار ،القوائم المنسدله ،قائمة اختيار
الملفات و غيرها ،في مثالنا السابق كان لدينا اداتين ،النافذه الرئيسيه ،و الزر المكتوب عليه "مرحباً بالعالم"
.1.4.2الدوات مع الـ PyGTK .1.4.2.1البدايه في PyGTKلكل اداة من الدوات التي يقدمها صنف ) (Classخاص بها و من خلل إنشاء كائن ) (Objectمن هذا الصنف فإننا نقوم بإنشاء الداة و من خلل الكائن نتحكم في الداة التي قمنا بإنشاءها،
حسب مثالنا السابق قمنا بإنشاء نافذه و وضعنا داخل هذه النافذه زر ،لنأخذ اولً السطر الذي قمنا بإنشاء النافذه من خلله و هو التالي :
;)window = gtk.Window(gtk.WINDOW_TOPLEVEL حسناً كما تلحظ في الشيفره السابقه ،قمنا بوضع متغير اسمه ،windowو استخدمنا الصنف
gtk.Windowمن اجل إنشاء النافذه الرئيسيه و كلفنا المتغير windowبها و بالتالي أصبح كائناً ) 13
(Objectيمكننا من خلله التحكم بالنافذه )ل بد من الملحظه ان تكون قد درست الـ OOPمع الـ
Pythonحتى تتمكن من استيعاب ما يُشرح( ،و كما ذكرنا مسبقاً إنّ كل اداة في GTKيكون لها صنف
خاص بها ،هذا يعني أنّ لدينا العديد من الصناف غير الصنف ،gtk.Windowمثلً لدينا الصنف
gtk.Buttonللزره و gtk.Entryلمربعات النصوص و عشرات الصناف غيرها التي تخدم مجموعه
كبيره من المجالت ،بالطبع لن نتمكن من ذكر جميع هذه الصناف في هذا الكتاب و لكننا سنذكر الساسيه
منها حتى نتعلم طريقة الكتابه ،و بعدها يمكنك التبحر عن طريق قراءة الدليل الرسمي الخاص بـ PyGTKو الذي يشرح جميع الصناف المتوفره.
على كل حال نعود لمثالنا ،كما أسلفنا إنّ المتغير windowأصبحَ كائناً ) ،(Objectيمكننا من خلل
هذا الكائن التحكم بالنافذه التي أنشأناها من خلل مجموعه من الدوال التي يوفرها لنا الصنف
،gtk.Windowبالطبع لكل اداة مجموعه من الدوال التي تخصها و التي تعطيك التحكم الكامل في الداة
التي تقوم بإنشاءها ،ننتقل الن إلى السطر التالي في مثالنا و هو :
ً بالعالم"(window.set_title ;)"مرحبا الداله set_titleهي أحد الدوال التي يُقدمها الصنف gtk.Windowو التي تساعدك على التحكم في
الداة التي قمت بإنشاءها ،تقوم هذه الداله بتغيير عنوان النافذه إلى "مرحباً بالعالم" ،بالطبع هناك عشرات الدوال التي يقدمها الصنف gtk.Windowيمكنك القراءه عنها في دليل PyGTKالرسمي ،لنأخذ مثال سريع
على احد هذه الدوال ،الداله resizeتقوم بتغيير حجم النافذه ،قم بإضافة السطر التالي اسفل السطر المدروس في العلى :
;)window.resize(200,200 لحظ أنّ حجم النافذه تغير عن المره السابقه و إنّهه أصبح اكبر ،البارامتر الول لهذه الداله هو عرض النافذه ،و البارامتر الثاني هو الرتفاع.
الن تجاهل السطرين التاليين لنّنا سندرسهما في موضع آخر و إذهب إلى السطر الذي يليهما ،وهو 14
ً بالعالم"(hello_world_button = gtk.Button ;)"مرحبا وفقاً لما اسلفنا من شرح لبد و انك عرفت وظيفة هذا السطر ،يقوم هذا السطر بإنشاء زر جديد و يجعل المتغير
hello_world_buttonكائن لهذا الزر و كما تلحظ أنّ البارامتر هو النص الذي سيُطبع على الزر ،و
كما هو الحال مع النافذه التي انشأناها هناك مجموعه من الدوال التي يقدمها الصنف gtk.Buttonو التي تعطينا التحكم في الزر الذي أنشأناه ،لنأخذ مثلً الداله set_labelو التي تقوم بتغيير النص الموجود على الزر ،مثال :
;)"تم تغيير النص"(hello_world_button.set_label يقوم هذا السطر بتغيير النص الموجود على الزر من "مرحباً بالعالم" إلى "تم تغيير النص" ننتقل إلى السطر التالي وهو :
;)(hello_world_button.show
بعدما قمنا "بإنشاء" الزر ،و قمنا بالتعديل عليه بما يناسب برنامجنا )مثل اختيار النص الذي يظهر على الزر و غيره من هذه المور( ل بد من "عرض" الزر و هذا يتم بإستخدام الداله ،showنستخدم الداله showمع جميع الدوات تقريباً حتى يتم عرض الداة بعد إنشاءها. ننتقل الن إلى السطر التالي : ;)window.add(hello_world_button بعدما اظهرنا الزر في السطر السابق ل بد من اضافته إلى النافذه الرئيسيه ،و هذا ما نقوم به في السطر الذي نقوم
بشرحه الن حيث نستخدم الداله addلهذا الغرض ،في الحقيقه ل يمكننا استخدام هذه الداله إل مره واحده
فقط في برنامجنا ،اي اننا ل نستطيع إل اضافة اداة واحد فقط إلى النافذه الرئيسيه ،قد تتسائل في هذه الحاله كيف
يمكننا وضع اكثر من اداة في نافذتنا الرئيسيه؟ لنفرض إننا نريد تصميم برنامج لتحويل القياسات ،في هذه الحاله 15
سوف نحتاج إلى مجموعه من العناصر ،مثلً مربع نصوص ،زر ،سطر نصّي لطباعة النتائج ،في هذه الحاله كيف يمكنني اضافة جميع هذه الدوات على نافذتي الرئيسيه و في الحقيقه ل يمكنني استدعاء دالة الضافه addإل مره واحده فقط ،الجواب على هذا السؤال هو الداتين VBoxو HBoxاو ما سوف نطلق عليهم اسم الصناديق ،سوف نشرح كيفية استخدام الصناديق و فائدتها فيما بعد. السطر التالي : ;)(window.show كما ذكرنا سابقاً ،الداله showتقوم بعرض الداة بعد إنشاءها و هذا فعلناه مع الزر الذي قمنا بإضافته ،أنشأناه
اولً ثم عرضناه ،و هذا الذي يجب ان يحصل مع اغلب الدوات ،و كما نعلم ان النافذه الرئيسيه تعتبر من ضمن
الدوات و بالتالي لبد من عرضها بعد إنشاءها ،و هذا ما يتم في السطر الذي ندرسه الن. ننتقل إلى السطر الخير وهو :
;)(gtk.main و هذا السطر لبد من كتابته في جميع برامجنا في النهايه ،لنه هو المسؤول عن عرض ما قمنا بتصميمه.
16
.1.4.2.2الصناديق .1.4.2.2.1مقدمه في اثناء شرحنا السابق لشيفرة "مرحباً بالعالم" ظهر لدينا إشكال ،و هو عدم إمكانية استدعاء الداله addاكثر
من مره واحده ،و كما نعلم هذه الداله تقوم بإضافة الدوات التي ننشؤها إلى النافذه الرئيسيه ،و اذا عرفنا انه ل
يمكننا استدعاءها إل مره واحده هذا يعني عدم إمكانية إضافة اكثر من اداة واحد في برنامجنا ،ذكرنا في ذاك الموضع إنّ الحلَ مع الصناديق ،الصناديق HBoxو VBoxهي ادوات توفرها ،PyGTKتُعتبر
الصناديق من اهم الدوات التي سوف نستخدمها في جميع برامجنا التي تحتوي على أكثر من اداة ،لِأُوضِّح
الفكرةَ أكثر :في PyGTKيجب أنْ تكون كل اداة في صندوق لوحده ،مثلً لنفرض اننا نريدُ تصميم واجهه
تحتوي على مربع نص و زر واحد ،افتراضياً في النافذه التي نُنشئها في PyGTKيمكننا إضافة اداة واحده فقط
هذا يعني اننا لن نتمكن من اضافة اداة اخرى ،لحل هذه المشكله ل بد من اضافة صناديق جديده و وضع
الدوات فيها ،هذا يعني اننا سوف ننشئ صندوقين اثنين نضع في واحد منهما مربع النص و نضع في الخر الزر.
HBox.1.4.2.2.2و VBox يوجد لدينا نوعين من الصناديق في ،PyGTKالنوع الول هو HBoxو هذا النوع يقوم بإنشاء الصناديق
بشكل افقي ،اما النوع الثاني هو VBoxو الذي يقوم بإنشاء الصناديق بشكل عمودي ،حسناً الن اذا اردنا وضع الزر بجانب صندوق النص سوف نقوم بإستخدام ،HBoxاما اذا اردنا وضع الزر في اسفل مربع النص )او العكس( سوف نستخدم ،VBoxالجدير بالذكر انه يمكننا وضع صناديق داخل صناديق.
لنرى الن كيفية تعريف : HBox
)gtk.HBox(homogeneous=False, spacing=0
اما تعريف : VBox 17
gtk.VBox(homogeneous=False, spacing=0) يعني اعطاء مساحات متساويه لما يقع داخل الصندوق اذا كانتhomogeneous البارامتر الول .True
. و هي المسافه بين ما يقع داخل الصندوق بالبيكسلspacing البارامتر الثاني حيث تقوم هذه الداله بوضع اداة معينه داخل،pack_start يوفران داله و هيHBox وVBox
.ًصندوق معين مُعرّف مسبقا
ً إستخدام الصناديق عمليا.1.4.2.2.3 فعلً جوابك، اذا اردنا الن اضافة زر آخر بجانب الزر القديم ما الذي يجب فعله،"نعود لمثالنا "مرحباً بالعالم : الشيفره التاليه تحقق لنا ما أردنا،HBox ( سوف نستخدم-: صحيح
# -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; def delete_event(widget,data): False; def destroy(widget,data=None): gtk.main_quit(); window = gtk.Window(gtk.WINDOW_TOPLEVEL); window.set_title("ً بالعالم ;)"مرحبا window.connect('delete_event',delete_event); window.connect('destroy',destroy); box1 = gtk.HBox(False,0); # New line window.add(box1); # New line hello_world_button = gtk.Button("ً بالعالم ;)"مرحبا 18
box1.pack_start(hello_world_button,True,True,0); # New line ;)(hello_world_button.show "); # New lineالزر الثاني"(hello_world_button_2 = gtk.Button box1.pack_start(hello_world_button_2,True,True,0); # New line hello_world_button_2.show(); # New line box1.show(); # New line ;)(window.show ;)(gtk.main لحظ انني وضعت التعليق " New line" #عند السطر الجديده التي قمت بإضافتها على مثالنا الول. لنبدأ بشرح السطر الجديده ،اولً : box1 = gtk.HBox(False,0); # New line window.add(box1); # New line في السطر الول أضفنا الصندوق الذي سنضع فيه الداتين ،لحظ أنّ هذا الصندوق من النوع الفقي لننا نريد
وضع الزر الجديد بجانب الزر القديم ،لو كنا نريد وضع الزر الجديد اسفل الزر القديم لستخدمنا صندوق من النوع ،VBoxفي السطر التالي قمنا بإضافة الصندوق الرئيسي إلى النافذه ،لحظ اننا حذفنا هذا السطر من
اسفل الملف و اضفناه هنا.
ننتقل إلى الجزء الذي يليه من الشيفره : ً بالعالم"(hello_world_button = gtk.Button ;)"مرحبا box1.pack_start(hello_world_button,True,True,0); # New line ;)(hello_world_button.show من خلل هذه السطر قمنا بإنشاء الزر و عرضه ،هل تتذكر المثال السابق؟ هذه الشيفره من المثال السابق ،(-:
كل ما قمنا به هو إضافة السطر الجديد و هو السطر الثاني ،من خلل هذا السطر أضافة الداة
hello_world_buttonداخل الصندوق ،سوف نستخدم الداله pack_startدائماً من اجل 19
إضافة الدوات داخل الصناديق ،و على اختلف نوع الصندوق سواء كان صندوق VBoxاو HBoxفل يوجد فرق فالداله نفسها.
ننتقل إلى السطر التاليه : "); # New lineالزر الثاني"(hello_world_button_2 = gtk.Button box1.pack_start(hello_world_button_2,True,True,0); # New line hello_world_button_2.show(); # New line ل يوجد شئ جديد هنا لننا شرحنا معنى هذه السطر مسبقاً ،هذه السطر تقوم بإضافة زر جديد بجانب الزر
القديم ،السطر الول يقوم بإنشاء الزر ،السطر الثاني يقوم بإضافة الزر إلى الصندوق ،و السطر الخير يقوم بإظهار الزر.
ننتقل إلى آخر سطر جديد قمنا بإضافته : box1.show(); # New line في هذا السطر أظهرنا الصندوق الذي وضعنا ادواتنا فيه ،لحظ انه يمكننا إظهار الداة في اي مكان ،مثلً ل يوجد
مشاكل لو قمنا بإظهار الصندوق بعد إنشاءه مباشره في مثالنا هذا ،ببساطه يمكننا استدعاء الداله showو إظهار اداتنا في اي مكان ،المهم ان ننشئ الداة اولً.
20
.1.5الشارات و الدوال Callback .1.5.1مقدمه في بادئ المر درسنا مفهوم الدوات ) (Widgetو كما تعلم إنّه مفهومٌ مهمٌ جداً ،المفهوم الخر و الذي
يجب إستيعابه بشكلٍ جيّد هو "الشارات" ) ،(Signalsتخيل معي ان برنامجنا يحتوي على زر مكتوب عليه
"اضغط هنا" عندما يضغطُ المستخدمُ على الزر لن يحدثَ اي شئ! لننا وضعنا الزر ضمن النافذه ل اكثر ،اذا اردنا من الزر أنْ يقومَ بعملٍ مفيدٍ سوف نستخدم شيئين هما الشارات ) (Signalsو الدوال )
،(Callbackعندما يضغط المستخدم على الزر تُعتبر هذه الضغطه اشاره و تؤدي إلى تنفيذ داله ) (Callbackتؤدي هذه الداله وظيفه معينه ،مثلً اغلق البرنامج.
.1.5.2الشارات مع PyGTK هل تتذكر السطرين الذين تجاهلناهما عندما بدأنا بشرح مثالنا الول "مرحباً بالعالم"؟ نعم ل بد و إنّك تتذكرهما (-:
;)window.connect('delete_event',delete_event ;)window.connect('destroy',destroy هذان السطران تطبيقٌ للشارات ،مع اي اداة ننشؤها يمكننا استخدام الداله connectحتى نجعل الداة
تقوم بعمل مفيد ،في مثالنا الحالي قمنا بربط النافذه بحدثين ،الحدث الول يُسمى delete_eventو الثاني يُسمى ،destroyنستخدم هذين الحدثين مع النافذه الرئيسيه فقط لنهما يساعدان على إغلق البرنامج عندما
يطلب المستخدم ذلك ،عندما يتم تنفيذ الحدث الول تُستدعى الداله delete_eventو التي عرّفناها مسبقاً ،و عندما يتم تنفيذ الحدث الثاني تُستدعى الداله ،destroyالدالتان delete_eventو destroyهما دالتان من نوع .callback
21
def delete_event(widget,data): False; def destroy(widget,data=None): gtk.main_quit(); حتى تتضح الصوره،هذا النوع من الدوال يستقبل على القل بارامترين كما تلحظ في تعريفنا لهاتين الدالتين
بحيث اذا ضغطنا على زر "مرحباً بالعالم" تظهر لنا نافذه جديده،"بشكل اكبر سنطور مثال "مرحباً بالعالم ."PyGTK تحتوي على نص "هذا برنامجي الول مع : الشيفره التاليه ستقوم بالمطلوب
# -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; def delete_event(widget,data): False; def destroy(widget,data=None): gtk.main_quit(); def the_new_window(widget,data=None): new = gtk.Window(gtk.WINDOW_TOPLEVEL); new.set_title(";)"نافذتنا الجديده msg = gtk.Label(" هذا برنامجي الول معPyGTK"); msg.show(); new.add(msg); new.show(); window = gtk.Window(gtk.WINDOW_TOPLEVEL); window.set_title("ً بالعالم ;)"مرحبا window.connect('delete_event',delete_event); window.connect('destroy',destroy); 22
box1 = gtk.HBox(False,0); window.add(box1); hello_world_button = gtk.Button("ً بالعالم ;)"مرحبا hello_world_button.connect('clicked',the_new_window); # New line box1.pack_start(hello_world_button,True,True,0); hello_world_button.show(); hello_world_button_2 = gtk.Button(";)"الزر الثاني box1.pack_start(hello_world_button_2,True,True,0); hello_world_button_2.show(); box1.show(); window.show(); gtk.main(); . مع داله جديده،لحظ اننا قمنا بإضافة سطر واحد جديد hello_world_button.connect('clicked',the_new_window); عندما يقوم المستخدم بالحدث،clicked في هذا السطر قمنا بـ "ربط" الزر الول بـ "حدث" يُسمى لبد و انك خمنت إنّ الحدث،the_new_window يتم استدعاء داله اسمهاclicked
لنرى،the_new_window هذا يعني عندما يُضغط الزرُ قمْ بإستدعاء الداله، يعني الضغطclicked
: ما هي هذه الداله التي عرفناها
def the_new_window(widget,data=None): new = gtk.Window(gtk.WINDOW_TOPLEVEL); new.set_title(";)"نافذتنا الجديده msg = gtk.Label(" هذا برنامجي الول معPyGTK"); msg.show(); new.add(msg); new.show(); كل ما قمنا به هو إنشاء نافذه جديده و وضعنا لها عنوان،من المفترض إنّه ل شئ جديد عليك في هذه الداله 23
"نافذتنا الجديده" و اضفنا إليها نص للقراءه "هذا برنامجي الول مع ،"PyGTKالشئ الجديد في هذه الداله
هي الداة Labelوالتي لم نتعامل معها مسبقاً ،تقوم هذه الداله بطباعة النص الذي تقوم بوضعه كنص للقراءه.
هناك شئ هام ذكرناه مسبقاً و سوف نعيد ذكره هنا بشئ من السهاب ،جميع الدوال التي صنفناها من النوع callbackيجب أنْ تستقبلَ بارامترين على القل ،و هذا الذي تلحظه عندما عرّفنا الداله
،the_new_windowالبارامتر الول و الذي سميناه widgetيُمثل الداة التي استدعت الداله
،the_new_windowو في مثالنا هذا الداة التي قامت بإستدعاء الداله the_new_window
هي ) hello_world_buttonالزر المكتوب عليه مرحباً بالعالم( ،في هذه الحاله يكون البارامتر
widgetهو نفسه ،hello_world_buttonو بالتالي يمكنك استخدام جميع الدوال التي يقدمها الصنف gtk.Buttonداخل الداله the_new_windowعن طريق البارامتر .widget
ننتقل إلى البارامتر الثاني وهو ،dataتخيل معي اننا نريد تمرير بعض البيانات إلى الداله
،the_new_windowكيف يمكننا ذلك؟ نعود لسطرنا الجديد الذي أضفناه مؤخراً : ;)hello_world_button.connect('clicked',the_new_window
حتى نقوم بتمرير بعض البيانات إلى الداله the_new_windowنقوم بتمرير بارامتر ثالث إلى الداله ،connectمثلً نريد تمرير كلمة " "testإلى ،the_new_windowيتم ذلك كالتالي :
;)"hello_world_button.connect('clicked',the_new_window,"test بالطبع يمكننا تمرير انواع بيانات اخرى ،مثلً : ;]'info = ['One','Two','Three ;)hello_world_button.connect('clicked',the_new_window,info الن كيف يمكننا استقبال البيانات التي قمنا بإرسالها؟ يتم ذلك من خلل البارامتر ،dataاذهب إلى الداله the_new_windowو قم بطباعة محتوى البارامتر dataفي نهايتها ،و جرّب المثالين السابقين ،في 24
اول مثال سوف تطبع كلمة testو في المثال الثاني سوف تطبع السطر ]'،['One','Two','Three
بالطبع هذا السلوب سيفيدك كثيراً اذا كنت ل تستخدم OOPفي كتابة برامجك ،لنك احياناً تحتاج إلى التحكم في بعض الدوات من داخل دوال ،callbackو لن تتمكن من ذلك إل اذا مررت الكائنات الخاصه بهذه الدوات إلى دالة ،callbackسوف يمر هذا علينا في امثلتنا القادمه ان شاء ال .(-:
هكذا نكون قد انتهينا بفضل ال من شرح المفاهيم الساسيه ،ننتقل الن إلى المثال الخر حتى نتدرب اكثر.
25
.1.6المثال الثاني :برنامج تحويل درجة الحراره سوف نقوم الن بإستخدام ما تعلمناه و نبني برنامجاً جديداً ،يأخذ هذا البرنامج درجة الحراره بالسيليزي و
يحولُها إلى الفهرنهايتي ،هذا يعني إنّ برنامجنا يحتوي على مربع نص لخذ القيمه السيليزيه من المستخدم ،و
كذلك سوف نحتاج إلى زر يضغط المستخدم عليه عند النتهاء من وضع القيمه المُراد تحويلها ،و اخيراً نحتاج إلى نص ) (Labelلطباعة الناتج ،لنبدأ اولً بكتابة السطر التي اتفقنا على كتابتها في بادئ الكتاب :
*# -*- coding: utf-8 -;import pygtk ;)'pygtk.require('2.0 ;import gtk نبني الن النافذه ونضع عنواناً لها ،و نكتب الدالتين اللتان تساعدان على الخروج من البرنامج : def delete_event(widget,data): ;False def destroy(widget,data=None): ;)(gtk.main_quit ;)window = gtk.Window(gtk.WINDOW_TOPLEVEL ;)"برنامج التحويل"(window.set_title ;)window.connect('delete_event',delete_event ;)window.connect('destroy',destroy كما اتفقنا في البدايه ،سوف نحتاج إلى مربع نص ،زر و نص مقروء ) ،(Labelسنضعهم اسفل بعضهم
البعض ،و بالتالي سنستخدم صندوق من نوع ،VBoxسنقوم بإنشاء هذا الصندوق ثم نضيفه إلى النافذه كما
تعلمنا من المثال السابق.
;)main_box = gtk.VBox(False,0 ;)window.add(main_box لنبدأ بإضافة مربع النص اولً ،لحظ اننا و للمره الولى نستخدم هذه الداة ،نقوم بإنشاءها اولً عن طريق إنشاء 26
كائن لها ثم نضيفها داخل صندوقنا و اخيراً نقوم بإظهارها : ;)(text_entry = gtk.Entry ;)main_box.pack_start(text_entry,True,True,0 ;)(text_entry.show الن نضيف اداة النص و التي تعرفنا عليها مسبقاً و كانت بإسم ،Labelتكون في البدايه فارغه ،و سوف يتم تغييرها فيما بعد :
;)""(status_label = gtk.Label ;)main_box.pack_start(status_label,True,True,0 ;)(status_label.show
لننشئ زر و نضع فوقه كلمة "موافق" ،ثم نضيفه إلى صندوقنا و اخيراً نظهره : ;)"موافق"(ok_button = gtk.Button ;)main_box.pack_start(ok_button,True,True,0 ;)(ok_button.show و اخيراً سوف نُظهر الصندوق و النافذه الرئيسيه و نستدعي الداله الساسيه : gtk_main ;)(main_box.show ;)(window.show ;)(gtk.main صممنا الن واجهه بسيطه لبرنامجنا ،لحظ إنّ البرنامج لحد الن ل يقوم بأي عمل مفيد ،حتى يقوم البرنامج
بعمل مفيد سنقوم بربط الزر بحدث ،بحيث عندما يتم الضغط على الزر تُستدعى داله معينه تقوم بالعمليه الحسابيه و تطبع الناتج ،نتوجه إلى السطرين اللذين قمنا بكتابتهما :
;)"موافق"(ok_button = gtk.Button 27
;)main_box.pack_start(ok_button,True,True,0 و نضيف في اسفلهما دالة الربط ،لبد و انك تعرفها (-: ;]widgets = [status_label,text_entry ;)ok_button.connect('clicked',ClickEvent,widgets لنتوقف هنا قليلً ،كما تعلم إنّ البارامتر الول هو الحدث ،و clickedبمعنى الضغط.
البارامتر الثاني هي الداله التي تُستدعى عندما يتم تنفيذ الحدث ،وفي حالتنا هذه الحدث هو الضغط على الزر. البارامتر الثالث وهو ما جلعني اتوقف هنا ،هل تتذكر شرحنا السابق عن موضوع تمرير البارامترات إلى دوال callback؟ هنا قمنا بتمرير مصفوفه تحتوي على الداتين الموجودتين في برنامجنا كبارامتر للداله
ClickEventالتي سوف نكتبها بعد قليل ،و سوف تستقبل ClickEventهذا البارامتر بإسم
،dataلبد و انك تعرف هذه المعلومات و لكن وجب التذكير ،قد تتسائل لماذا مررنا هذه المصفوفه؟ سيأتيك
الجواب عند تعريف الداله .ClickEvent
لنصعد قليلً ،و بالضبط اسفل الداله destroyسوف نعرّف دالتنا ،ClickEventتقوم هذه الداله
بأخذ القيمه الموجوده في مربع النص تضربها في 1.8ثم تضيف عليه 32و بالتالي يكون الناتج درجة الحراره
بالفهرنهايت ،نكتب اولً رأس الداله و التي تستقبل بارامترين كما شرحنا مسبقاً :
def ClickEvent(widget,data=None): يُعتبر البارامتر dataالن مصفوفه تحتوي على عنصرين ،العنصر الول هو الكائن status_labelو الذي
يتيح لنا التحكم بأداة النص ) ،(Labelاما العنصر الثاني هو الكائن text_entryو الذي يتيح لنا التحكم
في اداة مربع النص ،و بالطبع البارامتر widgetيتيح لنا التحكم بالزر ،مررنا الكائن status_labelعلى الداله ClickEventحتى نتمكن من تغيير النص الموجود في اداة النص ) (Labelمن داخل الداله،
هذا يعني إنّنا نريد الوصول إلى هذه الدوات من داخل الداله ،و بالتالي مررنا كائنات هذه الدوات كبارامتر للداله ،الن تحتوي [data[0على الكائن status_labelو تحتوي [data[1على الكائن text_entryو حتى نقوم بالتسهيل نخزّن القيم في متغيرات جديده ذات اسم اوضح : 28
;]status_label = data[0 ;]text_entry = data[1 نأخذ الن القيمه الموجوده في داخل مربع النص بإستخدام الداله get_textو التي يوفرها الصنف : gtk.Entry
;)(val = text_entry.get_text نحوّل هذه القيمه من نوع stringإلى نوع : float ;)val = float(val نُجري عمليتنا الحسابيه : ;result = (val * 1.8) + 32 نحول الناتج من نوع floatإلى stringمره اخرى من اجل كتابته داخل اداة النص : ;)result = str(result اخيراً نغير النص الموجود في اداة النص بإستخدام الداله set_textو التي يوفرها الصنف : gtk.Label
;)status_label.set_text(result بهذا الشكل تكون شيفرة الداله كامله كالتالي : def ClickEvent(widget,data=None): ;]status_label = data[0 ;]text_entry = data[1 ;)(val = text_entry.get_text 29
;)val = float(val ;result = (val * 1.8) + 32 ;)result = str(result ;)status_label.set_text(result نعود ثانية لنقطة البارامتر ،dataهناك طريقه اخرى بدلً من تمرير الدوات التي نريد التحكم بها من داخل
الداله فبدلً من تمريرها كبارامترات للداله ClickEventيمكننا استخدام الكلمه globalللوصول إلى
كائنات هذه الدوات بدون الحاجه إلى تمريرها كبارامترات ،و بالتالي تكون دالتنا بهذا الشكل :
def ClickEvent(widget,data=None): ;global text_entry,status_label ;)(val = text_entry.get_text ;)val = float(val ;result = (val * 1.8) + 32 ;)result = str(result ;)status_label.set_text(result و السطران : ;]widgets = [status_label,text_entry ;)ok_button.connect('clicked',ClickEvent,widgets يصبحان بهذا الشكل : ;)ok_button.connect('clicked',ClickEvent بدون تمرير اي شئ ،ما رأيك أليست اسهل و اكثرا اختصاراً؟ (-:سنستخدم globalمن الن فصاعداً ،الن الشيفره النهائيه لبرنامج التحويل كالتالي :
*# -*- coding: utf-8 -30
import pygtk; pygtk.require('2.0'); import gtk; def delete_event(widget,data): False; def destroy(widget,data=None): gtk.main_quit(); def ClickEvent(widget,data=None): global text_entry,status_label; val = text_entry.get_text(); val = float(val); result = (val * 1.8) + 32; result = str(result); status_label.set_text(result); window = gtk.Window(gtk.WINDOW_TOPLEVEL); window.set_title(";)"برنامج التحويل window.connect('delete_event',delete_event); window.connect('destroy',destroy); main_box = gtk.VBox(False,0); window.add(main_box); text_entry = gtk.Entry(); main_box.pack_start(text_entry,True,True,0); text_entry.show(); status_label = gtk.Label(""); main_box.pack_start(status_label,True,True,0); status_label.show(); ok_button = gtk.Button(";)"موافق main_box.pack_start(ok_button,True,True,0); ok_button.connect('clicked',ClickEvent); ok_button.show(); main_box.show(); window.show(); gtk.main();
31
.1.7المثال الثالث :آله حاسبه قبل النتقال إلى الموضوع التالي لنأخذ مثالً ثالثاً ،هذا المثال عباره عن آله حاسبه ،تقوم بالعمليات الساسيه، جمع ،طرح ،ضرب ،قسمه ،تحتوي واجهة البرنامج على ثلث مربعات نص يُكتب في المربع الول العدد الول و
تُكتب العمليه المطلوب تنفيذها في المربع الثاني و اخيراً يُكتب الرقم الثاني في المربع الخير ،بالضافه إلى ذلك يكون هناك زراً لظهار الناتج و يكون هناك نصاً ) (Labelيظهر فيه الناتج بعد الضغط على الزر.
كما تعودنا نكتب اولً الشيفرات الساسيه و التي دائماً ما نستخدمها في بداية برامجنا : *# -*- coding: utf-8 -;import pygtk ;)'pygtk.require('2.0 ;import gtk نبني الن نافذه البرنامج مع الدالتين اللتين تعودنا دائماً ربط النوافذ بهما : def delete(widget,data): ;False def des(widget,data=None): ;)(gtk.main_quit ;)win = gtk.Window(gtk.WINDOW_TOPLEVEL ;)"آله حاسبه"(win.set_title ;)win.connect('delete_event',delete ;)win.connect('destroy',des لنأخذ وقتنا بالتفكير بعدما بنينا النافذه ،الصندوق الرئيسي الذي سنضعه هل يكون من نوع HBoxام ،VBox
كما اتفقنا سيكون هنالك ثلث مربعات نص ،و جميعها بجانب بعضها البعض ،و في اسفلها جميعها يكون هناك
زر و اسفل الزر هناك نص تظهر فيه النتائج ،في هذه الحاله نحتاج إلى النوعين ،نحتاج الصندوق HBoxمن
اجل مربعات النص الثلثه بجانب بعضهم ،و نحتاج إلى صندوق VBoxحتى نضع الزر اسفل هذه الدوات و 32
نضع النص اسفل المربع ،الصندوق الرئيسي سيكون من نوع ،VBoxنقسم هذا الصندوق إلى ثلث خانات
عموديه ،الخانه الولى تحتوي على مربعات النص ،الخانه الثانيه تحتوي على الزر و الخانه الثالثه تحتوي على النص ) ،(Labelالمشكله هنا أنّ الخانه الواحده ل تستوعب إل اداة واحده ،و بما أنّ نوع الصندوق
VBoxاذاً سيتم وضع كل اداة جديده يتم اضافتها إلى الصندوق في اسفل الداة التي تسبقها ،اذاً لدينا
مشكله في الخانه الولى و هي كيف نضع الثلث ادوات بجانب بعضهن البعض؟ سنقوم بإنشاء صندوق جديد
من نوع HBoxغير الصندوق الرئيسي ،بعدها نضيف مربعات النص إلى الصندوق الثاني HBoxو اخيراً
نضيف الصندوق HBoxإلى الصندوق الرئيسي من نوع VBoxفي الخانه الولى ،هذه الطريقه هي الطريقه
المُستخدمه دائماً لحل مثل هذه المشاكل ،عندما يكون لدينا واجهه معقده و تحتوي على ادوات بجانب بعضها و ادوات اخرى اسفلها نستخدم الحل المشروح و الذي نضع عليه مثال عملي الن.
حسناً لنبدأ ،نضيف الن الصندوق الرئيسي و الذي كما اتفقنا سيكون من نوع : VBox ;)main_box = gtk.VBox(False,0 ;)win.add(main_box كما اتفقنا ،في الخانه الولى سنضع مربعات النص الثلثه بجانب بعضهن ،ووفقاً لما شرحناه بالعلى سننشئ صندوقاً جديداً من نوع HBoxمن اجل تنفيذ هذا الغرض :
;)box1 = gtk.HBox(False,0 نضيف الن مربع النص الول بشكل عادي و كما تعلمنا في الصفحات السابقه ،و لكننا بدلً من اضافته إلى
الصندوق الرئيسي main_boxنضيفه إلى الصندوق الثاني ،box1يُستخدم مربع النص هذا من اجل كتابة الرقم الول :
;)(first_number = gtk.Entry ;)box1.pack_start(first_number,True,True,0 ;)(first_number.show نضيف بعدها المربع الثاني ،و الذي يُستخدم من اجل كتابة نوع العمليه المطلوبه +او -او * او \
33
;)(operation = gtk.Entry ;)box1.pack_start(operation,True,True,0 ;)(operation.show و اخيرا نضيف المربع الثالث و الذي يُستخدم لكتابة الرقم الثاني : ;)(second_number = gtk.Entry ;)box1.pack_start(second_number,True,True,0 ;)(second_number.show الن و بعد اضافة جميع مربعات النص إلى الصندوق الثاني ،نضيف الصندوق إلى الصندوق الرئيسي ثم نُظهره : ;)main_box.pack_start(box1,True,True,0 ;)(box1.show نضيف الن بقية الدوات بشكل عادي طالما انها سوف تكون تحت بعضها البعض ،بالطبع ل داعي ان اقول لك اننا سوف نضيفها إلى الصندوق الرئيسي ، (-:نضيف اولً الزر :
;)"أظهر الناتج"(button = gtk.Button ;)main_box.pack_start(button,True,True,0 ;)(button.show بعد الزر نضيف النص : ;)(result = gtk.Label ;)main_box.pack_start(result,True,True,0 ;)(result.show
و اخيراً نُظهر الصندوق الرئيسي ،النافذه ،و ننادي الداله الساسيه : ;)(main_box.show 34
;)(win.show ;)(gtk.main انتهينا الن من تصميم الواجهه فحسب ،يجب ان يكون برنامجنا اكثر من مجرد واجهه ،(-:يبدأ عمل البرنامج
عندما يضغط المستخدم على الزر ،يستدعي الزرُ داله معينه تقوم بالعمليه الرياضيه ثم تطبع الناتج على النص ) (Labelهذا كل شئ.
اذاً نبدأ مع الزر ،نربط الزر بالحدث clickedو نستدعي داله اسمها DoMathعن طريق السطر التالي
و الذي سوفه نضيفه فوق وضع الزر في الصندوق الرئيسي :
;)button.connect('clicked',DoMath نُعرّف الن الداله DoMathفي اسفل الدوال التي عرفناها مسبقاً : def DoMath(widget,data=None): نستخدم globalكما شرحنا مسبقاً حتى نتمكن من التحكم بالدوات من داخل الداله : ;global result,first_number,second_number,operation نبدأ اولً في التحقق اذا كان المستخدم ترك مربعات النص فارغه ام ل ،في حال ترك احدها فارغه نُظهر له رسالة خطأ :
;)(f = first_number.get_text ;)(s = second_number.get_text ;)(o = operation.get_text if f == "" or s == "" or o == "": ;)"يرجى تعبئة المعلومات المطلوبه"(result.set_text 35
اذا كان كل شئ يعمل حسب الصول و جميع المعلومات المطلوبه متوفره ،نبدأ بالعمليه الحسابيه ،في بادئ المر نحوّا نوع كل من المتغيرين fو sإلى integerحتى يكون ناتج العمليه الحسابيه صحيحاً : ;)f = int(f ;)s = int(s بعدها نتحقق من النص الذي ادخله المستخدم في المربع الثاني ،هل هو +او -او * او \ ثم نقوم بالعمليه
المطلوبه وفقاً لمحتوى المربع الثاني ،اذا كان محتوى المربع الثاني ل يساوي اياً من القيم المذكوره سنطبع رساله خطأ للمستخدم و نخرج من دالة : DoMath
if o == "+": ;r = f + s elif o == "-": ;r = f - s elif o == "*": ;r = f * s elif o == "/": ;r = f / s else: العمليه التي قمت بإختيارها"(result.set_text ;)"غير صحيحه ;return False و اخيراً نطبع الناتج للمستخدم بعد تحويله إلى نص : ;))result.set_text(str(r هذا كل شئ ،ما رأيك هل المور سهله؟ (-: النص الكامل للبرنامج :
*# -*- coding: utf-8 -;import pygtk ;)'pygtk.require('2.0 ;import gtk 36
def delete(widget,data): False; def des(widget,data=None): gtk.main_quit(); def DoMath(widget,data=None): global result,first_number,second_number,operation; f = first_number.get_text(); s = second_number.get_text(); o = operation.get_text(); if f == "" or s == "" or o == "": result.set_text(";)"يرجى تعبئة المعلومات المطلوبه else: f = int(f); s = int(s); if o == "+": r = f + s; elif o == "-": r = f - s; elif o == "*": r = f * s; elif o == "/": r = f / s; else: result.set_text("العمليه التي قمت بإختيارها ;)"غير صحيحه return False; result.set_text(str(r)); win = gtk.Window(gtk.WINDOW_TOPLEVEL); win.set_title(";)"آله حاسبه win.connect('delete_event',delete); win.connect('destroy',des); main_box = gtk.VBox(False,0); win.add(main_box); box1 = gtk.HBox(False,0); first_number = gtk.Entry(); box1.pack_start(first_number,True,True,0); first_number.show(); operation = gtk.Entry(); 37
box1.pack_start(operation,True,True,0); operation.show(); second_number = gtk.Entry(); box1.pack_start(second_number,True,True,0); second_number.show(); main_box.pack_start(box1,True,True,0); box1.show(); button = gtk.Button(";)"أظهر الناتج button.connect('clicked',DoMath); main_box.pack_start(button,True,True,0); button.show(); result = gtk.Label(); main_box.pack_start(result,True,True,0); result.show(); main_box.show(); win.show(); gtk.main();
38
.1.8مدخل إلى Glade .1.8.1مقدمه قد يعتبر البعض كتابة الشيفره المصدريه الخاصه بواجهة البرنامج عمليه ممله و على العكس قد يكون هناك
اشخاص يتمتعون عندما يكتبون شيفرة واجهة البرنامج ،و لكن لنفكر بالموضوع قليلً ،اذا قررنا كتابة برنامج ضخم بإستخدام PyGTKفإننا سنستغرق الكثير من الوقت من اجل كتابة الشيفره المصدريه الخاصه
بالواجهه ،و قد تكون عملية الضافه على الواجهه في ما بعد عمليه متعبه و طويله ،لحسن الحظ هناك حل لهذه المشكله و هو برنامج .Glade
يسهّل عليك برنامج Gladeتصميم واجهات برنامجك ،فبدلً من كتابة شيفرة الواجهه تقوم بتصميم الواجهه
بشكل مرئي عن طريق برنامج ،Gladeسوف يفهم مبرمجي Gambasو Delphiو Visual
Basicقصدي ،يقوم برنامج Gladeبعدها بتوليد ملفات من نوع xmlتستخدمها فيما بعد داخل شيفرة
برنامجك حتى تتمكن من التحكم في واجهتك الرسوميه.
Gladeليس برنامجاً فحسب ،بل يأتي معه مكتبه برمجيه تحتوي على مجموعه من الدوال التي تساعدك على
التعامل مع ملفات xmlالتي يولدها ،Gladeإلى وقت تأليف هذا الكتاب فإن النسخه النهائيه من Glade هي النسخ ،3.4الجيل الثالث من Gladeكان اعادة كتابه من الصفر ،من وجهة نظري ان واجهة الجيل
الثالث افضل بكثير من واجهة الجيل الثاني
سوف نتعلم في الصفحات القادمه إن شاء ال كيفية استخدام Gladeو استخدام مكتبته لنشاء تطبيقاتنا.
39
.1.8.2التصميم مع Glade لقد تعلمنا في القسام السابقه كيف نكتب شيفرة واجهة المستخدم ،عملية تصميم واجهة المستخدم مع
Gladeاسهل بكثير ،بمجرد رؤيتك لواجهة البرنامج سوف ترى إنّ العمليةَ بسيطةٌ ،لنلقي نظره على الصوره
التاليه :
هذا هو برنامج Gladeالذي سنستخدمه لتصميم واجهاتنا من الن فصاعداً ،حسناً لنرى الن كيف يمكننا
استخدام هذا البرنامج في تصميم واجهتنا ،نلحظ على الجانب اليسر قائمه تحتوي على الدوات مثل النافذه و
الصناديق و مربعات النص و غيرها الكثير من الدوات التي لم نستخدمها مسبقاً ،هذه هي جميع الدوات التي 40
تقدمها ،GTKكما تعودنا دائماً في البدايه نحن بحاجه إلى نافذه حتى نضيف عليها ادوات البرنامج ،ستجد
النافذه في قائمة Toplevelsو اسمها ،Windowالصوره التاليه توضح لك اننا اضفنا نافذه إلى واجهتنا التي نبنيها :
لحظ الفرق بعد اضافة النافذه ،في المنتصف ظهرت النافذه التي اضفناها و التي سوف نضيف فوقها الدوات،
اذاً الجزء الذي يقع في المنتصف هو مكان العمل ،اما جهة اليمين نلحظ ان هناك قائمه في العلى ،هذه القائمه
تحتوي على جميع الدوات التي اضفناها في مشروعنا هذا ،اما اسفل هذه القائمه نجد صندوقاً يحتوي على
مجموعه من مربعات النص و القوائم و التبويبات ،من خلل هذا الصندوق يمكننا التحكم بخصائص اي اداة ،و في هذه الصوره بالذات فإننا نتحكم بخصائص النافذه التي اضفناها ،الصوره التاليه توضح شرحنا هذا بشكل اوضح :
41
ما رأيك الن لو صممنا واجهه بسيطه جداً نتعلم من خللها كيفية التصميم؟ نريد واجهه تحتوي على زر مكتوب عليه "مرحباً بالعالم" هذا كل شئ!
هيا لنبدأ نضيف اولً النافذه تجدها كما ذكرنا تحت القسم Toplevelsبإسم ،Windowلننا سنضيف
اداة واحده فقط و بالتالي لسنا بحاجه إلى استخدام الصناديق ،نضيف الزر إلى النافذه مباشره ،بعد إضافة الزر
نذهب إلى خصائصه لنغيرها )يمين الشاشه في السفل( ،يجب ان يكون التبويت المُختار هو Generalنجد حقل اسمه labelو بجانبه مربع نص كبير ،نغيّر النص في هذا المربع إلى "مرحباً بالعالم" ،يجب ان يظهر
لك شئ مشابه للصوره التاليه )بالمناسبه ل تقلق بشأن حجم النافذه( :
42
نخزّن الن واجهتنا البسيطه. من اجل الترتيب من الفضل إنشاء مجلد خاص ببرنامجنا لنخزّن فيه الملف ،لنسمي ملفنا بإسم ،guiبعد
التخزين يجب ان يكون لدينا في المجلد ملف بإسم ،gui.gladeهذا الملف هو الواجهه التي صممناها و
التي سوف نستخدمها في النص البرمجي لظهار الواجهه ،ننتقل إلى القسم التالي حتى نرى كيف نتعامل مع ملفات Gladeفي النص البرمجي.
43
.1.8.3استخدام ما تم تصميمه مع Gladeفي النص البرمجي نبدأ برنامجنا كما تعودنا دائماً بإرفاق المكتبات التي سوف نستخدمها : *# -*- coding: utf-8 -;import pygtk ;)'pygtk.require('2.0 ;import gtk لننا الن سوف نتعامل مع ملفات من نوع glade.فإننا بحاجه إلى ارفاق مكتبة Gladeحتى نتكمن من التعامل مع الملف الذي ولّده برنامج : Glade
;import gtk.glade نضيف الدوال التي اعتدنا على اضافتها في كل برنامج : def delete_event(widget,data): ;False def destroy(widget,data=None): ;)(gtk.main_quit نستخدم الن الداله XMLالتي تقدمها لنا مكتبة ،Gladeبالطبع تحتوي مكتبة Gladeعلى الكثير من
الدوال التي تقدم لنا وظائف مختلفه و لكننا سنستخدم الدوال الساسيه ،يمكنك البحث اكثر من خلل قراءتك للدليل الرسمي ،نعود للداله XMLتأخذ هذه الداله اسم ملف glade.كبارامتر و تجعل المتغير -الذي يقوم المبرمج بوضعه للداله - XMLهو المُتحكم في الواجهه ،نقوم الن بإستخدام هذه الداله :
;)'g = gtk.glade.XML('gui.glade وفقاً للسطر السابق فإن gهو المتغير الذي سوف نتحكم من خلله بواجهتنا ،و gui.gladeهو اسم الملف 44
الذي ولّده برنامج ،Gladeلبد و انك تتذكر اننا سمينا الملف بإسم .gui تحتوي واجهتنا هذه على اداتين ،الداة الولى هي النافذه الرئيسيه ،الداة الثانيه هي الزر المكتوب فوقه "مرحباً بالعالم".
في السابق عندما كنّا نكتب واجهاتنا الرسوميه عن طريق السطر البرمجيه كان هناك متغير لكل اداة نضيفها ،و من خلل هذا المتغير نتحكم في الداة ،لحسن الحظ يمكننا فعل ذلك مع مكتبة Gladeعن طريق استخدام الداله ،get_widgetتأخذ هذه الداله اسم الداة التي تريد التحكم بها كبارامتر ،ارجع إلى برنامج
Gladeو شغّل الملف ،gui.gladeاختر النافذه و انظر في خصائصها ،انظر الخاصيه Nameسوف
تجد ان المكتوب في المربع الواقع بجانب Nameهو window1هذا هو السم الذي سوف نمرره إلى الداله ،get_widgetكذلك بالنسبه للزر سوف تجد ان اسمه ،button1بالطبع يمكنك تغيير هذه
السماء إلى اسماء انسب ،من وجهة نظري جعل السماء الفتراضيه التي يضعها Gladeمن window1و button1و button2عاده سيئه و سوف يظهر ذلك بوضوح عندما تصمم برنامج ذات واجهه معقده،
لذلك من الفضل وضع اسماء ذات معنى لكل اداة.
غيّر الن اسم النافذه إلى ،main_windowو اسم الزر إلى hello_world_buttonو احفظ التعديلت ،الن لنستخدم الداله : get_widget
;)'window = g.get_widget('main_window ;)'button = g.get_widget('hello_world_button ممتاز ،الن يمكننا التحكم بالدوات ،كما تعودنا نقوم بإظهار الدوات عن طريق الداله : show ;)(window.show ;)(button.show نستدعي الداله الساسيه :
45
;)(gtk.main حسناً ل بد و انك تعلم اننا الن يمكننا استخدام المتغيرين windowو buttonمن اجل التحكم
بالداتين و بالتالي يمكننا استخدام جميع الدوال التي يقدمها صنف gtk.Windowاو صنف
،gtk.Buttonمثلً اذا اردنا تغيير عنوان النافذه يمكننا استخدام الداله set_titleو هكذا.
الن اصبح من الواضح كيفية استخدام الملفات التي يولّدها برنامج Gladeفي داخل برامجنا ،سوف ننتقل إلى القسم التالي و نتعلم كيف يمكننا التعامل مع الشارات. الشيفره كامله : *# -*- coding: utf-8 -;import pygtk ;)'pygtk.require('2.0 ;import gtk ;import gtk.glade def delete_event(widget,data): ;False def destroy(widget,data=None): ;)(gtk.main_quit ;)'g = gtk.glade.XML('gui.glade ;)'window = g.get_widget('main_window ;)'button = g.get_widget('hello_world_button ;)(window.show ;)(button.show ;)(gtk.main
46
.1.8.4استخدام الشارات مع ما تم تصميمه مع Glade هناك طريقتان يمكننا استخدام احدهما للتعامل مع الشارات عند استخدامنا لـ ،Gladeالطريقه الولى هي
الطريق التي اعتدنا عليها و استخدمناها في السابق ،درسنا قبل قليل الداله get_widgetو التي تسمح لنا
باستخدام اي اداة بشكل عادي و كأننا اضفناها عن طريق شيفره برمجيه ،و بالتالي يمكننا استخدام الطريقه
القديمه و هي الداله ،connectنعود لمثالنا السابق ،نريد ربط الزر بحدث الضغط ،عندما يضغط المستخدم
على الزر يتغير النص الموجود في اعلى الزر إلى "تم الضغط على الزر" و بالطبع ل ننسى ربط النافذه بدالة الغلق :
;)'window = g.get_widget('main_window ;)'button = g.get_widget('hello_world_button اولً نبدأ بدوال الغلق : ;)window.connect('delete_event',delete_event ;)window.connect('destroy',destroy الن نعمل مع الزر ،و نربطه بحدث الضغط كما تعلمنا مسبقاً : ;)button.connect('clicked',ButtonClicked نكتب الن الداله ButtonClickedاعلى الملف : def ButtonClicked(widget,data=None): ;)'تم الضغط على الزر'(widget.set_label هذا كل شئ! الشيفره كامله : 47
# -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; import gtk.glade; def delete_event(widget,data): False; def destroy(widget,data=None): gtk.main_quit(); def ButtonClicked(widget,data=None): widget.set_label(';)'تم الضغط على الزر g = gtk.glade.XML('gui.glade'); window = g.get_widget('main_window'); button = g.get_widget('hello_world_button'); window.connect('delete_event',delete_event); window.connect('destroy',destroy); button.connect('clicked',ButtonClicked); window.show(); button.show(); gtk.main(); بالطبع ل داعي للسهاب عن ما قمنا به لننا تحدثنا بشكل مفصّل عنه بالقسام السابقه و يفترض بك أنْ تكون .فهمته بشكل جيّد
مع برنامجgui.glade شغّل الملف،Glade ننتقل الن إلى الطريقه الثانيه و هذه الطريقه خاصه بـ
و هذه المره بدلً من اختيار، اذهب إلى الخصائص، و اختر الزر من اجل تغيير خصائصه، مره اخرىGlade
، من خلل هذا التبويب يمكننا ربط الزر بأي حدث مطلوب،Signals اختر التبويبGeneral التبويت
سوف نجد مربع نص يمكنناclicked بجانب الكلمه،clicked في برنامجنا هذا الحدث المطلوب هو
شخصياً افضّل،ً نكتب دائماً في هذا المربع اسماً مميزا،Handler مكتوب اعلى هذا المربع،الكتابه فيه 48
كتابة اسم الداله التي سوف تعالج الحدث ،يمكننا تغيير السم إلى اي شئ آخر و لكن حتى ل نزيد السماء
في برنامجنا نستخدم اسم موحّد ،سنستخدم هذا السم في النص البرمجي بحيث نضعه في مصفوفه )او قاموس(
و يكون السم مفتاح المُدخل و تكون قيمته هي الداله التي يجب استدعاءها ،في حالتنا هذه الداله التي سوف تعالج الحدث هي ButtonClickedو التي كتبناها منذ قليل ،اذاً قم بوضع ButtonClicked في ،Handlerاحفظ المشروع و لننتقل إلى النص البرمجي ،سوف نعدّل على المثال السابق و نحوّل
الشارات فيه من الطريقه العاديه إلى طريقة ،Gladeو بالتالي يجب علينا حذف السطر التاليه من النص البرمجي :
;)window.connect('delete_event',delete_event ;)window.connect('destroy',destroy ;)button.connect('clicked',ButtonClicked ننشئ الن المصفوفه التي اتفقنا على إنشائها ،تحتوي هذه المصفوفه على مُدخل واحد فقط ،له مفتاح بإسم ،ButtonClickedو قيمة هذا المفتاح هي الداله ،ButtonClickedل تخلط بين الثنين،
المفتاح هو السم الذي حددناه في Gladeو ليس من الضروري ان يكون بنفس إسم الداله ،اما السم
الثاني فهو اسم الداله نفسها ،سوف نسمّي هذه المصفوفه بإسم : signals
}signals = {"ButtonClicked" : ButtonClicked الن حتى تتم عملية الربط بنجاح نقوم بإستدعاء الداله signal_autoconnectو التي تقدّمها لنا مكتبة Gladeكالتالي :
;)g.signal_autoconnect(signals الن ربطنا الزر فقط بطريقة ،Gladeيمكنك تجربة البرنامج الن و سوف يعمل كما كان ،نُكمل عملية الربط
الن و نربط النافذه بحدث الغلق حتى نتمكن من إغلق البرنامج بشكل صحيح ،افتح الملف gui.glade مع برنامج Gladeمره اخرى ،هذه المره اختر النافذه الرئيسيه ،انتقل إلى التبويب ،Signalsكما تعودنا
سوف نربط النافذه بالحدثين delete_eventوالذي تجده في GtkWidgetكما اتفقنا سوف نضع 49
الحدث الثاني هو،delete_event بنفس اسم الداله و دالتنا هنا هيHandler اسم الـ
احفظ العمل،destroy هنا بإسمHandler سوف نسمي الـ،GtkObject تجده فيdestroy
.و انتقل إلى النص البرمجي
نضيف مُدخلين جديدين إلى مصفوفة الشارات و بالطبع هذان المُدخلن يخصّان الحدثين الذين اضفناهما قبل : و بالتالي تصبح مصفوفتنا بعد التعديل كالتالي،قليل
signals = {
"ButtonClicked" : ButtonClicked, "destroy" : destroy, "delete_event" : delete_event}
g.signal_autoconnect(signals); (-: جرّب البرنامج الن و ستجده يعمل وفقاً للصول : الشيفره كامله # -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; import gtk.glade; def delete_event(widget,data): False; def destroy(widget,data=None): gtk.main_quit(); def ButtonClicked(widget,data=None): widget.set_label(';)'تم الضغط على الزر g = gtk.glade.XML('gui.glade'); window = g.get_widget('main_window'); button = g.get_widget('hello_world_button'); signals = {
"ButtonClicked" : ButtonClicked, 50
"destroy" : destroy, "delete_event" : delete_event} g.signal_autoconnect(signals); window.show(); button.show(); gtk.main();
51
Glade نقل برنامج تحويل درجة الحراره إلى.1.8.5 سوف،Glade و كما تعلم فإننا كتبنا واجهته برمجياً و لم نصممها مع،بالطبع تتذكر المثال الثاني الذي كتبناه
النص البرمجي هو، و بالطبع سوف نستند إلى نفس النص البرمجي،Glade ننقل واجهة هذا البرنامج الن إلى :
# -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; def delete_event(widget,data): False; def destroy(widget,data=None): gtk.main_quit(); def ClickEvent(widget,data=None): global text_entry,status_label; val = text_entry.get_text(); val = float(val); result = (val * 1.8) + 32; result = str(result); status_label.set_text(result); window = gtk.Window(gtk.WINDOW_TOPLEVEL); window.set_title(";)"برنامج التحويل window.connect('delete_event',delete_event); window.connect('destroy',destroy); main_box = gtk.VBox(False,0); window.add(main_box); text_entry = gtk.Entry(); main_box.pack_start(text_entry,True,True,0); text_entry.show(); status_label = gtk.Label(""); 52
main_box.pack_start(status_label,True,True,0); status_label.show(); ok_button = gtk.Button(";)"موافق main_box.pack_start(ok_button,True,True,0); ok_button.connect('clicked',ClickEvent); ok_button.show(); main_box.show(); window.show(); gtk.main(); : و بعد حذف شيفرة الواجهه الرسوميه يصبح كالتالي # -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; def delete_event(widget,data): False; def destroy(widget,data=None): gtk.main_quit(); def ClickEvent(widget,data=None): global text_entry,status_label; val = text_entry.get_text(); val = float(val); result = (val * 1.8) + 32; result = str(result); status_label.set_text(result); gtk.main(); و يستخدم صندوق من نوع، برنامج تحويل الحراره يستخدم الصناديق،Glade نبدأ الن بالتصميم بإستخدام ، إلى النافذهVBox و بالتالي اول شئ نفعله بعد اضافة النافذه الرئيسيه هو اضافة صندوق من نوعVBox 53
ستجد الصندوق VBoxتحت التصنيف Containersتحت اسم ،Vertical Boxعندما تضعه
على النافذه سيسألك : Gladeكم عدد العناصر المطلوبه ،و كما تعلم اننا نريد اضافة ثلث ادوات واحده
اسفل الخرى ،و بالتالي سوف نحتاج إلى 3عناصر ،بعد اختيارك للعدد 3و الضغط على موافق تجد إنّ النافذه تقسمت إلى 3اقسام ،في القسم الول نضيف مربع نص تجده بإسم Text Entryفي القسم
،Control And Displayالقسم الثاني نضيف نص ) (Labelوالذي تجده بإسم Labelفي نفس
قسم مربع النص ،و اخيراً نضيف الزر في القسم الثالث ،نعدّل الن بعض الخصائص :
نختار الزر و نذهب إلى خصائصه ،و نغيّر قيمة الخاصيّه Labelإلى "موافق" ،بعدها نختار النص )(Label
و نذهب إلى خصائصه و نحذف قيمة الخاصيّه ،Labelنختار الن النافذه الرئيسيه و نذهب إلى خصائصها و نضع قيمة الخاصيّه Window Titleكالتالي " :برنامج التحويل".
نقوم الن بعمليات الربط ،لنربط النافذه الرئيسيه بالحدثين الذين اعتدنا عليهما و هما delete-eventو نضع اسم delete_eventللـ Handlerو الحدث destroyو نضع اسم destroyللـ
،Handlerبعدها نضيف الحدث clickedللزر الذي اضفناه ،و ليكن اسم Handlerهذا الزر هو
ClickEventبنفس اسم الداله المكتوبه مسبقاً ،هذا كل ما نحتاجه من ،Gladeخزّن الملف و ليكن اسمه gui.glade
ننتقل الن إلى النص البرمجي ،قبل كل شئ نستدعي مكتبة : Glade ;import gtk.glade اسفل الداله ClickEventمباشره نبدأ بإستدعاء ملف الواجهه glade.كما تعلمنا مسبقاً ،كالتالي : ;)'g = gtk.glade.XML('gui.glade لو لحظنا نصنا البرمجي سنلحظ أنّنا بحاجه إلى اداتين فقط هما اللتان سنتحكم بهما ،الداة الولى هي مربع النص ،و الثانيه هي النص ) ،(Labelو بالتالي سنستخدم الداله get_widgetمع هاتين الداتين فقط، 54
: كالتالي window = g.get_widget('window1'); text_entry = g.get_widget('entry1'); status_label = g.get_widget('label1'); (-: show فنحن نقوم بأخذ النافذه دائماً من اجل استدعاء الداله،ل تستغرب : كالتالي،ًنربط الن الشارات كما تعلمنا مسبقا signals = {
"ClickEvent" : ClickEvent, "destroy" : destroy, "delete_event" : delete_event}
g.signal_autoconnect(signals); : نُظهر النافذه الرئيسيه window.show(); : و بالطبع ل ننسى الداله الرئيسيه التي لم نحذفها من الشيفره السابقه gtk.main(); (-: !هذا كل شئ : الشيفره كامله # -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; 55
import gtk.glade; def delete_event(widget,data): False; def destroy(widget,data=None): gtk.main_quit(); def ClickEvent(widget,data=None): global text_entry,status_label; val = text_entry.get_text(); val = float(val); result = (val * 1.8) + 32; result = str(result); status_label.set_text(result); g = gtk.glade.XML('gui.glade'); window = g.get_widget('window1'); text_entry = g.get_widget('entry1'); status_label = g.get_widget('label1'); signals = {
"ClickEvent" : ClickEvent, "destroy" : destroy, "delete_event" : delete_event}
g.signal_autoconnect(signals); window.show(); gtk.main();
56
Glade نقل الله الحاسبه إلى.1.8.6 (-: أليس كذلك؟،ً بسيطاGlade كان نقل برنامج التحويل إلى سوف، الشيفره المصدريه هي نفسها السابقه،لننقل الن الله الحاسبه التي كتبناها في احد القسام السابقه : Glade نعدلها من اجل العمل مع
# -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; def delete(widget,data): False; def des(widget,data=None): gtk.main_quit();
def DoMath(widget,data=None): global result,first_number,second_number,operation; f = first_number.get_text(); s = second_number.get_text(); o = operation.get_text(); if f == "" or s == "" or o == "": result.set_text(";)"يرجى تعبئة المعلومات المطلوبه else: f = int(f); s = int(s); if o == "+": r = f + s; elif o == "-": r = f - s; elif o == "*": r = f * s; elif o == "/": r = f / s; else: result.set_text("العمليه التي قمت بإختيارها ;)"غير صحيحه return False; 57
result.set_text(str(r)); win = gtk.Window(gtk.WINDOW_TOPLEVEL); win.set_title(";)"آله حاسبه win.connect('delete_event',delete); win.connect('destroy',des); main_box = gtk.VBox(False,0); win.add(main_box); box1 = gtk.HBox(False,0); first_number = gtk.Entry(); box1.pack_start(first_number,True,True,0); first_number.show(); operation = gtk.Entry(); box1.pack_start(operation,True,True,0); operation.show(); second_number = gtk.Entry(); box1.pack_start(second_number,True,True,0); second_number.show(); main_box.pack_start(box1,True,True,0); box1.show(); button = gtk.Button(";)"أظهر الناتج button.connect('clicked',DoMath); main_box.pack_start(button,True,True,0); button.show(); result = gtk.Label(); main_box.pack_start(result,True,True,0); result.show(); main_box.show(); win.show(); gtk.main(); : و اذا حذفنا شيفرة الواجهه تكون الشيفره كالتالي،هذه هي الشيفره
58
# -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; def delete(widget,data): False; def des(widget,data=None): gtk.main_quit(); def DoMath(widget,data=None): global result,first_number,second_number,operation; f = first_number.get_text(); s = second_number.get_text(); o = operation.get_text(); if f == "" or s == "" or o == "": result.set_text(";)"يرجى تعبئة المعلومات المطلوبه else: f = int(f); s = int(s); if o == "+": r = f + s; elif o == "-": r = f - s; elif o == "*": r = f * s; elif o == "/": r = f / s; else: result.set_text("العمليه التي قمت بإختيارها ;)"غير صحيحه return False; result.set_text(str(r)); gtk.main(); القسم، كانت تنقسم إلى ثلث اقسام،Glade كيف كانت واجهة الله الحاسبه حتى نصممها مع،لنتذكر الن
و القسم الخير،" القسم الثاني يحتوي على زر مكتوب عليه "اظهر الناتج،الول يحتوي على ثلث مربعات نص .كان يحتوي على نص لظهار الناتج فيه
59
نبدأ مع ،Gladeو كما تعودنا نضيف دائماً النافذه ،و لنّ برنامجنا يحتوي على 3اقسام عموديه فإننا سوف
نستخدم الصندوق VBoxو نحدد الرقم ،3القسم الول يحتوي على ثلث مربعات نص بجانب بعضهن ،هذا
يعني إننا بحاجه إلى صندوق داخل هذا القسم و هذا الصندوق من نوع HBoxتجده في القسم
Containersبإسم ،Horizontal Boxو نختار الرقم 3لننا بحاجه إلى ثلث اقسام افقيه ،سوف
تجد ان القسم الول انقسم إلى ثلث اقسام افقيه عن طريق خطوط ظهرت ،نضيف الن مربع نص في كل قسم
افقي جديد ،نذهب الن إلى القسم العمودي الثاني و نضيف زراً جديداً ،اخيراً نضيف النص ) (Labelفي آخر قسم.
نغيّر عنوان النافذه اولً إلى "آله حاسبه" و نربطها بالحدثين delete-eventو destroyو لتكن اسماء الـ Handlerنفس اسماء الدوال delete ،و .des
نغيّر النص في اعلى الزر إلى "اظهر الناتج" و نربط الزر بالحدث clickedو نسمي الـ Handlerبإسم الداله .DoMath
نحذف الن النص الموجود في اداة النص. هذا كل شئ! ،خزّن العمل الن و سم الملف بالسم ،guiننتقل الن إلى النص البرمجي ،نستدعي المكتبه اولً :
;import gtk.glade ثم ملف الواجهه : ;)'g = gtk.glade.XML('gui.glade نستخدم الداله get_widgetمع مربعات النص الثلثه و نص اظهار النتائج بالضافه إلى النافذه الرئيسيه : ;)'window = g.get_widget('window1 ;)'first_number = g.get_widget('entry1 ;)'operation = g.get_widget('entry2 ;)'second_number = g.get_widget('entry3 60
result = g.get_widget('label1'); : signal_autoconnect نُنشئ مصفوفة الشارات و نربطها بإستخدام الداله signals = {
"DoMath" : DoMath, "des" : des, "delete" : delete}
g.signal_autoconnect(signals); : اخيراً ل ننسى اضافة السطرين window.show(); gtk.main(); (-: المور بسيطه أليست كذلك؟،Glade هذا كل شئ! تعمل الن الحاسبه الن مع : الشيفره كامله
# -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; import gtk.glade; def delete(widget,data): False; def des(widget,data=None): gtk.main_quit(); def DoMath(widget,data=None): global result,first_number,second_number,operation; f = first_number.get_text(); s = second_number.get_text(); o = operation.get_text();
if f == "" or s == "" or o == "": result.set_text(";)"يرجى تعبئة المعلومات المطلوبه else: 61
f = int(f); s = int(s); if o == "+": r = f + s; elif o == "-": r = f - s; elif o == "*": r = f * s; elif o == "/": r = f / s; else: result.set_text("العمليه التي قمت بإختيارها ;)"غير صحيحه return False; result.set_text(str(r)); g = gtk.glade.XML('gui.glade'); window = g.get_widget('window1'); first_number = g.get_widget('entry1'); operation = g.get_widget('entry2'); second_number = g.get_widget('entry3'); result = g.get_widget('label1'); signals = {
"DoMath" : DoMath, "des" : des, "delete" : delete}
g.signal_autoconnect(signals); window.show(); gtk.main(); . مقارنه بالشيفره السابقهGlade لحظ مدى بساطة شيفرة
62
الفصل الثاني مدخل إلى SQLite
63
.2.1مقدمه هناك الكثير من البرامج التي تقوم بإدارة البيانات ،حيث تأخذ مدخلت من المستخدم و تخزنها من اجل
الستخدام فيما بعد ،و تتيح هذه البرامج للمستخدم التعديل على البيانات المُخزنه او حتى التخلص منها ،مثلً البرامج التي تُستخدم في الشركات و التي تديرُ الشركةُ من خللها بياناتَ الموظفين ،كذلك برامج القواميس
التي تعطي معاني الكلمات ،تستخدم هذه البرامج مُحركات قواعد بيانات لتخزين البيانات و التعامل معها ،تقدّم لغة Pythonالعديد من مُحرّكات قواعد البيانات ،و لكننا سوف نستخدم المُحرّك SQLiteنظراً لبساطته و
سهولته ،يستخدم هذا المُحرّك لغة SQLمن اجل التعامل مع البيانات ،أفترض هنا ان لديك إلمام بلغة
،SQLاذا لم تكن تعرف هذه اللغه انصحك بمراجعة الكتب المُتخصصه او مواقع النترنت فهي مليئه بدروس SQLالتي تشرح التفاصيل او مراجعة مؤلفنا الخاص بلغة ،SQLاستخدم اي محرّك بحث و سوف تجد
العديد من المواقع على الشبكه.
بالطبع قبل البدأ لبد من تثبيت مكتبة SQLiteعلى حاسوبك ،سوف تحتاج إلى المكتبه PySQLite فراجع موقعها للحصول عليها و على كيفية تثبيتها.
64
.2.2الدوال الساسيه تقدّم المكتبه PySQLiteمجموعه من الدوال ،و لكننا لن نحتاجها جميعها ،سوف نستخدم بعض الدوال
الساسيه التي يحتاجها اي برنامج يعتمد على ،PySQLiteيمكنك كالعاده القراءه فيما بعد اكثر حول الدوال التي تقدمها PySQLiteو قد تجد دوال تهمك.
نبدأ بالدالتين connectو ،cursorو هما اهم دالتين ،الداله الولى و هي connectو تُستخدم
للتصال بقاعدة البيانات ،قاعدة البيانات عباره عن ملف يمكنك تخزينه في اي مكان تشاء ،و تمرر مسار هذا الملف إلى الداله ،connectاذا لم تجد الداله connectملف قاعدة البيانات في المسار المطلوب ستقوم
بإنشاء ملف قاعدة البيانات.
الداله الثانيه هي cursorوهي داله اساسيه جداً ،حيث تأخذ مؤشر SQLiteو تُعيده إلى متغير ،سوف
نستخدم هذا المتغير فيما بعد في جميع العمليات الخرى ،مثل عملية إنشاء الستعلمات او اخذ ناتج عملية الستعلمات ،و هذا مثال لستخدام هاتين الدالتين :
;from pysqlite2 import dbapi2 as sqlite ;)"connect = sqlite.connect("DB ;)(cur = connect.cursor اولً نستدعي مكتبة SQLiteفي السطر الول ،في السطر الثاني نتصل بقاعدة البيانات التي اسمها ،DBبما
اننا لم نمرر مسار معين و قمنا بوضع اسم قاعدة البيانات فحسب هذا يعني ان قاعدة البيانات DBمُخزنه في نفس المجلد الذي يحتوي على ملف بايثون ،السطر الخير يأخذ المؤشر و يخزنه في المتغير ،curبعدها سنستخدم المتغير curدائماً من اجل العمليات الخرى.
نعرّج على الدالتين executeو ،fetchallبالنسبه للداله الولى وهي executeنستخدمها لتنفيذ 65
الستعلمات بلغة ،SQLنمرر نص الستعلم كبارامتر للداله ،سنستخدم هذه الداله فيما بعد من اجل إنشاء
الجدوال و من اجل اخذ البيانات و التعديل عليها.
الداله الثانيه وهي fetchallنستخدمها دائماً بعد عمل استعلم من نوع ،SELECTبحيث نحصل على
مصفوفه تحتوي على النتائج من خلل الداله .fetchall
اخيراً الداله commitو نستخدمها اذا قمنا بأي عملية تغيير في قاعدة البيانات ،مثلً اضفنا بيانات او حدّثنا
بيانات او انشأنا جدول جديد ،اذا أجرينا اي تغيير في قاعدة البيانات و لم نستخدم هذه الداله ستضيع جميع التعديلت بمجرد إغلق البرنامج.
سوف نرى امثله واقعيه لجميع هذه الدوال عندما نكتب مشروعنا في هذا الكتاب إن شاء ال .(-:
66
.2.3المثال الول :برنامج لتخزين السماء و عرضها اصبح لدينا الن إلمام بكتابة برامج ذات واجهه رسوميه بإستخدام ،GTKو لدينا إلمام مسبق بلغة ،SQLو
تعرفنا على الدوال الساسيه التي تخص مكتبة ،SQLiteاذاً نحن الن جاهزون لكتابة برنامج رسومي يعتمد
على قواعد البيانات لتخزين معلوماته ،سوف نبدأ في مثالنا الول هذا و سوف يكون بسيط جداً ،وظيفته هي
تخزين مجموعه من السماء و عرضها في قائمه ،هذا كل ما هنالك.
نبدأ اولً بتخطيط جداول قواعد البيانات ،نحتاج إلى جدول واحد فحسب ،نخزّن في هذا الجدول السماء و
اسم هذا الجدول هو ،namesاما بالنسبه للحقول نحتاج إلى حقلين ،الحقل الول هو idو الذي يخزّن
رقم تعريفي مميز ،امّا الحقل الثاني هو nameوهو السم الذي نود تخزينه.
الن لنتحدث عن واجهة البرنامج ،لننا نريد البرنامج بسيطاً لن يكون هناك عدّة نوافذ ،اعني اننا لن نستخدمَ نافذة
منفصلة من اجل إضافة اسم جديد ،و لكن نستخدم النافذه الرئيسيه و نضع فيها قائمة السماء و في اسفلها نضع
الدوات اللزمه لضافة اسم جديد إلى قواعد البيانات ،هذا يعني اننا سوف نقسّم واجهة البرنامج إلى خمس اقسام بإستخدام صندوق من نوع ،VBoxالقسم الول يحتوي على قائمه من نوع ،TreeViewالقسم
الثاني يحتوي على نص ) (Labelو نغيره إلى "إضافة كلمة جديده" ،الصندوق الثالث يحتوي على مربع نص
يتم كتابة السم فيه ،اما الصندوق الرابع فيحتوي على زر مكتوب عليه "موافق" ،و اخيراً الصندوق الخامس يحتوي على نص فارغ يعرض الحاله ،مثلً اذا نجحت الضافه يتم عرض ذلك في هذا النص ،تكون واجهة البرنامج كالتالي :
67
صممها ثم قم بتخزين الملف بإسم ،guiل ننسى الن ربط النافذه بالدالتين الساسيتين ،للحدث
destroyنستخدم داله بإسم desاما الحدث delete-eventنستخدم داله بإسم ،deleteبعدها
نربط الزر بالحدث clickedبالداله ،AddNewNameنخزّن الواجهه الن ثم نعرّج على الملف
البرمجي ،نكتب الساسيات التي اعتدنا دائماً على كتابتها :
*# -*- coding: utf-8 -;import pygtk ;)'pygtk.require('2.0 ;import gtk ;import gtk.glade ;from pysqlite2 import dbapi2 as sql def delete(widget,data): ;False def des(widget,data=None): 68
;)(gtk.main_quit ;)'g = gtk.glade.XML('gui.glade مناداة المكتبات و كتابة الدالتين deleteو desثم إستدعاء ملف الواجهه و تكليف المتغير gبه ،نستدعي
الن الدوات التي سنستخدمها داخل النص البرمجي ،سنحتاج إلى التعامل مع القائمه و مع مربع النص و مع النص الثاني الذي يبين الحاله ،و بالتالي نستدعي هذه الدوات الثلثه.
;)'window = g.get_widget('window1 ;)'tree = g.get_widget('treeview1 ;)'entry = g.get_widget('entry1 ;)'status = g.get_widget('label2 نربط الحداث : "AddNewName" : AddNewName, "des" : des, }"delete" : delete
{ = signals
;)g.signal_autoconnect(signals قبل كتابة الداله AddNewNameلبد من عمل هام جداً ،وهو إنشاء قاعدة البيانات و الجدول الذي
سوف نستخدمه ،من الفضل إنشاء ملف برمجي جديد يحتوي على شيفرة إنشاء الجدول ،نسمّي هذا الملف بإسم database_create.pyو يكون محتواه كالتالي ،نستدعي اولً مكتبة : SQLite
*# -*- coding: utf-8 -;from pysqlite2 import dbapi2 as sql ثم نتصل بقاعدة بيانات إسمها sqlite_dbبإستخدام الداله connectالمشروحه سلفاً : ;)'con = sql.connect('sqlite_db
69
نخزّن المؤشر في المتغير : cur ;)(cur = con.cursor ثم نمرر جملة SQLالخاصه بإنشاء جدولنا المطلوب إلى الداله executeو نستخدم printقبلها حتى نتعرف على حالة النشاء :
;)'))print cur.execute('CREATE TABLE names (id int,name varchar(255 هذا كل شئ ،سوف يكون الملف كالتالي : *# -*- coding: utf-8 -;from pysqlite2 import dbapi2 as sql ;)'con = sql.connect('sqlite_db ;)(cur = con.cursor ;)'))print cur.execute('CREATE TABLE names (id int,name varchar(255 نخزّن الملف بإسم database_create.pyو نقوم بتشغيله ،بعد تشغيل الملف بنجاح نلحظ أنّ هناك ملف جديد إسمه sqlite_dbموجود في مجلد برنامجنا ،هذه هي قاعدة البيانات التي تحتوي على الجدول الذي سوف نتعامل معه فيما بعد.
نعود الن إلى الملف الساسي للبرنامج وهو ،main.pyقبل كتابة الداله AddNewNameيجب أنْ
نتصلَ بقاعدة البيانات كما فعلنا تماماً في الملف ،database_create.pyنذهب إلى الملف main.pyو قبل السطر :
;)'g = gtk.glade.XML('gui.glade وهو السطر الخاص بإحضار واجهة البرنامج ،نضيف السطرين الخاصّين بالتصال في قاعدة البيانات و اخذ مكان 70
المؤشر : ;)'con = sql.connect('sqlite_db ;)(cur = con.cursor لحظ ان اسم قاعدة البيانات هو نفسه ،sqlite_dbنعود الن إلى الداله ،AddNewNameتقوم هذه
الداله بأخذ المحتوى الموجود داخل مربع النص ،و التحقق اذا كان المحتوى فارغاً اي ان مستخدم البرنامج
لم يكتب شئاً داخل مربع النص و ضغط على الزر تُعرض له رساله في السفل تخبره بإنه عليه تعبئة
المعلومات ،اما اذا كان السم مكتوباً سوف نقوم بإضافة السم إلى قاعدة البيانات ،نكتب دالتنا في اسفل الداله ،desنبدأ كالتالي :
def AddNewName(widget,data=None): في داخل الداله سنتعامل مع مربع النص الذي سنأخذ منه السم المطلوب تخزينه ،وسنتعامل مع النص الذي
نعرض الحاله من خلله ،كذلك نستخدم قواعد البيانات داخل الداله اذاً نحن بحاجه إلى المتغيرين الخاصّين بقواعد البيانات و هما conو ،curو بالتالي سوف نستخدم globalكما تعودنا مع المتغيرات الربعه entryو statusو conو curكالتالي :
;global entry,status,con,cur نستخدم الن الداله get_textمن اجل اخذ المحتوى الموجود في صندوق النص و نخزّن القيمه في متغير نسميه : name
;)(name = entry.get_text نتحقق الن ،اذا كانت القيمه فارغه نطبع رساله للمستخدم : if name == '': ;)'يرجى كتابة السم المطلوب'(status.set_text 71
اذا لم تكن فارغه نقوم بإضافة السم في قاعدة البيانات ،اولً نستخدم الداله executeالتي تأخذ منّا جملة SQLالخاصه بإضافة البيانات إلى قاعدة البيانات ،تكون دائماً دالة executeضمن المتغير الذي يخزّن
المؤشر و في حالتنا هذه المتغير هو ،curاذاً نكتب اولً السطر الخاص بإضافة البيانات :
insert = cur.execute('INSERT INTO names(id,name) VALUES(NULL,"' + name ;)')"' + عندما نقوم بأي تعديل على البيانات ،سواء اضفنا او حذفنا او حدّثنا البيانات عن طريق الداله ،execute
يجب علينا استدعاء الداله commitحتى نسجّل هذه التغييرات بشكل فعلي في قاعدة البيانات ،فإذا قمنا
مثلً بإضافة قيمه جديده في قاعدة البيانات و لم نقم بإستدعاء commitبعدها في حال إغلق البرنامج تذهب
هذه المعلومات التي اضفناها ول تُسجّل في قاعدة البيانات ،و بالتالي انتبه دائماً إلى هذه النقطه حتى ل تضيع المعلومات ،تقع الداله دائماً تحت متغير التصال و اسم هذا المتغير في حالتنا هو : con
;)(check = con.commit الداله commitتُرجع Noneفي حال نجاحها ،و بالتالي نتحقق من نجاحها و في حال نجاحها نخبر
المستخدم بذلك كالتالي :
if check == None: ;)'تم اضافة السم بنجاح'(status.set_text و من اجل التسهيل على المستخدم حتى يتمكن من اضافة اسماء متعدده اسم تلو الخر نقوم بتفريغ محتوى مربع النص بعد نجاح العمليه كالتالي :
;)''(entry.set_text
إنتهينا الن من الداله AddNewNameو شيفرتها كالتالي : def AddNewName(widget,data=None): ;global entry,status,con,cur ;)(name = entry.get_text 72
if name == '': ;)'يرجى كتابة السم المطلوب'(status.set_text else: )insert = cur.execute('INSERT INTO names(id,name ;)')"' VALUES(NULL,"' + name + ;)(check = con.commit if check == None: ;)'تم اضافة السم بنجاح'(status.set_text ;)''(entry.set_text انهينا الن جزء هام في البرنامج ،تبقى الجزء الثاني وهو عرض قائمه السماء ،كما تعلم قمنا بإضافة الداة
TreeViewمن اجل عرض قائمة السماء ،في الحقيقه تستخدم هذه الداة في عملها اسلوب يُسمّى بـ
Model-View-Controllerو نُطلق عليه MVCاختصاراً ،و حتى نتمكن من التعامل بشكل
صحيح مع هذه الداة لبد ان نفهم هذا السلوب.
يعتمد هذا السلوب على تقسيم العمل إلى ثلث اجزاء ،الجُزء الول هو Modelو يتخص هذا الجزء
بالبيانات بمعنى ان البيانات بأنواعها تُخزن داخل هذا الجزء ،اما الجزء الثاني وهو Viewفتقع عليه مسؤولية عرض هذه البيانات المُخزنه في ،Modelاما الجزء الثالث وهو Controllerفهو مسؤول عن معالجة
الحداث ،الجزءان الول و الثاني هما الهم بالنسبه لنا الن ،اذاً يمكننا الستنتاج انّ الهدف الساسي من
اسلوب MVCهو فصل البيانات عن العرض ،الجدير بالذكر انّ اسلوب MVCاحد الساليب الهامه و
الشهيره في هندسة البرمجيات المُطبقه بشكل كبير في العديد من الماكن ،يمكنك التوسع في البحث حتى تتعرف على المزيد عن هذا السلوب و المشاكل المحلوله بواسطته.
نعود للداة ،TreeViewكما ذكرنا ان هذه الداة تستخدم السلوب ،MVCتُقدّم مكتبة GTKفئه
اسمها TreeViewColumnهذه الفئه هي المسؤوله عن جزء العرض ،Viewكما انها تُقدّم فئتين
ListStoreو TreeStoreو يمكن استخدام احد هاتين الفئتين كـ Modelلتخزين البيانات التي
ستُعرض في القائمه ،تُستخدم الفئه الولى من اجل تخزين البيانات التي ستُعرض على شكل قائمه بسيطه ،اما
الفئه الثانيه فتُستخدم من اجل تخزين البيانات التي ستُعرض على شكل شجره او بالحرى التي ستُعرض على
شكل قائمه تحتوي على هرميات و بيانات تقع تحت بيانات اخرى ،و بالطبع لننا سوف نطبع السماء على شكل 73
قائمه بسيطه يجب علينا استخدام الفئه .ListStore نبدأ اولً بكتابة جزء العرض ) (Viewمع الداة ،TreeViewColumnكما ذكرنا ان هناك صنف
بنفس السم و بالتالي من اجل البدء بجزء العرض نقوم بإنشاء كائن من هذه الداة و نُكلّف متغير ليكن اسمه colبها كالتالي :
;)',gtk.CellRendererText(),text=0السم'(col = gtk.TreeViewColumn نُلحظ انّ البارامتر يحتوي على عنوان العمود الذي يكون في العلى ،في كُل مره نُنشئ كائن جديد من الصنف
TreeViewColumnفإننا بهذه الطريقه نقوم بإنشاء عمود جديد ضمن قائمتنا ،في حالتنا هذه نحن بحاجه
إلى عمودٍ واحدٍ فقط لننا نريد عرض السماء فقط ،لو كنّا نريد عرض المزيد من المعلومات مثلً السم و رقم الهاتف و البريد اللكتروني ،سنحتاج إنشاء ثلث كائنات لتُنشئ بدورها ثلث اعمده.
نعود للسطر السابق ،البارامتر الثاني غالباً ما يكون ثابتاً ،اما البارامتر الثالث تتضح فائدته عندما تكون لدينا عدّة
اعمده حيث يأخذ العمود الول الرقم 0و العمود الثاني الرقم 1و هكذا ،لن نفصّل حالياً هذا الموضوع لننا
سوف نفصّله فيما بعد ،في الوقت الحالي لن لدينا عمود واحد و لن هذا العمود هو الول و بالتالي سوف يأخذ الرقم .0
بعدما انشأنا العمود وهو الجزء المُسمى بـ Viewبإسلوب MVCالذي تستخدمه الداة TreeView
يجب علينا الن إضافة هذا الجزء إلى الكائن المطلوب ،لمزيد من التوضيح نفرض ان واجهة برنامجنا تحتوي على قائمتين ،هذا يعني أنّ لدينا كائنان من الصنف ،TreeViewو هذان الكائنان بحاجه إلى الجزء Viewالذي ننشئه من خلل الصنف ،TreeViewColumnلنفرض ان القائمه الولى تحتوي على عمود واحد
اسمه "البريد اللكتروني" كذلك بالنسبه للقائمه الثانيه لنفرض انها تحتوي على قائمه واحده وهي "رقم الهاتف" ،ببساطه يمكننا إنشاء كائنين من الصنف TreeViewColumnكالتالي :
البريد'(email_col = gtk.TreeViewColumn ;)',gtk.CellRendererText(),text=0اللكتروني 74
رقم'(phone_col = gtk.TreeViewColumn ;)',gtk.CellRendererText(),text=0الهاتف و قبلها كما تعلم لبد ان يكون لدينا كائنين من الصنف ،TreeViewلنفرض ان الول اسمه first_tree و الثاني اسمه ،second_treeو لكن كيف يمكن للبرنامج ان يعرف انّ الكائن email_colيخص
القائمه الولى ول يخص القائمه الثانيه؟ حسناً ماذا لو قررنا جعل العمود الول هو الذي يحتوي على عمود "رقم الهاتف" و الثاني يحتوي على عمود "البريد اللكتروني" ،ببساطه الصنف TreeViewيحتوي على
الداله append_columnهذه الداله تحدد الجزء Viewيخص اي كائن ،TreeViewتستقبل
هذه الداله بارامتر وهو اسم الكائن الخاص بالجزء ،Viewو بالتالي حتى نضع هذين العمودين كلٌ في قائمته نستخدم هذه الداله كالتالي :
;)first_tree.append_column(email_col ;)second_tree.append_column(phone_col نعود لبرنامجنا الساسي ،من الشرح السابق نستنتج اننا يجب ان نُكلّف الكائن colبالقائمه التي يُمثلها الكائن treeكالتالي :
;)tree.append_column(col هكذا انتهينا من الجزء ،Viewتبقّى جزء .Model
كما اسلفنا انّ الجزء Modelهو الخاص بتخزين البيانات ،تُقدّم مكتبة PyGTKصنفين احدهما خاص
بالقوائم البسيطه العاديه التي استخدمناها في هذا البرنامج ) (ListStoreو الخر خاص بالقوائم المُركبه و المُعقده و التي يُطلق عليها اسم الشجار ) ،(TreeStoreبالطبع سوف نستخدم الصنف ListStore حتى نُنشئ الجزء Modelكالتالي :
;)store = gtk.ListStore(str لنشرح الن نظام البارامترات هنا ،نلحظ في برنامجنا هذا انّ الجزء Viewيحتوي على عمود واحد فقط وهو عمود السم ،و البيانات التي ستقع تحت هذا العمود حتماً ستكون نصوص )او stringكما نسميها 75
بالبرمجه( ،لحظ الن البارامتر الذي مررناه عندما نادينا الصنف ،ListStoreقمنا بتمرير بارامتر واحد لن
لدينا عمود واحد و لن هذا العمود سوف تقع تحته بيانات من نوع النص فهذا البارامتر يوضّح نوع هذه
المعلومات عن طريق إرسال اسم النوع ،حسناً ماذا لو اضفنا عمودنا الثاني و المعلومات التي تقع هي عمر
صاحب السم ،في هذه الحاله سوف نحتاج لتمرير بارامترين الول يخص العمود الول الخاص بالسماء و
بالتالي سوف نرسل strاما الثاني فيخص العمود الثاني الخاص بالعمار و بالتالي سوف نرسل ) intرقم صحيح( و هكذا في كل مره نستخدم فيها الصنف TreeViewلعرض قوائمنا.
هل تتذكر ما فعلناه مع الكائن الذي يمثله المتغير col؟ حيث قُمنا بتكليف الكائن colإلى القائمه التي يمثلها المتغير treeعن طريق السطر :
;)tree.append_column(col لنفس السبب الذي شرحناه يجب علينا كذلك تكليف الكائن storeإلى القائمه الرئيسيه و ذلك عن طريق الداله set_modelكالتالي :
;)tree.set_model(store إنتهينا الن من إعداد الجزء ،Modelنُريد الن تعبئة البيانات التي سوف تُعرض في القائمه ،عندما يفتح
المُستخدم البرنامج لبد ان تظهر قائمة السماء المُخزنه في قواعد البيانات و يمكنه بعدها اضافة المزيد إن اراد ذلك.
كما تعلم لخذ قائمة السماء في قاعدة البيانات سوف نحتاج إلى استخدام مكتبة ،PySQLiteنستخدم اولً الداله executeلرسال الستعلم ،نحتاج إلى اخذ جميع البيانات المُخزنه في جدول namesو بالتالي
لبد من صياغة إستعلم لتحقيق هذا المطلب بإستخدام لغة : SQL
;)"get_names_query = cur.execute("SELECT * FROM names
76
نستقبل الن ناتج الستعلم عن طريق الداله fetchallو نُخزنها في ،namesالناتج عباره عن مصفوفه : ;)(names = cur.fetchall اذا كانت هناك معلومات فعلً فإن حجم namesيجب ان يكون اكبر من ،0و بالتالي لبد ان نتحقق اذا
كان هناك معلومات فعلً ام قاعدة البيانات فارغه كالتالي :
if len(names) > 0: بعدما تأكدنا ان هناك معلومات فعلً نقوم بقراءة المصفوفه ووضع محتواها داخل الجزء ،Modelهذه
المصفوفه ثنائية البعد ،هناك عدّة مُدخلت و التي تُمثل مُدخلت قواعد البيانات وهو عدد متغير يعود إلى
البيانات التي اضافها المستخدم ،و هناك حقلن وهما idو nameالموجودان في الجدول ،namesما
نريده هو قراءة هذه المُدخلت كامله و طباعة محتوى الحقل nameمنها و يكون ذلك كالتالي :
;x = 0 while x < len(names): ;)]]store.append([names[x][1 ;x += 1 كما تلحظ أنشأنا حلقه تعتمد على حجم المصفوفه namesو تقرأ البيانات ،و استخدمنا الداله appendالتي يقدمها الصنف ListSoreلضافة البيانات في داخله.
انتهينا الن بشكل كامل من الجزء Modelو الشيفره كامله لهذا الجزء : ;)store = gtk.ListStore(str ;)tree.set_model(store ;)"get_names_query = cur.execute("SELECT * FROM names ;)(names = cur.fetchall ;print names if len(names) > 0: 77
;x = 0 while x < len(names): ;)]]store.append([names[x][1 ;x += 1 هكذا نكون قد انتهينا من البرنامج و لكن هناك مشكله واحده ،ل يتوقف برنامجنا اذا لم يتم حلّها و لكنه
سيتحسن في حال حلّها ،جرّب اضافة اسم جديد من خلل البرنامج ،لحظ انه على الرغم من النجاح في
تخزين السم بقاعدة البيانات و لكنه ل يظهر في القائمه مباشره بعد النجاح في اضافته ،و لكن يجب علينا إغلق البرنامج ثم تشغيله مره اخرى حتى يظهر السم الجديد ضمن القائمه.
الحل كما خمّنت بسيط جداً ،نتوجّه إلى الداله AddNewNameالخاصه بإضافة السماء إلى قاعدة
البيانات ،نُضيف المتغير storeضمن قائمة المتغيرات التي تقع بعد globalحتى يصبح السطر كالتالي : ;global entry,status,con,cur,store
نتوجّه الن إلى الشرط الذي يتحقق من نجاح اضافة السم وهو : if check == None: ;)'تم اضافة السم بنجاح'(status.set_text ;)''(entry.set_text نستخدم في نهايته الداله appendالتي تحدثنا عنها منذ قليل لضافة السم إلى القائمه كالتالي : ;)]store.append([name ستصبح الشيفره بهذا الشكل : if check == None: ;)'تم اضافة السم بنجاح'(status.set_text ;)''(entry.set_text ;)]store.append([name
78
حسناً و،جرّب الن و لحظ ان السم يُضاف مباشره إلى القائمه بدون الحاجه إلى إغلق البرنامج و تشغيله (-: اخيراً انتهينا من هذا المثال الحمدل : الشيفره الكامله للبرنامج # -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; import gtk.glade; from pysqlite2 import dbapi2 as sql; def delete(widget,data): False; def des(widget,data=None): gtk.main_quit(); def AddNewName(widget,data=None): global entry,status,con,cur,store; name = entry.get_text(); if name == '': status.set_text(';)'يرجى كتابة السم المطلوب else: insert = cur.execute('INSERT INTO names(id,name) VALUES(NULL,"' + name + '")'); check = con.commit(); if check == None: status.set_text(';)'تم اضافة السم بنجاح entry.set_text(''); store.append([name]); con = sql.connect('sqlite_db'); cur = con.cursor(); g = gtk.glade.XML('gui.glade'); window = g.get_widget('window1'); tree = g.get_widget('treeview1'); entry = g.get_widget('entry1'); 79
status = g.get_widget('label2'); signals = {
"AddNewName" : AddNewName, "des" : des, "delete" : delete}
g.signal_autoconnect(signals); ###### col = gtk.TreeViewColumn(''السم,gtk.CellRendererText(),text=0); tree.append_column(col); ### store = gtk.ListStore(str); tree.set_model(store); get_names_query = cur.execute("SELECT * FROM names"); names = cur.fetchall(); if len(names) > 0: x = 0; while x < len(names): store.append([names[x][1]]); x += 1; ###### window.show(); gtk.main();
80
الفصل الثالث إنشاء مشروع
81
.3.1برنامج إدارة بيانات موظفين في شركه ما لنبدأ الن ببناء مشروع كامل نستخدم فيه ما تعلمناه ،لحظ انّ هذا الفصل يعتمد بشكل كبير على شرح المثال
الول الموجود في الفصل الثاني و بالتالي تأكد من فهمك الجيّد له ،البرنامج ببساطه لدارة بيانات موظفين ،يشابه إلى حد كبير المثال الول الذي كتبناه ،و لكن هناك المزيد من البيانات للتعامل معها و هي اسم الموظف ،عمره، راتبه ،و سوف يكون للبرنامج اكثر من نافذه بعكس المثال الول الذي كان يحتوي على نافذه واحده تعرض
البيانات و تتم الضافه من خللها ،في هذا المشروع تحتوي النافذه الرئيسيه على قائمه بأسماء الموظفين و
اعمارهم و رواتبهم ،في اسفل النافذه ثلث ازرار الول "اضافة موظف" و يفتح نافذه جديده تطلب بيانات
الموظف الجديد ،اما الزر الثاني "تحديث البيانات" بحيث يختار المستخدم الموظف المطلوب من القائمه ثم
يضغط على هذا الزر لتحرير بيانات الموظف ،اما الزر الخير "حذف الموظف" الذي يعمل بنفس طريقة الزر السابق و لكنه يحذف الموظف بدلً من تحرير معلوماته.
اربط الحدثين الساسيين و هما delete-eventمع الداله deleteو destroyمع الداله ،desو الن اربط احداث الزرار ،اربط الحدث clickedلزر اضافة الموظفين مع داله بإسم
AddNewEmpاما زر التحديث فأربط الحدث clickedمع داله بإسم ،UpdateEmpاما
الزر الخير فأربط الحدث clickedمع داله بإسم ،DeleteEmpخزّن الملف في مجلد البرنامج و
ليكن اسمه gui.glade
نشرع الن بالشيفره ،نبدأ اولً مع الملف database_create.pyو الذي يُنشئ جدول قاعدة البيانات كما مر عليك عندما انشأنا المثال الول :
*# -*- coding: utf-8 -;from pysqlite2 import dbapi2 as sql ;)'con = sql.connect('employees ;)(cur = con.cursor print cur.execute('CREATE TABLE names (id int,name varchar(255),salary 82
;)')int,age int لحظ اننا انشأنا قاعدة بيانات اسمها employeesتحتوي على جدول به اربع حقول idو nameو
salaryو ageو ل تخفى عليك انواعها و الهدف منها ،نشغّل الن الملف database_create.py
حتى يتم إنشاء قاعدة البيانات.
نبدأ بملفنا البرمجي ،main.pyنبدأ بمناداة المكتبات : *# -*- coding: utf-8 -;import pygtk ;)'pygtk.require('2.0 ;import gtk ;import gtk.glade ;from pysqlite2 import dbapi2 as sql و دائماً الدالتين deleteو (-: des def delete(widget,data): ;False def des(widget,data=None): ;)(gtk.main_quit نتصل بقاعدة البيانات : ;)'con = sql.connect('employees ;)(cur = con.cursor نستدعي الملف gui.gladeو نُكلّف المتغير gبه : ;)'g = gtk.glade.XML('gui.glade سوف نحتاج للتعامل مع جميع الدوات في داخل الشيفره البرمجيه و بالتالي نحتاج للداله : get_widget 83
window = g.get_widget('window1'); tree = g.get_widget('treeview1'); add_emp = g.get_widget('button1'); update_emp = g.get_widget('button2'); delete_emp = g.get_widget('button3'); add_emp المتغير، مسؤول عن القائمهtree المتغير، مسؤول عن النافذه الرئيسيهwindow المتغير
المتغير،" مسؤول عن زر "تحديث البياناتupdate_emp المتغير،"مسؤول عن زر "اضافة موظف ." مسؤول عن زر "حذف موظفdelete_emp
: نربط الشارات signals = {
"AddNewEmp" : AddNewEmp, "UpdateEmp" : UpdateEmp, "DeleteEmp" : DeleteEmp, "des" : des, "delete" : delete}
g.signal_autoconnect(signals); : نضيف في نهاية الملف window.show(); gtk.main(); العمود الول خاص، نبدأ بإنشاء العواميد في قائمتنا كما تعلمنا عندما انشأنا المثال الول،قبل السطران السابقان : بالسم
col1 = gtk.TreeViewColumn(''السم,gtk.CellRendererText(),text=0); tree.append_column(col1);
84
كما ذكرنا ،البارامتر الثالث يحتوي على القيمه 0بالنسبه للعمود الول و كلما زاد عمود نزيد بالرقم ،هذا يعني
ان العمود الثاني ستكون قيمة البارامتر الثالث الخاص به هي 1و الثالث يأخذ القيمه 2و هكذا ،نضيف الن العمود الثاني :
;)',gtk.CellRendererText(),text=1الراتب'(col2 = gtk.TreeViewColumn ;)tree.append_column(col2 ثم العمود الثالث : ;)',gtk.CellRendererText(),text=2العمر'(col3 = gtk.TreeViewColumn ;)tree.append_column(col3 انتهينا الن من العواميد او بالحرى الجزء ،Viewلنبدأ بجزء التخزين ،Modelبالطبع سوف نستخدم الصنف ListStoreلن قائمتنا عاديه و بسيطه :
;)store = gtk.ListStore(str,int,int اذا قارنا هذا السطر بالسطر المكتوب بالمثال الول نلحظ اننا اضفنا بارامترين و بالتالي اصبحت لدينا ثلث
بارامترات مُمرره ،اما المثال الول احتوى فقط على بارامتر واحد ،لبد و انك تعرف السبب لننا شرحناه
سابقاً ،(-:السبب هو انه لدينا ثلث عواميد هنا و بالتالي كل بارامتر يخص عمود من هذه العواميد و يحدد
نوعية البيانات التي تقع تحته ،و السطر السابق يُخبر المكتبه بأن العمود الول يحتوي على نصوص ،العمود الثاني يحتوي على اعداد صحيحه و كذلك بالنسبه للعمود الثالث. نُحدد الن الكائن الذي تنتمي إليه هذه المعلومات : ;)tree.set_model(store نأخذ قائمة السماء كما فعلنا في المثال الول : 85
;)"get_names_query = cur.execute("SELECT * FROM names ;)(names = cur.fetchall اذا كان هناك بيانات فعلً اضفها إلى القائمه : if len(names) > 0: ;x = 0 while x < len(names): ;)]]store.append([names[x][1],names[x][2],names[x][3 ;x += 1 لحظ كيف اصبحت المصفوفه المُمرره إلى الداله appendبوجود عواميد متعدده ،كما رأيت التعامل مع
عدّة عواميد موضوع بسيط و طبقناه هنا لتتعرف على مدى بساطته (-:
نبدأ الن بالجزئيه الخرى وهي كتابة الدوال التي تخص الزرار ،لنكتب اولً دالة اضافة موظف جديد. عندما يضغط المستخدم على هذا الزر ،نُفتح له نافذه جديده تحتوي على ثلث مربعات نص الول لكتابة اسم
الموظف ،الثاني لكتابة راتب الموظف ،الخير لكتابة عمر الموظف ،و اسفلهم زر مكتوب عليه "موافق" يضغط
عليه المستخدم عندما ينتهي من تعبئة المعلومات حتى تتم اضافة المعلومات إلى قاعدة البيانات ،و اسفل هذا
الزر نص الحاله الذي يُكتب فيه "تم اضافة المعلومات بنجاح" عند نجاح اضافة المعلومات ،صمم هذه النافذه عن طريق Gladeو خزّن الملف في مجلد البرنامج و ليكن اسمه emp.glade
لنسمي مربع النص الخاص بإسم الموظف بـ emp_nameاما المربع الخاص بالراتب بـ salaryاما
المربع الخاص بالعمر ،ageزر موافق نسميه insert_buttonو اخيراً نص الحاله نسميه ،status
لبد من لفت نظرك إلى شئ هام جداً الربط بالحدثين delete-eventو destroyهنا غير مهم لن
نافذتنا التي نصممها الن نافذه فرعيه و ليست نافذه رئيسيه ،هذا يعني انها نافذه تظهر عندما نضغط زر محدد في النافذه الرئيسيه ،اذا حصل و ربطنا الحدثين في النافذه الفرعيه سوف يؤدي إغلق النافذه الفرعيه إلى إغلق البرنامج بالكامل و هذا الذي ل يجب أنْ يحدثَ ،إننا بحاجه إلى الربط بحدث واحد فقط وهو clicked 86
الخاص بزر موافق لنربطه بـ .Emp لنتعلم الن كيف يمكننا إظهار هذه النافذه و جعلها تؤدي وظيفتها بعد الضغط على زر "اضافة موظف" في النافذه الرئيسيه ،نبدأ اولً بتعريف : AddNewEmp
def AddNewEmp(widget,data=None): ببساطه لظهار نافذه فرعيه مُخزنه في ملف منفصل نفعل كما فعلنا مع النافذه الرئيسيه تماماً ،ننادي الملف الخاص بالنافذه ثم نُكلّفه إلى متغير معين في حالتنا هذه سميناه : add_gui
;)'add_gui = gtk.glade.XML('emp.glade نُكلّف النافذه بمتغير معين ليكن اسمه : add_window ;)'add_window = add_gui.get_widget('window1 ثم نطلب إظهار النافذه : ;)(add_window.show هل رأيت كم العمليه بسيطه ،(-:نُكمل الن اخذ الدوات التي سنحتاج لستخدامها في الشيفره المصدريه بعد السطر :
;)'add_window = add_gui.get_widget('window1 نأخذ كُل من مربعات النص ،و زر الموافقه ،و نص الحاله ول يخفى عليك السبب : ;)'emp_name = add_gui.get_widget('emp_name ;)'salary = add_gui.get_widget('salary ;)'age = add_gui.get_widget('age 87
;)'status = add_gui.get_widget('status نربط الحداث ،و لكن ربط الحداث في هذه المره يختلف قليلً ،لنرى الشيفره اولً : })signals = {"Emp" : (InsertEmp,emp_name,salary,age,status ;)add_gui.signal_autoconnect(signals من السطر السابقه تستنتج اننا عندما نضغط على زر الموافقه يتم استدعاء داله بإسم ،InsertEmpو لكن
لماذا في هذه المره بالذات وضعنا الداله داخل مصفوفه مع مجموعه من الكائنات التي عرفناها مسبقاً؟ قبل كل
شئ لنحدد ما الذي نريد الوصول له من الدوات من داخل الداله ،InsertEmpسوف نحتاج للوصول
إلى مربعات النص الثلث لستخراج المعلومات منهن و اضافة المعلومات في قاعدة البيانات ،بالضافه إلى ذلك سوف نحتاج إلى نص الحاله لننا سنغيره في حال نجاح عملية الضافه او فشلها ،جميع هذه الدوات يمكن
الوصول لها من خلل الكائنات التاليه emp_name :لسم الموظف salary ،لراتبه age ،لعمره، statusلنص الحاله ،الحل الذي اعتدنا على استخدامه هو استخدام ،globalهذا يعني اننا لو اردنا
تطبيق هذا الحل في حالتنا هذه سوف يكون اول سطر في الداله InsertEmpمشابه للتالي :
;global emp_name,salary,age,status و لكن هذا السطر لن يعمل في هذه الحاله! لماذا يا ترى؟ ببساطه هذه الكائنات الربعه عباره عن متغيرات محليه ) (Local variablesو ليست متغيرات عامه ) ،(Global variablesالمتغيرات المحليه هي
المتغيرات التي تم تعريفها في داخل داله معينه و لن يستطيع اي جزء آخر من البرنامج الوصول لها بشكل
مباشر ما عدا الداله نفسها ،اما المتغيرات العامه فيتم تعريفها خارج جميع الدوال و بالتالي يمكن لجميع الدوال الوصول لها من خلل وضع اسمها بعد ،globalجرب بنفسك المثال التالي :
def A(): ;y = 10 ;)(B def B(): ;global x,y ;print x 88
;print y ;x = 5 ;)(A و لحظ الخطأ الذي يظهر لك ،يطبع البرنامج اولً الرقم 5بنجاح و لكنه عندما يصل إلى yفإنه يُظهر خطأ يقول ان yليست موجوده ،هذه هي نفس الحاله التي تمر بنا الن و الحل بسيط ان شاء ال ،يكمن الحل في إرسال
هذه الكائنات على شكل بارامترات إلى الداله InsertEmpو يتم إرسال بارمترات إلى الداله التي نناديها
عند وقوع الحدث مثلما رأيت منذ قليل ،مصفوفه نضع فيها اسم الداله اولً و بعدها البارمترات بالترتيب ،يمكننا استخدام الطريقه المشروحه في "الشارات و الدوال "Callbackمن هذا الكتاب ،عموماً سيكون تعريف الداله : InsertEmp
def InsertEmp(widget,emp_name,salary,age,status): يمكننا القول انّ الداله AddNewEmpخاصه بتجهيز و إظهار نافذة الضافه و الداله InsertEmp
خاصه بإضافة المعلومات التي تزودها بها الداله الولى ،لنُكمل ،بما اننا نريد الوصول إلى الجزء Modelفي قائمتنا لننا نريد اضافة الموظف مباشره إلى القائمه عند النجاح في اضافته إلى قاعدة البيانات وبما ان الجزء
Modelيُمثله الكائن storeوهو عباره عن متغير عام و ليس محلي سوف نقوم بإستخدام globalمعه
حتى نصل إليه :
;global store بغرض الختصار و المثَلَه نُخزّن القيم في متغيرات منفصله : ;)(name_val = emp_name.get_text ;)(salary_val = salary.get_text ;)(age_val = age.get_text نقوم بعملية اختبار للقيم و إخبار المستخدم في حال فشل الختبار : if name_val == '': ;)'يرجى كتابة السم'(status.set_text 89
elif salary_val == '': ;)'يرجى كتابة الراتب'(status.set_text elif age_val == '': ;)'يرجى كتابة العمر'(status.set_text نُكمل السلسله الشرطيه بإضافة elseو الشيفره الحقيقيه اسفلها ،اول سطر يكون الستعلم الذي يُدخل المعلومات إلى قاعدة البيانات :
else: )insert = cur.execute('INSERT INTO names(id,name,salary,age VALUES(NULL,"' + name_val + '","' + salary_val + '","' + age_val + ;)')"' بعدها نتأكد من اضافة البيانات إلى قاعدة البيانات : ;)(check = con.commit اذا تم إدخال البيانات بشكل صحيح يجب ان نقوم بالتالي :اولً طباعة ان العمليه تمت بنجاح في نص الحاله
ثم تفريغ المربعات من المعلومات القديمه حتى تكون اضافة معلومات موظف جديد اسهل مثلما فعلنا مسبقاً ،و
اخيراً اضافة البيانات مباشره إلى قائمتنا بدلً من إغلق البرنامج و اعادة فتحه مره اخرى حتى تظهر المعلومات التي اضفناها :
if check == None: ;)'تم اضافة الموظف بنجاح'(status.set_text ;)''(emp_name.set_text ;)''(salary.set_text ;)''(age.set_text ;)])store.append([name_val,int(salary_val),int(age_val
لحظ اننا استخدمنا الداله intلتحويل البيانات في هذه المتغيرات إلى نوع .integer انتهينا الن من كتابة ميزة اضافة الموظف إلى قاعدة البيانات ،تبقى تحرير معلومات الموظف و حذفه ،عندما يريد
المستخدم تحرير معلومات الموظف يجب عليه اختيار الموظف المطلوب من القائمه ثم الضغط على زر تحديث البيانات ،عند الضغط على هذا الزر ستظهر نافذه مشابهه لنافذة اضافة الموظف و لكن مربعات النص فيها تحتوي 90
على معلومات الموظف المُختار ،يحرر المستخدم بيانات الموظف كما يشاء ثم يضغط على زر الموافقه ليتم
تحديث المعلومات في قواعد البيانات ،يعمل الحذف بنفس السلوب تقريباً ،يختار المستخدم الموظف المطلوب ثم يضغط على زر حذف الموظف فيتم حذف الموظف من قواعد البيانات و من القائمه.
لنبدأ اولً بكتابة شيفرة تحرير معلومات موظف ،تقدم مكتبة PyGTKصنف يسمى TreeSelectionو
من خلل هذا الصنف يمكننا التعامل مع الصف الذي يختاره المستخدم من القائمه ،في حالتنا هنا يختار
المستخدم الموظف المطلوب تحديث معلوماته ثم يضغط على زر "تحديث البيانات" سيسبب الضغط على هذا
الزر مناداة داله بإسم UpdateEmpيجب ان تقوم هذه الداله بالتالي :تتعرف على اسم الموظف الذي
تم اختياره حتى تأخذ بياناته من قاعدة البيانات و حتى تستخدم السم في جملة SQLالخاصه بعملية
التحديث ،حسناً السؤال هنا كيف يمكننا اخذ بيانات الموظف الذي اختاره المستخدم بإستخدام الصنف
،TreeSelectionالصنف الرئيسي TreeViewيوفر لنا داله اسمها get_selectionتُرجع هذه
الداله كائن من نوع TreeSelectionو يمكننا من خلل هذا الكائن الوصول إلى بيانات الصف المُختار،
لنعرّف اولً الداله : UpdateEmp
def UpdateEmp(widget,data=None): نستخدم الداله get_selectionالتي تحدثنا عنها منذ برهه لنأخذ الكائن المطلوب و نضعه في متغير نسميه : select
;)(select = tree.get_selection الن ،يعتبر المتغير selectكائناً من الصنف ،TreeSelectionيقدم لنا هذا الصنف داله اسمها
get_selectedو التي تُرجع مصفوفه تحتوي على مُدخلين الول من نوع gtk.TreeModelوهو
الجزء Modelمن القائمه التي اخترنا منها الموظف و يساوي في حالتنا هذه المتغير ،storeاما المُدخل
الثاني من نوع gtk.TreeIterو الذي يُمثّل الصف الذي اختاره المستخدم من ضمن الصفوف الموجوده
في قائمتنا :
91
;)(ar = select.get_selected سنستخدم الن الداله التي يوفرها لنا الصنف TreeModelوهي get_valueلخذ اسم الموظف
الذي اختاره المستخدم ،تأخذ هذه الداله بارامترين الول هو الصف المطلوب وهو الموجود في [ar[1كما
ذكرنا ،و البارامتر الثاني هو العمود المطلوب ،و من خلله يمكننا تحديد ما هي القيمه التي نريد اخذها بالضبط، اسم الموظف ام عمره ام راتبه ،سوف نحتاج إلى اسمه هنا و السم موجود في العمود رقم 0و بالتالي قيمة البارامتر الثاني تساوي ،0يمكننا هنا إما استخدام الكائن storeاو استخدام [ar[0الذي ارجعته الداله ،get_selectedحسناً سوف استخدم [ar[0هنا حتى نستفيد منها على القل : (-:
;)name = ar[0].get_value(ar[1],0 اصبح اسم الموظف مُخزناً في المتغير ،nameيمكننا استخدام هذا المتغير الن في جملة SQLلخذ
معلومات الموظف من قواعد البيانات :
info_query = cur.execute('SELECT * FROM names WHERE name="' + name + ;)'"' نُحضر جميع المعلومات و نخزنها في المتغير : info ;)(info = cur.fetchall بنفس الطريقه التي فتحنا بها نافذه جديده لضافة موظف جديد نفتح نافذه جديده لعرض بيانات الموظف المُختار
و السماح بتغييرها ،سوف نستخدم نفس النافذه التي صممناها لنافذة اضافة موظف وهي emp.gladeلنه ل فرق بينهما بتاتاً :
;)'edit_gui = gtk.glade.XML('emp.glade نأخذ الدوات التي نحتاج ان نتعامل معها في الشيفره البرمجيه :
92
;)'edit_window = edit_gui.get_widget('window1 ;)'emp_name = edit_gui.get_widget('emp_name ;)'salary = edit_gui.get_widget('salary ;)'age = edit_gui.get_widget('age ;)'status = edit_gui.get_widget('status هنا نربط الحدث Empبالداله EditEmpو هذه الداله مشابهه للداله InsertEmpإلى حد كبير و
تستقبل نفس البارامترات تقريباً :
})signals = {"Emp" : (EditEmp,info[0][1],emp_name,salary,age,status ;)edit_gui.signal_autoconnect(signals لحظ البارامتر الول الذي تستقبله الداله EditEmpوهو اسم الموظف المُراد تحرير بياناته ،البارامتر
الثاني هو الكائن الذي يتحكم في مربع النص الخاص بإسم الموظف ،البارامتر الثالث هو الكائن الذي يتحكم في
مربع النص الخاص بالراتب ،البارامتر الرابع هو الكائن الذي يتحكم في مربع النص الخاص بالعمر ،و البارامتر الخامس هو الكائن الذي يتحكم في نص الحاله ،يغير المستخدم البيانات كما يشاء ثم يضغط على زر موافق
لتحديث المعلومات في قاعدة البيانات و الداله EditEmpهي المسؤوله عن عملية تحديث البيانات في قاعدة البيانات.
نعرض الن معلومات الموظف ،كُل معلومه في محلها الصحيح : ;)]emp_name.set_text(info[0][1 ;))]salary.set_text(str(info[0][2 ;))]age.set_text(str(info[0][3 و اخيراً نُظهر النافذه : ;)(edit_window.show
شيفرة UpdateEmpكامله : 93
def UpdateEmp(widget,data=None): select = tree.get_selection(); ar = select.get_selected(); name = ar[0].get_value(ar[1],0); info_query = cur.execute('SELECT * FROM names WHERE name="' + name + '"'); info = cur.fetchall(); edit_gui = gtk.glade.XML('emp.glade'); edit_window = edit_gui.get_widget('window1'); emp_name = edit_gui.get_widget('emp_name'); salary = edit_gui.get_widget('salary'); age = edit_gui.get_widget('age'); status = edit_gui.get_widget('status'); signals = {"Emp" : (EditEmp,info[0] [1],emp_name,salary,age,status)} edit_gui.signal_autoconnect(signals); emp_name.set_text(info[0][1]); salary.set_text(str(info[0][2])); age.set_text(str(info[0][3])); edit_window.show(); : EditEmp نبدأ بكتابة الداله الحقيقيه لتحديث المعلومات وهي def EditEmp(widget,old_name,emp_name,salary,age,status): : نأخذ المعلومات الجديده التي كتبها المستخدم name_val = emp_name.get_text(); salary_val = salary.get_text(); age_val = age.get_text(); : ًنتأكد من انّ المستخدمَ لم يترك شيئاً خاليا 94
if name_val == '': status.set_text(';)'يرجى كتابة السم elif salary_val == '': status.set_text(';)'يرجى كتابة الراتب elif age_val == '': status.set_text(';)'يرجى كتابة العمر
else:
: و يكون تحتها الستعلم الذي يُحدّث البياناتelse نكمل هذه السلسله الشرطيه بـ
update = cur.execute('UPDATE names SET name="' + name_val + '",salary="' + salary_val + '",age="' + age_val + '" WHERE name="' + old_name + '"'); : نُغيّر المعلومات بشكل فعلي check = con.commit(); : نتحقق من نجاح العمليه و نطبع ذلك للمستخدم
;)'بنجاح
if check == None: status.set_text('تم تحديث معلومات الموظف : الشيفره كامله،InsertEmp لحظ مدى التشابه بين هذه الداله و بين اختها،هذا كل شئ
def EditEmp(widget,old_name,emp_name,salary,age,status): name_val = emp_name.get_text(); salary_val = salary.get_text(); age_val = age.get_text(); if name_val == '': status.set_text(';)'يرجى كتابة السم elif salary_val == '': status.set_text(';)'يرجى كتابة الراتب elif age_val == '': status.set_text(';)'يرجى كتابة العمر else: update = cur.execute('UPDATE names SET name="' + name_val + '",salary="' + salary_val + '",age="' + age_val + '" WHERE name="' + old_name + '"'); 95
;)(check = con.commit if check == None: تم تحديث معلومات الموظف'(status.set_text
;)'بنجاح
الجزء الخير في هذا المشروع (-:الداله DeleteEmpوهي المسؤوله عن حذف الموظف ،يختاره
المستخدم ثم يضغط على الزر ليحذفه ،كما فعلنا تماماً مع UpdateEmpيجب علينا اخذ اسم الموظف الذي نريد حذفه حتى نستخدمه في استعلم الحذف ،نعم كما خمّنت تماماً سوف نستخدم الصنف ،(-: TreeSelectionاولً التعريف :
def DeleteEmp(widget,data=None): نأخذ اسم الموظف بنفس السلوب السابق : ;)(select = tree.get_selection ;)(ar = select.get_selected ;)name = ar[0].get_value(ar[1],0 نستخدم المتغير nameفي الستعلم : ;)'"' delete = cur.execute('DELETE FROM names WHERE name="' + name + نحدّث قاعدة البيانات : ;)(check = con.commit حسناً الذي نريده الن بعدما يتم حذف الموظف بنجاح من قاعدة البيانات هو اختفاءه من القائمه كذلك ،نستخدم الداله removeالتي يقدمها الصنف ListStoreلزالة الموظف من القائمه ،تحتاج هذه الداله لبارامتر
واحد وهو الصف المطلوب حذفه و كما تعلم هو موجودٌ في [ar[1و بالتالي شيفرتنا ستكون هكذا : 96
if check == None: store.remove(ar[1]); : الشيفره الكامله،(-: بفضل ال و توفيقه نكون هكذا انتهينا من هذا المشروع
# -*- coding: utf-8 -*import pygtk; pygtk.require('2.0'); import gtk; import gtk.glade; from pysqlite2 import dbapi2 as sql; def delete(widget,data): False; def des(widget,data=None): gtk.main_quit(); def AddNewEmp(widget,data=None): add_gui = gtk.glade.XML('emp.glade'); add_window = add_gui.get_widget('window1'); emp_name = add_gui.get_widget('emp_name'); salary = add_gui.get_widget('salary'); age = add_gui.get_widget('age'); status = add_gui.get_widget('status'); signals = {"Emp" : (InsertEmp,emp_name,salary,age,status)} add_gui.signal_autoconnect(signals); add_window.show(); def InsertEmp(widget,emp_name,salary,age,status): global store; name_val = emp_name.get_text(); salary_val = salary.get_text(); age_val = age.get_text(); if name_val == '': status.set_text(';)'يرجى كتابة السم elif salary_val == '': status.set_text(';)'يرجى كتابة الراتب 97
elif age_val == '': status.set_text(';)'يرجى كتابة العمر else: insert = cur.execute('INSERT INTO names(id,name,salary,age) VALUES(NULL,"' + name_val + '","' + salary_val + '","' + age_val + '")'); check = con.commit(); if check == None: status.set_text(';)'تم اضافة الموظف بنجاح emp_name.set_text(''); salary.set_text(''); age.set_text(''); store.append([name_val,int(salary_val),int(age_val)]); def UpdateEmp(widget,data=None): select = tree.get_selection(); ar = select.get_selected(); name = ar[0].get_value(ar[1],0); info_query = cur.execute('SELECT * FROM names WHERE name="' + name + '"'); info = cur.fetchall(); edit_gui = gtk.glade.XML('emp.glade'); edit_window = edit_gui.get_widget('window1'); emp_name = edit_gui.get_widget('emp_name'); salary = edit_gui.get_widget('salary'); age = edit_gui.get_widget('age'); status = edit_gui.get_widget('status'); signals = {"Emp" : (EditEmp,info[0] [1],emp_name,salary,age,status)} edit_gui.signal_autoconnect(signals); emp_name.set_text(info[0][1]); salary.set_text(str(info[0][2])); age.set_text(str(info[0][3])); edit_window.show(); def EditEmp(widget,old_name,emp_name,salary,age,status): name_val = emp_name.get_text(); salary_val = salary.get_text(); age_val = age.get_text(); if name_val == '': 98
status.set_text(';)'يرجى كتابة السم elif salary_val == '': status.set_text(';)'يرجى كتابة الراتب elif age_val == '': status.set_text(';)'يرجى كتابة العمر else: update = cur.execute('UPDATE names SET name="' + name_val + '",salary="' + salary_val + '",age="' + age_val + '" WHERE name="' + old_name + '"'); check = con.commit(); ;)'بنجاح
if check == None: status.set_text('تم تحديث معلومات الموظف
def DeleteEmp(widget,data=None): select = tree.get_selection(); ar = select.get_selected(); name = ar[0].get_value(ar[1],0); delete = cur.execute('DELETE FROM names WHERE name="' + name + '"'); check = con.commit(); if check == None: store.remove(ar[1]); con = sql.connect('employees'); cur = con.cursor(); g = gtk.glade.XML('gui.glade'); window = g.get_widget('window1'); tree = g.get_widget('treeview1'); add_emp = g.get_widget('button1'); update_emp = g.get_widget('button2'); delete_emp = g.get_widget('button3'); signals = {
"AddNewEmp" : AddNewEmp, "UpdateEmp" : UpdateEmp, "DeleteEmp" : DeleteEmp, "des" : des, "delete" : delete}
g.signal_autoconnect(signals); ### 99
col1 = gtk.TreeViewColumn(''السم,gtk.CellRendererText(),text=0); tree.append_column(col1); col2 = gtk.TreeViewColumn(''الراتب,gtk.CellRendererText(),text=1); tree.append_column(col2); col3 = gtk.TreeViewColumn(''العمر,gtk.CellRendererText(),text=2); tree.append_column(col3); ### store = gtk.ListStore(str,int,int); tree.set_model(store); get_names_query = cur.execute("SELECT * FROM names"); names = cur.fetchall(); if len(names) > 0: x = 0; while x < len(names): store.append([names[x][1],names[x][2],names[x][3]]); x += 1; ### window.show(); gtk.main();
.هذا و الحمدل رب العالمين و صلواته و سلمه على خير المرسلين و آله الطاهرين .وال من وراء القصد
100
وصلت: ملحق أ GTK برامج تستخدم /http://www.gimp.org : – عنوان الويبGIMP برنامج
/http://www.abisource.com : – عنوان الويبAbiWord برنامج /http://pidgin.im : – عنوان الويبPidgin برنامج
/http://liferea.sourceforge.net : – عنوان الويبLiferea برنامج /http://www.gnome.org : – عنوان الويبGNOME مشروع /http://www.xfce.org : – عنوان الويبXfce مشروع
/http://gpe.linuxtogo.org : – عنوان الويبGPE مشروع
وصلت دروس : " على العنوان التاليPyGTK 2.0 Tutorial" دروس متكامله بإسم
http://www.pygtk.org/pygtk2tutorial/index.html : " على العنوان التاليCreating a GUI using PyGTK and Glade" درس بإسم
http://www.learningpython.com/2006/05/07/creating-a-gui/using-pygtk-and-glade
101
" على العنوانBuilding an Application with PyGTK and Glade" درس بإسم http://www.learningpython.com/2006/05/30/building-an- : التالي /application-with-pygtk-and-glade
" على العنوانA Beginner's Guide to Using pyGTK and Glade" درس بإسم http://www.linuxjournal.com/article/6586 : التالي
" على العنوان التاليGTK+ and Glade3 GUI Programming Tutorial" درس بإسم http://www.micahcarrick.com/12-24-2007/gtk-glade-tutorial- : part-1.html
: " على العنوان التاليUsing SQLite in Python" درس بإسم
/http://www.devshed.com/c/a/Python/Using-SQLite-in-Python : " على العنوان التاليPyGTK tutorial" دروس بإسم
/http://zetcode.com/tutorials/pygtktutorial
http://programming-fr34ks.net/ : و الترجمه العربيه للوصله السابقه على العنوان التالي /strikytutorials/pygtktutorial
102
قائمة المصادر http://www.pygtk.org/docs/pygtk/index.html : على العنوان التاليPyGTK الدليل الرسمي لـ /http://scentric.net/tutorial : " على العنوان التاليGTK+ 2.0 Tree View Tutorial" درس بعنوان /http://glade.gnome.org : الرسمي على العنوان التاليGlade موقع http://www.enode.com/x/ : " على العنوان التاليModel-View-Controller Pattern" درس بعنوان markup/tutorial/mvc.html : الموجوده على العنوان التاليPySQLite وثائق http://oss.itsystementwicklung.de/download/pysqlite/doc/sqlite3.html /http://www.gtk.org : الرسمي على العنوان التالي+GTK موقع
103