اذهب إلى المحتوى

البرمجة باستخدام لغة بايثون في تطبيقات راسبيري باي


ابراهيم الخضور

الآن وبعد أن تعرّفت على سكراتش Scratch في المقال السابق، البرمجة باستخدام سكراتش Scratch في راسبيري، ستكتشف في هذا المقال كيفية كتابة شيفرة نصية باستخدام لغة البرمجة بايثون Python.

سُميت اللغة بايثون Python تيمنًا بالفرقة الكوميدية مونتي بايثون Monty Python، وتطورت هذه اللغة التي أنشأها غويدو فان روسوم Guido van Rossum من مشروع هواة أُطلق للمرة الأولى عام 1991 إلى لغة البرمجة المحبوبة التي تقف خلف الكثير من البرمجيات والمشاريع حاليًا. بايثون هي لغة برمجةٍ نصية على عكس اللغة البصرية سكراتش؛ حيث تُكتب التعليمات باستخدام لغةٍ مبسطةٍ وبتنسيقٍ محددٍ ينفذها الحاسوب لاحقًا.

تُعد بايثون خطوةً ثانيةً رائعةً في طريق احتراف البرمجة بالنسبة للذين اكتسبوا خبرةً في استخدام سكراتش، حيث تمُدّهم بمرونةٍ أكبر وبيئة عملٍ برمجية تقليدية. وليس الغرض من هذا الكلام إيصال فكرةٍ أن اللغة صعبة التعلم، حيث يمكن لأي مهتم وبقليلٍ من التدريب كتابة برامج بايثون لأغراضٍ مختلفة انطلاقًا من آلة حاسبة بسيطة، وصولًا إلى ألعاب على درجة من التعقيد.

بُني هذا المقال على المفاهيم والمصطلحات التي قدمناها في مقال البرمجة باستخدام لغة سكراتش، لذلك ستجد هذا المقال أسهل إدراكًا إذا نفذت التمارين التي شُرحت في المقال السابق، وننصح بالعودة والعمل عليها إذا لم تفعل ذلك بعد.

اقتباس

هذا الفصل جزء من سلسلة "مدخل إلى راسبيري باي".

ثوني Thonny: بيئة تطوير بايثون المتكاملة

تتألف الواجهة الرئيسية للبيئة من الأجزاء التالية:

thonny_interface_00.png

  • A- شريط الأدوات: يقدم لك "النمط البسيط simple mode" للبيئة شريط أدواتٍ بأيقوناتٍ مريحةٍ للمستخدم، تساعدك في إنشاء وحفظ وتحميل وتشغيل برامج بايثون، كما تساعدك على اختبار برامجك بطرقٍ عدة.
  • B- منطقة كتابة الشيفرة: وهي المنطقة التي تكتب فيها تعليمات بايثون، وتقسم إلى منطقةٍ رئيسية لكتابة الشيفرة، ومنطقةٍ جانبية طولانية تشكل هامشًا لإظهار أرقام الأسطر.
  • C- منطقة مفسّر أوامر بايثون Python shell: ويسمح لك المُفسِّر في كتابة تعليماتٍ مستقلةٍ، ثم تنفيذها بضغط المفتاح Enter، كما تزوّدك بمعلوماتٍ عن البرامج الجاري تنفيذها.
  • D- منطقة المتغيرات: ويظهر فيها أي متغيرٍ تنشئه في برنامجك، بالإضافة إلى قيمته الحالية، لسهولة المراجعة.
اقتباس

إصدارات ثوني: يوجد نسختان للبيئة تختلفان بالواجهة، هما "النمط الطبيعي Normal Mode" و"النمط البسيط Simple Mode" المخصص عادةً للمبتدئين. سنستعمل في هذا المقال النمط البسيط الذي سيُحمَّل افتراضيًا عند فتح ثوني من قائمة راسبيري باي/ فئة البرمجيات.

انطلق مع بايثون

ستجد ثوني مثل غيره من البرامج المثبتة مسبقًا على راسبيان ضمن قائمة راسبيري باي. انقر على الأيقونة ثم انقل مؤشر الفأرة إلى قسم البرمجة Programming، ثم انقر على Thonny Python IDE. ستظهر بعد ثوانٍ واجهة النمط البسيط لثوني والتي تُحمَّل افتراضيًا.

يُعَد ثوني Thonny حزمة برمجياتٍ تُعرف باسم بيئة التطوير المتكاملة Integrated Development Environment -أو اختصارًا IDE-. إن التسمية معقدةً في الواقع لكنها سهلة الشرح؛ فهي تجمع أو تُكامل جميع الأدوات المختلفة التي تحتاجها لكتابة وتطوير برمجيات ضمن واجهة عمل أو بيئةٍ واحدة.

هناك الكثير من بيئات التطوير التي يدعم بعضها عدة لغات برمجةٍ مختلفة، بينما يدعم البعض الآخر لغةً واحدةً مثل ثوني. وعلى خلاف سكراتش التي تقدم لك كتلًا برمجيةً بصريةً تبني من خلالها برنامجك، ستكتب تعليماتك مثل أي لغة برمجةٍ تقليدية عند استخدام بايثون.

ابدأ برنامجك الأول بالنقر ضمن نافذة Python shell أسفل ويسار نافذة ثوني، واكتب التعليمات التالية، ثم اضغط المفتاح Enter.

print("Hello, World!")

سيعمل البرنامج مباشرةً ويُظهر لك العبارة "!Hello, World" ضمن نفس النافذة، كما هو موضحٌ في الشكل التالي، وهذا ما طلبته تمامًا. فما يفعله مفسر سطر الأوامر shell هو قراءة تعليماتك وتفسير معناها. يًدعى هذه الأسلوب الأسلوب التفاعلي والذي يشابه إلى درجةٍ ما الحوار وجهًا إلى وجه مع أحدهم؛ فعندما تنهي ما تقوله سيرد عليك الشخص الآخر وينتظر حتى تقول شيئًا ما من جديد.

Python_prints_Hello_World_01.png

شكل 1-5 يطبع بايثون الرسالة Hello, World في نافذة مفسر الأوامر

اقتباس

خطأ في الصيغة: إذا لم يعمل برنامجك، بل طبع لك رسالةً في نافذة المفسِّر تبدأ بالعبارة "خطأ في الصيغة Syntax Error"، فهذا يعني أن هناك خطأٌ ما فيما كتبت، حيث لا بُدّ من كتابة تعليمات بايثون بطريقةٍ محددةٍ تمامًا؛ فلن يعمل إذا أغفلت قوسًا، أو إشارة تنصيص، أو أخطأت بكتابة الكلمة "print" مثلًا إذا بدأتها بحرفٍ كبير "P"، أو أضفت رمزًا لا حاجة له ضمن التعليمات. حاول إذا صادفتك هذه الرسالة، إعادة تدقيق ما كتبت ليطابق تمامًا ما نورده في الكتاب ثم اضغط المفتاح Enter.

يمكنك كتابة التعليمات في منطقة الشيفرة إلى يسار نافذة ثوني أيضًا. انقر على النافذة واكتب الشيفرة السابقة تمامًا، ثم اضغط المفتاح Enter. لن يحدث شيء هذه المرة، بل سينتقل المؤشر إلى سطرٍ جديد في نافذة الشيفرة، ولا بدّ أن تنقر أيقونة التشغيل Run الموجودة في شريط أدوات ثوني من أجل تنفيذ تعليماتك، حيث سيُطلب منك حفظ برنامجك، لهذا اختر اسمًا مناسبًا، مثل "Hello World"، ثم انقر زر الحفظ Save. ستُطبع الآن هذه العبارة في نافذة المفسِّر بمجرد حفظ برنامجك.

>>>> %Run 'Hello World.py'
      Hello, World!

يمثّل السطر الأول توجيهًا للمفسر بتشغيل البرنامج الذي حفظته لتوّك، بينما يمثل الثاني نتيجة التنفيذ. تهانينا، لقد كتبت ونفذت أول برامج بايثون بالاسلوبين التفاعلي والتقليدي (الكتابة في نافذة الشيفرة).

Running_simple_program_02.png

شكل2-5 تشغيل برنامجك البسيط

اقتباس

تحدي "رسالة جديدة": هل تستطيع تغيير الرسالة التي يطبعها برنامج بايثون السابق عند تنفيذه؟ إذا أردت طباعة رسائلٍ أخرى فهل ستستعمل النمط التفاعلي أم التقليدي؟ ما الذي قد يحدث إذا أزلت الأقواس أو إشارة التنصيص ثم نفّذت البرنامج؟

الحلقات وإزاحة التعليمات

تستخدم سكراتش كما رأينا كتلًا تمثّل شيفراتٍ محضرةً مسبقًا، ثم تربط بين هذه الكتل مثل قطع لعبة الأحجية؛ بينما تمتلك بايثون طريقتها الخاصة في التحكم بتسلسل التعليمات التي يُبنى عليها البرنامج وهي الإزاحة indentation.

أنشئ برنامجًا جديدًا بالنقر على الأيقونة جديد New في شريط أدوات ثوني، حيث لن يغلق ثوني برنامجك القديم بل سيفتح إلى جواره نافذةً فرعيةً جديدة. اكتب في النافذة الجديدة التعليمات التالية:

print("Loop starting!")
for i in range (10):

تطبع التعليمة في السطر الأول رسالةً بسيطةً ضمن المفسِّر تمامًا كما فعل البرنامج السابق، بينما يبدأ السطر الثاني تشكيل حلقةٍ محدودةٍ تعمل بنفس الطريقة التي عرفناها في سكراتش؛ حيث يُسنَد العداد i إلى الحلقة ويُعطى سلسلةً من الأعداد عبر التعليمة range والتي تحدد رقم البداية 0 صعودًا حتى 9 دون بلوغ العدد 10.

يُخبر رمز النقطتين المتعامدتين ":" بايثون أن التعليمات التالية ستكون جزءًا من الحلقة؛ حيث نلاحظ وجود التعليمات التي ينبغي أن تكون ضمن الحلقة فعلًا داخل الكتلة التي تشبه شكل الحرف "C" في سكراتش؛ بينما يكون الأمر مختلف في بايثون، حيث يجب أن تُزاح بداية أسطر التعليمات الموجودة داخل الحلقة بمقدار أربع مسافاتٍ فارغة بالموازنة مع سطر تعريف الحلقة، وهذا ما يفعله ثوني تلقائيًا عند الضغط على مفتاح Enter بعد السطر الثاني وكتابة التعليمة التالية:

    print("Loop number", i)

ستدفع المسافات الفارغة هذا السطر إلى اليمين موازنةً بالأسطر السابقة، وهذا هو الأسلوب الذي تعتمده بايثون للتمييز بين التعليمات داخل وخارج الحلقة، ونقول عندها أن التعليمات متداخلة Nested. لاحظ أنه بمجرد ضغط المفتاح Enter للانتقال إلى السطر التالي، سيستمر ثوني بإزاحة السطر آخذًا بالحسبان أن السطر الجديد لا يزال ضمن الحلقة، وللتراجع عن ذلك ضع المؤشر في بداية السطر واضغط الزر BACKSPACE مرةً واحدة.

print("Loop finished!")

وهكذا يكتمل برنامجك ذو الأسطر الأربعة، حيث سيُنفَّذ السطر الأول مرةً واحدةً كونه خارج الحلقة وكذلك السطر الرابع، بينما سيُنفّذ السطر الثالث عشرة مرات لوقوعه ضمن الحلقة التي يُعرّفها السطر الثاني:

print("Loop starting!")
for i in range (10):
    print("Loop number", i)
print("Loop finished!")

انقر على أيقونة التشغيل Run، احفظ البرنامج، وانظر إلى نافذة المفسّر لترى نتيجة التنفيذ:

Loop starting!
Loop number 0
Loop number 1
Loop number 2
Loop number 3
Loop number 4
Loop number 5
Loop number 6
Loop number 7
Loop number 8
Loop number 9
Loop finished!

Executing_loop_03.png

شكل 3-5 تنفيذ الحلقة

اقتباس

العد من الصفر: يبدأ العد في بايثون من الصفر Zero Indexed وليس من الواحد؛ لذلك طبع برنامجك الأعداد من 0 إلى 9 بدلًا من 1 إلى 10. يمكنك تغيير ذلك بتبديل التعليمة (rang(10 لتصبح (rang(1,11، أو كما تشاء.

إزاحة التعليمات أحد مصادر قوة بايثون، وفي نفس الوقت السبب الأكثر شيوعًا لكي لا يعمل البرنامج كما هو متوقع؛ فعندما تبحث عن أخطاء برنامج بالعملية التي تُدعى تنقيح الأخطاء Debugging، تحقق دومًا من الإزاحة وخاصةً عند ترتيب حلقاتٍ ضمن حلقات. يدعم بايثون أيضًا الحلقات اللامتناهية التي تُنفّذ ما بداخلها من تعليمات دون توقف. لتشاهد ذلك بدّل السطر الثاني من البرنامج ليصبح:

while True

نفِّذ البرنامج الآن وستحصل على رسالة خطأ: name 'i' is not defined لأنك حذفت السطر الذي أنشأ المتغير i وأسند قيمةً إليه. لإصلاح المشكلة اجعل السطر الثالث على النحو التالي:

    print("Loop running!")

انقر على أيقونة التشغيل Run، وقد ترى العبارة "!Loop starting" إذا كنت سريعًا بما يكفي، لأن ما ستشاهده تاليًا تكرارُ لا ينتهي للعبارة "!Loop running"، ولن ترى بالطبع العبارة "!Loop finished" لعدم وجود نهاية للحلقة، ولدى الانتهاء من طباعة رسالة "!Loop running" يعود مجددّا إلى البداية من جديد، وبالتالي الحلقة لا متناهية.

infinite_loop_04.png

شكل 4-5 حلقة لا متناهية ستستمر حتى توقف البرنامج بنفسك

انقر على أيقونة إيقاف Stop في شريط أدوات ثوني لإيقاف عمل برنامجك (مقاطعة عمل البرنامج)، فستظهر لك رسالةً في نافذة المفسِّر وسيتوقف البرنامج دون أن يصل حتى إلى السطر 4.

العبارات الشرطية والمتغيرات

المتغيرات أدواتٌ برمجيةٌ هامةٌ جدًا في جميع اللغات ولا تقتصر مهمتها على التحكم بالحلقات فقط.

ابدأ برنامجًا جديدًا بالنقر على الأيقونة جديد New في شريط أدوات ثوني، ثم اكتب الشيفرة التالية في نافذة الشيفرة:

userName = input ("what is your name? ")

انقر على الأيقونة Run واحفظ برنامجك بالاسم "Name Test"، ثم راقب ما يحدث في نافذة المفسِّر. ستُسأل عن اسمك، لذلك اكتبه في نافذة المفسِّر واضغط المفتاح Enter. وبما أن برنامجك مكوّنٌ من تعليمةٍ واحدة، فلن يحدث أكثر من ذلك، لكن ستلاحظ ظهور المتغير وقيمته تلقائيًا في نافذة المتغيرات إلى يمين نافذة ثوني كما هو موضحٌ في الشكل التالي، وستبقى المتغيرات في نافذتها حتى عند توقف البرنامج، ليسهل عليك إدراك ما فعله البرنامج.

variable_inside_variables_area_05.png

شكل 5-5 تساعدك نافذة المتغيرات على تتبع المتغيرات وقيمها

اقتباس

تحدٍ- حلقات وحلقات: هل يمكنك إعادة الحلقة السابقة إلى حلقةٍ محدودة؟ هل تستطيع إضافة حلقةٍ محدودةٍ أخرى إلى البرنامج؟ كيف ستضع حلقةً ضمن حلقة، وكيف تتوقع أن يُنفَّذ ذلك؟

وحتى يقدّم البرنامج شيئًا مفيدًا، ضع عبارة شرطية ضمنه بكتابة التعليمات التالية:

if userName == "Clark Kent":
    print("You are Superman!") 
else:    
    print("You are not Superman!")

لاحظ أن ثوني سيزيح الشيفرة تلقائيًا إذا وجد ذلك ضروريًا، لكنه لا يعرف متى يتوقف عن إزاحة الشيفرة؛ لذلك عليك حذف المسافات الفارغة بنفسك.

انقر على الأيقونة "Run" واكتب اسمك في نافذة المفسِّر؛ فإذا صدف وكان اسمك Clark Kent، سترى الرسالة "You are Superman". شغل البرنامج من جديد واحرص أن يكون اسمك Clark Kent بحرفي C وK كبيرين كما هو في الشيفرة تمامًا، سيميّز البرنامج عندها أنك Superman فعلًا، كما هو موضحٌ في الشكل التالي.

you_are_superman_06.png

شكل 6-5 ألا يحب أن تكون في الخارج تحمي العالم

يُستخدم الرمز "==" في بايثون للموازنة المباشرة؛ أي سيرى إذا كانت قيمة المتغير userName مطابقة للنص الموجود في برنامجك. يمكنك استخدام عوامل موازنة comparison operators أخرى إذا كنت تتعامل مع الأعداد، مثل العامل "<"، الذي يعني أكبر من، و">"، الذي يعني أصغر من، و"<=" الذي يعني أكبر من أو يساوي، و">="، الذي يعني أصغر من أو يساوي، كما يوجد العامل "=!" ويعني لا يساوي وهو المعاكس المباشر للعامل "==".

اقتباس

استخدام العامل "=" والعامل "==": يرتبط مفتاح التمكّن من استخدام المتغيرات بمعرفة الفرق بين العاملين "=" و"=="؛ حيث يعني العامل "=" جعل المتغير يساوي قيمةً ما، أي يسند قيمةً إلى متغير؛ بينما يتحقق العامل "==" من أن المتغير يساوي قيمةً ما أو لا. لا تخلط بين الاستخدامين فتكون النتيجة برنامجًا لا يعمل.

يمكن استخدام عوامل الموازنة ضمن الحلقات أيضًا. احذف الأسطر من 2 إلى 5 واكتب الشيفرة التالية:

while userName != "Clark Kent":
    print("You are not Superman - try again!")
    userName = input ("What is your name? ") 
print("You are Superman!")

انقر على الأيقونة Run مرةً أخرى، ستلاحظ سؤال البرنامج عن اسمك مرارًا وتكرارًا حتى تؤكد له أنك "Superman"، كما هو موضحٌ في الشكل التالي، أي حتى تكتب الاسم "Clark Kent" كما لو أنه كلمة سر من نوعٍ ما. إذًا، عليك كتابة الاسم "Clark Kent" حتى تخرج من الحلقة، أو تنقر الأيقونة Stop في شريط أدوات ثوني.

تهانينا، لقد تعلمت الآن استخدام العبارات الشرطية وعوامل الموازنة.

اقتباس

تحدٍ- أضف أسئلةً أكثر: هل يمكنك تعديل البرنامج لتسأل أسئلةً أكثر، وتخزن أجوبتها في متغيراتٍ مختلفة؟ هل يمكنك تصميم برنامجٍ يستخدم العبارات الشرطية وعوامل الموازنة لتقرر إذا كان الرقم الذي يدخله المستخدم أكبر أو أصغر من 5 كما فعلنا في المقال السابق "البرمجة باستخدام سكراتش"؟

loop_asking_your_name_07.png

شكل 7-5 سيستمر البرنامج بسؤالك عن اسمك حتى تكتب Clark Kent

المشروع الأول: السلحفاة والثلج

طالما أنك تعرّفت على الطريقة التي تعمل بها بايثون فقد حان الوقت للعمل مع الرسوميات وتصميم بلورات الثلج باستخدام أداة تُدعى turtle "سلحفاة".

اقتباس

تُصنَّع الأشكال الفيزيائية للروبوتات وفقًا لأسماء الحيوانات التي تحاكيها، لهذا تُصنّع السلاحف على شكل قلمٍ يتحرك وفق خطٍ مستقيم ويلتف ويرتفع أو ينخفض؛ وهذا يعني برمجيًا أن تتحرك وتقف وترسم خطًا عندما تتحرك. لا تمتلك بايثون أداة سلحفاةٍ مدمجة مثل غيرها من اللغات، مثل لغة Logo وتوزيعاتها، لكنها تأتي ضمن مكتبةٍ ذات إضافاتٍ برمجية لتزيد قدرة بايثون. وتُعرف المكتبات Libraries بأنها تجميعاتٌ Bundles لشيفراتٍ تضيف تعليماتٍ جديدة لتوسيع إمكانيات بايثون، ويمكن أن تضمها إلى برامجك من خلال الأمر import.

أنشئ برنامجًا جديدًا بالنقر على الأيقونة New، ثم اكتب الشيفرة التالية:

import turtle

عند استخدام التعليمات التي تتيحها مكتبةٌ ما، لا بُدّ من كتابة اسم المكتبة تليها نقطةٌ، ثم اسم التعليمة. قد يغدو هذا الأمر مزعجًا مع الوقت، لذلك يمكن إسناد هذا الأمر إلى متغيرٍ باسمٍ مختصر قد يكون حرفًا واحدًا مثلًا، لكننا سنختار اسم حيوانٍ أليفٍ لهذه السلحفاة على النحو التالي:

pat = turtle.Turtle()

ولاختبار السلحفاة لا بدّ أن نعطيها عملًا لتنفّذه، لذلك اكتب الشيفرة التالية:

pat.forward(100)

انقر الآن على أيقونة التشغيل Run، ثم احفظ برنامجك باسم "Turtle Snowflakes". ستظهر نافذةٌ رسوميةٌ جديدةٌ باسم Turtle Graphics، عندما يعمل البرنامج لتعرض نتيجة التنفيذ، وسترى تحرٌّك السلحفاة "pat" للأمام مقدار 100 وحدةٍ راسمةً خطًا مستقيمًا، كما هو وضحٌ في الشكل التالي.

urtle_moves_forward_08.png

شكل 8-5 تتحرك السلحفاة إلى الأمام وترسم خطًا مستقيمًا

عُد إلى نافذة ثوني الرئيسية إذا حجبتها النافذة الرسومية، وذلك بالنقر على نافذة ثوني أو تصغير النافذة الرسومية. انقر على أيقونة الإيقاف Stop لإغلاق النافذة الرسومية. نظرًا لمشقة كتابة كل تعليمة من تعليمات حركة السلحفاة، سنستعمل حلقةً Loop، لذلك احذف السطر الثالث واكتب الشيفرة التالية مكانه:

for i in range(2):
    pat.forward(100)    
    pat.right(60)    
    pat.forward(100)    
    pat.right(120)

شغل البرنامج لتشاهد متوازي الأضلاع الذي ترسمه السلحفاة والموضح بالشكل التالي.

draw_shapes_09.png

شكل 9-5 بدمج الانعطافات والحركات يمكن رسم الأشكال

ولتحويل الشكل إلى بلورة ثلج، أوقف البرنامج، ثم ضع الحلقة السابقة ضمن حلقةٍ جديدة بتعديل السطر 3 ليصبح على النحو التالي:

for i in range(10):

ووضع سطر الأوامر التالي في نهاية البرنامج:

    pat.right(36)

لن يعمل البرنامج حاليًا لعدم إزاحة الحلقة الداخلية بصورةٍ صحيحة، لذلك أزح الأسطر من 4 إلى 8 إلى الأمام بإضافة أربع مسافاتٍ فارغة قبل كلٍ منها. إليك الشيفرة الصحيحة:

import turtle
pat = turtle.Turtle()
for i in range(10):
    for i in range(2):        
        pat.forward(100)        
        pat.right(60)        
        pat.forward(100) 
        pat.right(120)    
    pat.right(36)

انقر على أيقونة التشغيل Run وشاهد كيف ترسم السلحفاة متوازي الأضلاع كما فعلت سابقًا، ثم تستدير بزاوية 36 درجة وترسم متوازي أضلاعٍ آخر وتستدير بنفس المقدار وترسم آخرًا وهكذا حتى ترسم 10 أشكالٍ متداخلة، كما هو موضحٌ في الشكل التالي.

draw_snowflake_10.png

شكل 10-5 تداخل متوازيات الأضلاع على الشاشة تعطي شكلًا مشابهًا لبلورة الثلج

يمكن رسم السلحفاة في بايثون بعدة ألوان. أضف الأسطر التالية قبل السطر الثالث لتدفع الأسطر إلى الأسفل:

turtle.Screen().bgcolor("blue") 
pat.color("cyan")

شغَّل برنامجك من جديد لترى تأثير الشيفرة، حيث سيتغير لون خلفية النافذة الرسومية إلى الأزرق ولون بلورة الثلج لتصبح رمادية، كما هو مبينٌ في الشكل التالي.

colored_bg_draw_11.png

تغيير لون الخلفية ولون شذرة الثلج

يمكن اختيار الألوان عشوائيًا من قائمةٍ أيضًا وذلك باستخدام المكتبة random. عُد إلى بداية برنامجك واكتب الشيفرة التالية لتصبح في السطر الثاني:

import random

غيَّر لون الخلفية في السطر الرابع من اللون الأزرق "blue" إلى اللون الرمادي "grey"، ثم أنشئ متغيرًا جديدًا يُدعى "colours" بكتابة الشيفرة التالية لتحل مكان السطر الخامس:

colours = ["cyan", "purple", "white", "blue"]
اقتباس

التهجئة الأمريكية: تستخدم معظم لغات البرمجة التهجئة الإنجليزية الأمريكية للكلمات بما في ذلك بايثون، حيث يُكتب أمر تغيير لون قلم السلحفاة بالتهجئة الأمريكية color، وإذا كتبته وفق التهجئة البريطانية colour فلن يفلح الأمر؛ بينما يمكن تسمية المتغيرات بالتهجئة التي تريد، مثل المتغير colours المُستخدم أعلاه، وستفهم بايثون ذلك.

يُعرف النوع السابق من المتغيرات بالقائمة list ويُحدَّد بقوسين مربعين. وضعنا في القائمة بعض الألوان المحتملة لبلورة الثلج وعليك الآن أن تخبر بايثون باختيار لونٍ منها في كل مرةٍ تُكرر فيها الحلقة. أضف في آخر البرنامج الشيفرة التالية وانتبه أن تبتعد عن بداية السطر أربع مسافاتٍ فارغة لتكون ضمن الحلقة الخارجية:

        pat.color(random.choice(colours))

انقر الآن على أيقونة التشغيل Run وسيرسم البرنامج بلورة الثلج التي تشبه نجمة النينجا من جديد، ولكن ستختار بايثون هذه المرة لونًا مختلفًا لكل متوازي أضلاعٍ من قائمة الألوان السابقة، وهذا يعطي البلورة ألوانًا مبهجة، كما هو موضحٌ في الشكل التالي.

Using_random_colours_12.png

شكل12-5 استخدام ألوان عشوائية لرسم بتلات شذرة الثلج

ولتظهر البلورة أقرب إلى بلورة الثلج الحقيقية وليس إلى نجمة النينجا، أضف الشيفرة التالية ابتداءً من السطر السادس وتحت قائمة الألوان:

pat.penup() 
pat.forward(90)
pat.left(45)
pat.pendown()

ستدفع التعليمتان penup وpendown بالقلم نحو الورقة وخارجها في روبوت سلحفاة واقعي، لكنهما تخبران السلحفاة في بايثون بالبدء برسم الخط أو التوقف. سنستخدم الآن دالةً بدلًا من الحلقة؛ والدالة function هي مجموعةٌ من التعليمات الممكن استدعائها سويةً في أي وقت كما لو أنك تصمم تعليمة بايثون خاصةٍ بك.

ابدأ بحذف شيفرة رسم بلورات الثلج المبنية على متوازي الأضلاع وهي الشيفرة الموجودة بين الأمر:

 pat.color("cyan")

في السطر 10 ضمنًا وحتى الأمر:

pat.right(36)

في السطر 17. اترك الأمر:

pat.color(random.choice(colours))

بعد أن تضع الرمز "#" في بداية السطر، ويُعرف ذلك بتعليق السطر؛ أي أن بايثون سيتجاهل تنفيذه. يمكن أيضًا استخدام "#" لإضافة ملاحظاتٍ عن شيفرتك، لجعلها سهلة الفهم وخاصةً عندما تعود لقرائتها بعد عدة أشهرٍ، أو إذا أردت إرسالها إلى شخصٍ آخر.

أنشئ دالتك التي سنسميها ()branch، وذلك بكتابة الشيفرة التالية في السطر العاشر تحت تعليمة ()pat.pendown مباشرةً:

def branch():

ستعرف التعليمة السابقة الدالة ()branch، وسيضيف ثوني تلقائيًا إنزياحًا للتعليمات التي ستأتي ضمن الدالة. اكتب الشيفرة التالية وانتبه جيدًا إلى الإزاحة، لأنك سترتّب شيفراتٍ متداخلة وفق ثلاث سوياتٍ من الإزاحة:

for i in range(3):        
        for i in range(3):            
            pat.forward(30)            
            pat.backward(30)            
            pat.right(45)        
        pat.left(90)        
        pat.backward(30)        
        pat.left(45)    
    pat.right(90)    
    pat.forward(90)

أخيرًا، أضف حلقةً إلى نهاية برنامجك لكن قبل سطر الاختيار العشوائي للون، والذي وضعت الرمز "#" قبله، وذلك لاستدعاء الدالة التي أنشأتها:

for i in range(8):    
    branch()    
    pat.left(45)
ستبدو الشيفرة الكاملة للمشروع على النحو التالي:

import turtle
import random
pat = turtle.Turtle()
turtle.Screen().bgcolor("grey") 
colours = ["cyan", "purple", "white", "blue"] 
pat.penup() 
pat.forward(90) 
pat.left(45) 
pat.pendown() 
def branch():    
    for i in range(3):        
        for i in range(3):            
            pat.forward(30)            
            pat.backward(30)            
            pat.right(45)
        pat.left(90)        
        pat.backward(30)        
        pat.left(45)    
    pat.right(90)    
    pat.forward(90) 
for i in range(8):    
    branch()    
    pat.left(45) 
#   pat.color(random.choice(colours))

انقر على أيقونة التشغيل Run وراقب السلحفاة وهي ترسم بلورة الثلج وفقًا لتعليماتك. تهانينا، تبدو بلورة الثلج الآن أكثر واقعية، وهذا موضحٌ في الشكل التالي.

branched_snowflake_13.png

شكل 13-5 تمنح التفرعات الإضافية شذرة الثلج شكلًا أكثر واقعية

اقتباس

تحدٍ: ماهو التالي؟ هل يمكن استخدام التعليمة المعطلة لرسم تفرعاتٍ ملونة لبلورة الثلج؟ هل يمكن أن تكتب دالةً اسمها "snowflake" وتستخدمها لرسم المزيد من بلورات الثلج في أماكنٍ مختلفة من الشاشة؟ هل يمكن لبرنامجك تغيير حجم ولون البلورات عشوائيًا؟

المشروع الثاني: إيجاد الاختلافات المخيفة

يمكن لبايثون التعامل أيضًا مع الصور والأصوات، وسنستخدم ذلك في تصميم لعبة إيجاد الاختلاف بين صورتين لكن مع نكهةٍ مرعبةٍ لتفاجئ اللاعب.

اقتباس

يحتاج المشروع إلى صورتين، الأولى هي الصورة الرئيسية وتتكون من قسمين متناظرين مع اختلافاتٍ محدودةٍ جدًا، والثانية هي الصورة المرعبة التي ستظهر فجأة. كما نحتاج أيضًا إلى ملفٍ صوتي لتعزيز التأثير المرعب.

انقر على أيقونة راسبيري باي وانتقل إلى المتصفح كروميوم، ثم اكتب في شريط الأدوات العنوان rpf.io/spot-pic وبعدها اضغط المفتاح Enter. بعد ان يحمل المتصفح الصور، انقر عليها بالزر اليميني للفأرة واختر "حفظ الصورة باسم Save image as"، ثم خزنها في المجلد home/pi/، وكرّر نفس الأمر لتنزيل الصورة الأخرى من العنوان rpf.io/scary-pic.

يختلف الأمر بالنسبة للملف الصوتي الموجود على العنوان rpf.io/scream، حيث سيبدأ المتصفح كروميوم بتنزيل الملف تلقائيًا إلى مجلد "التنزيلات Downloads"، لذلك لا بُدّ من نقله إلى المجلد home/pi/.

لنقل الملف، انقر على أيقونة راسبيري باي مرورًا بفئة البرامج الملحقة Accessories، ثم انقر على برنامج مدير الملفات File Manager، وافتح مجلد التنزيلات Downloads وابحث عن ملفٍ باسم "scream.wav". انقر على الملف بالزر اليميني، ثم اختر قص Cut، ثم انتقل إلى المجلد home/pi/ وانقر على أيّ مكانٍ فارغٍ من نافذته بالزر اليميني، ثم اختر لصق Paste. يمكنك الآن إغلاق كروميوم ومدير الملفات.

انقر على الأيقونة جديد New في شريط أدوات ثوني لإنشاء مشروعٍ جديد، حيث سنستخدم هنا أيضًا مكتبةً جديدةً تدعى "Pygame" لتوسيع إمكانيات بايثون، ويُلمِّح اسم هذه المكتبة كما نرى إلى علاقتها بالألعاب. اكتب الشيفرة التالية:

import pygame

سنحتاج في هذا المشروع إلى إدراج أجزاءٍ محددةٍ من المكتبة Pygame بالإضافة إلى أجزاءٍ من مكتباتٍ أخرى، ولتنفيذ ذلك اكتب الشيفرة التالية:

from pygame.locals import *
from time import sleep
from random import randrange

تختلف التعليمة from عن التعليمة import بأنها تدرج جزءًا محددًا من المكتبة بدلًا من إدراجها بالكامل. أما الآن فعلينا إعداد المكتبة Pygame، وتدعى هذه العملية التهيئة initialisation، حيث تحتاج Pygame معرفة ارتفاع وعرض جهاز العرض الذي تستخدمه أو ما يُسمى دقة جهاز العرض، لذلك اكتب الشيفرة التالية:

pygame.init()
width = pygame.display.Info().current_w 
height = pygame.display.Info().current_h

أما الخطوة النهائية في تهيئة Pygame فهي إنشاء نافذة العرض التي تُدعى شاشة وفق مصطلحات المكتبة. اكتب الشيفرة التالية لإنجاز ذلك:

screen = pygame.display.set_mode((width, height))

pygame.quit()

يشير الفراغ بين السطرين السابقين إلى أن شيفرة برنامجك ستكون بينهما.

انقر على أيقونة التشغيل Run واحفظ برنامجك باسم"Spot the difference" ثم راقب التنفيذ. ستُنشئ المكتبة نافذةً بخلفيةٍ سوداء لا تلبث أن تختفي مباشرةً نظرًا لتنفيذ تعليمة الخروج **()quit**، كما ستلاحظ امتلاء نافذة المتغيرات بعددٍ كبيرٍ من المتغيرات الجديدة التي ولّدتها Pygame تلقائيًا، كما هو موضحٌ في الشكل التالي. لا تلقِ بالًا لهذا الآن.

variable_window_filled_14.png

شكل 14-5 لا تلق بالًا للمتغيرات الجديدة التي ستظهر في نافذة المتغيرات

لعرض الصورة الأولى من أجل إيجاد الاختلاف بين قسميها، اكتب في الفراغ بين سطري الشيفرة السابقين السطر الجديد التالي:

difference = pygame.image.load('spot_the_diff.png')

ولتملأ الصورة شاشة العرض، لا بدّ من تغيير أبعاد الصورة لتتناسب مع دقة جهاز العرض الذي تستخدمه، لهذا اكتب الشيفرة التالية:

difference = pygame.transform.scale(difference, (width, height))

أصبحت الصورة جاهزةً الآن في الذاكرة وعليك عرضها على الشاشة بعملية تُعرف باسم نقل البتات blitting، لذلك اكتب الشيفرة التالية:

screen.blit(difference, (0, 0)) 
pygame.display.update()

ينقل السطر الأول الصورة إلى الشاشة، بينما يطلب السطر الثاني من Pygame تحديث الشاشة، وستبقى الصورة دون السطر الثاني في مكانها الصحيح، لكنها في الذاكرة ولن تتمكن من رؤيتها. انقر على أيقونة Run، ستظهر الصورة على الشاشة لبرهة وتختفي، كما هو موضحٌ في الصورة التالية.

game_image_15.png

الشكل 15-5 الصورة الأساسية للعبة "جد الاختلافات"

لعرض الصورة مدةً أطول، اكتب الشيفرة التالية:

sleep(3)

في السطر الذي يسبق التعليمة:

pygame.quit()

انقر على أيقونة التشغيل Run وسترى أن الصورة ستُعرض لفترةٍ أطول على الشاشة. أضف الآن الصورة المفاجِئة إلى برنامجك بكتابة الشيفرة التالية:

zombie = pygame.image.load('scary_face.png') 
zombie = pygame.transform.scale(zombie, (width, height))

تمامًا قبل السطر:

pygame.display.update()

أضف تأخيرًا زمنيًا لتبقى صورة الزومبي المخيفة فترةً أطول بكتابة الشيفرة التالية:

sleep(3)

وانقل أخيرًا بتات صورتك إلى الشاشة وحدّثها ليرى اللاعب الصورة المخيفة. لتنفيذ ذلك، اكتب الشيفرة التالية:

screen.blit(zombie, (0,0)) 
pygame.display.update()

انقر على أيقونة التشغيل Run وراقب ما يحدث. ستظهر الصورة الأساسية للعبة لكن ستحل محلها صورة الزومبي المخيفة بعد ثلاث ثوان، كما هو موضحٌ في الشكل التالي.

scary_surprise_16.png

شكل 16-5 سترعب أحدهم بالتأكيد

لكن ظهور الصورة المرعبة بعد ثلاث ثوانٍ سيجعل الأمر متوقعًا، ولتفادي ذلك بدّل السطر:

 sleep(3)

الذي يقع قبل تعليمة نقل بتات الصورة المخيفة بالسطر التالي:

sleep(randrange(5, 15))

تختار التعليمة السابقة عددًا عشوائيًا بين 5 و 15 وتؤخر البرنامج هذه المدة. سنحمّل الآن الملف الصوتي وذلك بإضافة السطر التالي:

scream = pygame.mixer.Sound('scream.wav')

فوق تعليمة التأخير الزمني الأخيرة مباشرةً.

سنشغل الملف الصوتي بعد عرض الصورة المخيفة فورًا بكتابة التعليمة:

scream.play()

ثم ضع تعليمة إيقاف الصوت:

scream.stop()

قبل التعليمة الأخيرة في البرنامج:

pygame.quit()

انقر على أيقونة التشغيل Run واستمتع بإنجازك؛ فبعد عرض الصورة البريئة التي سيجد اللاعب الاختلافات بين قسميها، ستظهر فجأة صورة الزومبي المخيفة مع صوتٍ دمويٍ مخيف. إذا ظهرت صورة الزومبي قبل أن يبدأ الصوت، يمكنك تعديل ذلك بإضافة تأخيرٍ زمني بسيط وفق التعليمة:

sleep(0.4)

قبل السطر:

screen.blit(zombie, (0,0))

ستبدو الشيفرة الكاملة للمشروع على النحو التالي:

import pygame
from pygame.locals import *
from time import sleep
from random import randrange
pygame.init()
width = pygame.display.Info().current_w 
height = pygame.display.Info().current_h 
screen = pygame.display.set_mode((width, height)) 
difference = pygame.image.load('spot_the_diff.png') 
difference = pygame.transform.scale(difference, (width, height)) 
screen.blit(difference, (0, 0)) 
pygame.display.update() 
zombie = pygame.image.load('scary_face.png') 
zombie = pygame.transform.scale (zombie, (width, height)) 
scream = pygame.mixer.Sound('scream.wav') 
sleep(randrange(5, 15)) 
scream.play() 
screen.blit(zombie, (0,0)) 
pygame.display.update() 
sleep(3) 
scream.stop() 
pygame.quit()

كل ما عليك فعله الآن هو دعوة أصدقائك للعب، وتأكد من توصيل مكبرات الصوت.

اقتباس

تحدٍ- بدّل المظهر: هل تستطيع تغيير الصورتين لتصبحان أكثر ملائمةً لمناسباتٍ أخرى؟ هل تستطيع تصميم صورة "إيجاد الاختلاف" الخاصة بك والصورة المخيفة بنفسك باستخدام محررٍ، مثل GIMP؟ هل يمكنك تعقب نقرات اللاعب على الفروقات لتبدو اللعبة أكثر واقعية؟

المشروع الثالث: المتاهة

بعد أن تعرّفت أكثر على بايثون، حان الوقت لاستخدام مكتبة Pygame في إنجاز مشروعٍ أكثر تعقيدًا، وهو لعبة متاهة ووحشٌ يلاحق اللاعب، مبنيةٌ على تعليماتٍ نصية، حيث تُعرف هذه الألعاب بالمغامرات ذات الأوامر الكتابية أو الخيال التفاعلي، وتعود إلى الزمن الذي لم تتمكن فيه الحواسب من التعامل مع الرسوميات وأصرَّ حينها معجبوا هذه الأجهزة بأن الرسوميات الأكثر وضوحًا هي تلك التي نرسمها في مخيلتنا.

اقتباس

يُعدُّ هذا البرنامج أكثر تعقيدًا من البرامج السابقة في هذا المقال، لذلك كُتبت شيفرته جزئيًا لتسهيل الأمر. نزّل شيفرة المشروع بفتح متصفح كروميوم وكتابة العنوان التالي rpf.io/rpg-code في شريط العناوين ثم الضغط على المفتاح Enter، حيث ستجد الشيفرة عند اكتمال التنزيل التلقائي في مجلد التنزيلات Downloads.

عُد الآن إلى ثوني وانقر أيقونة التحميل Load، ثم ابحث عن الملف rpg-rpg.py في مجلد التنزيلات Downloads وانقر على زر التحميل Load. انقر الآن على أيقونة التشغيل Run لتتعرف على الطريقة التي تُنفَّذ فيها المغامرات الكتابية، وسيظهر الخرج في نافذة المُفسِّر أسفل نافذة ثوني. كبِّر نافذة ثوني لتسهل عليك القراءة.

اللعبة بسيطةٌ جدًا كما تبدو الآن. هناك غرفتان دون أغراض، ويبدأ اللاعب في الصالة، وهي أولى الغرفتين. اكتب 'go south' للذهاب نحو المطبخ، ثم اضغط المفتاح Enter، كما هو موضحٌ في الشكل التالي؛ واكتب 'go north' للعودة من المطبخ إلى الصالة. يمكنك أيضًا كتابة أوامر، مثل 'go west' أو 'go east'، ولكن ستظهر لك رسالة خطأ لعدم وجود غرفٍ في هذه الاتجاهات.

two_rooms_maze_17.png

شكل17-5 هنالك غرفتان فقط حتى الآن

انقر على أيقونة الإيقاف Stop لإيقاف البرنامج، وانظر إلى المتغير rooms ضمن نافذة المتغيرات، ويُدعى هذا النوع من المتغيرات القاموس، واسُتخدِم من أجل إخبار اللعبة بالغرف المتوفرة وإلى أيِّ غرفةٍ يقود طريقٌ معين. يمكنك الانتقال إلى السطر 29 من الشيفرة لرؤية النقطة التي أُنشئ فيها هذا المتغير وكيف استُعمل.

لجعل اللعبة أكثر متعةً، أضِف غرفةً جديدةً هي غرفة الطعام شرق الصالة. لتنفيذ ذلك، ابحث عن المتغير rooms في الشيفرة ووسِّعه بوضع فاصلة "," بعد القوس "{" في السطر 38، وأضِف الشيفرة التالية، دون التدقيق كثيرًا على الإزاحة في القاموس:

 'Dining Room' : {                  
                       'west' : 'Hall'                
                     }

ستحتاج أيضًا إلى مخرج للغرفة فلن تولّده الشيفرة تلقائيًا؛ لذلك انتقل إلى بداية السطر 33 وأضِف فاصلة "," ثم أضِف السطر التالي:

                       'east' : 'Dining Room'

انقر الآن على أيقونة التشغيل Run، واختبر الغرفة الجديدة من خلال كتابة الأمر 'go east' لتنتقل من الصالة إلى غرفة الطعام، واكتب الأمر 'go west' للعودة من غرفة الطعام إلى الصالة.

تهانينا، صنعت غرفةً بنفسك.

new_room_18.png

شكل 18-5 لقد صنعت غرفة بنفسك

لن يكون الأمر ممتعًا إذا لم يكن في الغرفة أغراض؛ لذلك سنضيف شيئًا إلى الغرفة بتعديل قاموس الغرفة. أوقف البرنامج وابحث عن القاموس Hall في نافذة الشيفرة، ثم أضف فاصلةً "," في نهاية السطر:

                       'east' : 'Dining Room'

ثم اضغط المفتاح ENTER واكتب مايلي:

                       'item' : 'key'

انقر على أيقونة التشغيل Run مجددًا، حيث ستخبرك اللعبة بوجود غرضٍ جديدٍ، وهو المفتاح 'key'. يمكنك كتابة الأمر 'get key' لتتمكن من التقاطه وإضافته إلى قائمة الأغراض التي تحملها، ويُطلق على هذه القائمة اسم المقتنيات inventory، وهي تبقى معك أثناء تنقلك بين الغرف.

key_add_to_inventory_19.png

شكل 19-5 سيضاف المفتاح إلى مقتنياتك

انقر على أيقونة Stop لإيقاف اللعبة. علينا الآن جعل اللعبة أكثر تشويقًا من خلال إضافة وحشٍ إليها ينبغي تفاديه؛ ولهذا ابحث عن القاموس 'Kitchen'، وأضف إليه الغرض 'monster' الذي سيمثل الوحش كما أضفت الغرض السابق 'key'، وتأكد من وضع فاصلةٍ "," في نهاية السطر الذي يسبق السطر الجديد الذي ستضيفه:

                        'item' : 'monster'

حتى يكون الوحش قادرًا على مهاجمة اللاعب، لا بدّ من إضافة منطقٍ لهذه العملية؛ لذلك انتقل إلى آخر سطرٍ في البرنامج، وأضِف الأسطر التالية بما فيها السطر الذي يبدأ بالرمز "#". ستساعدك أسطر التعليقات على فهم برنامجك عندما تعود إليه بعد فترةٍ من الزمن، ولا تنسى إزاحة الأسطر بصورةٍ مناسبة:

# player loses if they enter a room with a monster    
if 'item' in rooms[currentRoom] and 'monster' in rooms[currentRoom]['item']:
    print('A monster has got you... GAME OVER!')        
    break

انقر على أيقونة Run مجددًا، ثم حاول الدخول إلى المطبخ، ستكون نهايتك.

monster_in_kitchen_20.png

شكل 20-5 وحش في المطبخ

لجعل اللعبة ذات معنىً، سنضيف أغراضًا جديدةً وغرفةً أخرى ووسيلةً للفوز تقتضي بمغادرة المنزل بسلام وبحوزتك كل الأغراض.

ابدأ بإضافة غرفةٍ أخرى إلى المتغير rooms كما أضفت غرفة الطعام 'Dining Room' وسمِّها هذه المرة 'Garden'، وأضف مخرجًا إليها من غرفة الطعام 'Dining Room'؛ لهذا عليك أولًا وضع فاصلةٍ "," في نهاية السطر الأخير من القاموس 'Dining Room' ثم إضافة السطر التالي:

                      'south' : 'Garden'

قبل القوس "{" في نهاية 'Dining Room' وأضف فاصلةً "," بعده.

بعد أن وضعت مخرجًا إلى الغرفة 'Garden'، أضف هذه الغرفة بعد 'Dining Room' بكتابة الشيفرة التالية:

   'Garden' : {                  
                       'north' : 'Dining Room' 
                      }

أضف الغرض 'potion' إلى القاموس 'Dining Room' ولا تنسى الفاصلة "," في نهاية السطر الذي يسبقه:

                       'item' : 'potion'

انتقل أخيرًا إلى نهاية البرنامج وأضف الشيفرة التي تتحقق من امتلاك اللاعب كل الأغراض لإبلاغه بالفوز.

# player wins if they get to the garden with a key and a potion    
if currentRoom == 'Garden' and 'key' in inventory and 'potion' in inventory:
    print('You escaped the house... YOU WIN!')

انقر أيقونة Run وحاول إنهاء اللعبة بالتقاط الغرضين 'key' و'potion' قبل التوجه إلى الحديقة. لا تدخل إلى المطبخ فالوحش موجودٌ هناك. ولإضفاء نكهةٍ إلى اللعبة، يمكنك إضافة إرشاداتٍ تساعد اللاعب على إنهاء اللعبة؛ ولتنفيذ الأمر انتقل إلى بداية البرنامج، وابحث عن الدالة ()showInstructions وأضف النص التالي ضمنها:

Get to the Garden with a key and a potion 
Avoid the monsters!

ستظهر الإرشادات السابقة في بداية برنامجك بمجرد تشغيل اللعبة.

تهانينا، لقد أنجزت لعبة متاهةٍ مبنيةٍ على أوامر نصية.

player_instructions_21.png

شكل 21-5 يعرف اللاعب الآن ما عليه فعله

اقتباس

تحدٍ- توسيع اللعبة: هل تستطيع إضافة غرفٍ أخرى لتطول مدة اللعبة؟ هل يمكنك إضافة غرضٍ يحميك من الوحش؟ كيف ستضيف سلاحًا للقضاء على الوحش؟ هل يمكنك إضافة غرفٍ أعلى وأسفل الغرف الموجودة تصل إليها عبر درج؟

ترجمة -وبتصرف- للفصل الخامس Programming with python من كتاب THE OFFICIAL Raspberry Pi Beginner’s Guide.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...