لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 12/28/21 في كل الموقع
-
3 نقاط
-
سَعِدت البارحة سعادةً بالِغةً بكتابٍ جديدٍ يُثري المكتبة العربيَّة طرحه الزَّميل والصَّديق عبَّاد ديرانيَّة في اليوم العالميِّ للغة العربيَّة، وعُنوانه "فنُّ التَّرجمة والتَّعريب"، وهمَّمت بقراءته بشغفٍ فأنهيته في أَقلَّ مِن يومٍ. يُناقِش هذا الكتاب مسألةً جوهريَّةً تمسُّ كُلَّ مَن يَعمَل على الحدود بين حضارتين أو ثقافتين مُتباينتين، فيَنقُل مِن فكر إِحداها إُِلى الأخرى، ويَأخذ مِن لغة الأُخرى إِلى الأُولى، وهذه المسألة هي: متى يَكُون هذا النَّقل ترجمةً وتعريباً ومتى لا يَكُون كذلك؟ وما هي المعايير اللازمة لضبطه فيُصبِح عملاً موضوعياً معياريَّاً بعيداً عن الذاتية والفوضى؟ يَبتدِئ هذا الكتاب بمُقدِّمةٍ أَدبيَّةٍ بحثيَّةٍ رفيعة المُستوَى، فيها نقاشٌ مُطوَّلٌ عن أَصل اللُّغة والمَعنى الكامِن فيها، ثُمَّ يَنتقل إِلى تعريف التَّرجمة وتَوضِيح معناها ليُبيِّن كيف استُعملت هذه الأَداة تاريخيَّاً حتَّى مَطلَع القرن العشرين في نقل المَعارِِف والعُلُوم بين الحضارات والأُمم، ويلي ذلك عرضٌ لدور العرب والمُسلمين في هذه الحركة الإِنسانيَّة، خاصَّةً في العصر الذَّهبيِّ لِلحضارة الإِسلاميَّة الذي كانت جوهرته بغدادُ وواسطة عقده بيت الحكمة، وتُغطِّي هذه المواضيع فُصُولاً ثلاثاً في مَطلَع الكِتاب. يأخذنا المُؤَلِّف بعد ذلك في باب جديد هو نظرية الترجمة فيعرض لنا الصراع التاريخيِّ بين القائلين بحرفيَّة التَّرجمة والقائِلين بالنَّقل بتصرفٍ ويَعُود بنا إِلى جذور هذا الجدال غير المنتهي، ثُمَّ يَستعرِض عَدَداً وافِراً مِن آفات التَّرجمة الحديثة على اللُّغة العربيَّة وتأثيرها الكبير على الفهم والمَعنَى ويَختُم هذا الباب بفصلٍ مُخصَّصٍ لِلمُعجَمات مِن لسان العرب إِلى أُكسفورد، لِيُبيِّن لنا فيها ضعف العمل المُعجميِّ العربيِّ، لا بل توقُّفه شبه التَّام، والحاجة الماسة إِلى تطويره وتحديثه، وهذه مَسألةٌ مُلِّحة، وعلى مجامع اللُّغة والخُبراء العرب في المَجالات كافةً التَّصدِّي لها والردَّ عليها بما يَلزَم مِن مُعجَماتٍ ومساردَ للأََلفاظ الحديثة. ويَنتقِل المُؤَلِّف بعد ذلك إِلى الجانب التَّعليميِّ، فيَطرَح في فصولٌ مُتلاحِقةٍ كيفيَّة تعريب المُفرَدة والتَّركيب والجملة ثُمَّ يَنتهِي بفصلٍ شيق لتعريب الثَّقافة نفسِها، وهذا الباب، بفصوله كُلِّها، مِن أَمتع ما قرأت، فهو مُختصرٌ في طُوله، كثير المعلومات، فيه عصارة العصارة وخلاصة الخلاصة وخبرةٌ كبيرةٌ تفاجأت بوجُودها لدى المُؤَلِّف الَّذي لم يُكمِل عقده الثَّالِث بعدُ. ومع أني أتفق اتفاقاً عاماً مع أغلب ما جاء في الكتاب، لكني أختلف اختلافاً كبيراً معه في درجة التشدد في منهج العمل المقترح، وأرى أن المُؤَلِّف يَتساهل في كثير من الأمورٌ ويُخفِّف فيها عن عاتِق المُترجِم، ويَترُك باب الاجتهاد له مُوارَباً، وإِِن كان يُحذِّر مِن مَغبَّته، وذلك لأني أجد بعد عملي لعقدٍ مِن الزَّمن في التَّرجمة والتَّعريب أَنَّ التَّشدُّد في هذا الباب شرٌّ لا بُدَّ مِنه، والالتزام بالفصيح والبليغ أَمرٌ لا يَلزَم التَّساهل فيه أَبداً، ولكن هذا يَبقى رأينا الشَّخصيَّ في مسألةٍ تَحتمِل الاختلاف. يَسدُّ هذا الكِتاب عموماً ثغرة كبيرة في المكتبة العربية، فلم أقع من قبلُ على كتاب يُناقِش هذه المسائِل كُلَّها في إِطارٍ واحِدٍ جامِعٍ مانِعٍ، وهُو زادٌ ضروريٌ لِكُلِّ مَن يَبتدِئ العمل في التَّرجمة والتعريب لِيُعينه على شقِّ طريقه بيده وليُجيب على أَسئلته الَّتي قد يَحتاجُ، مِن غير هذا الكتاب، إلى أَعوامٍ ليَصِل إِلى إِجابتها الوافِية.2 نقاط
-
هذه المقالة هي الجزء الأول من سلسلة من الصفر إلى مدير المنتج، وهي مجموعةٌ من المقالات التي تُساعدك على تبوّأ وظيفة مدير المنتجات. سنشرح في هذه المقالة عمل مدير المنتج، واختلاف دوره الوظيفيّ حسب اختلاف مجال الشركات، وسنتحدث أيضًا عن أفضل الصفات التي تجعل منك مدير منتجاتٍ متفوّق. هل تطمح إلى تغيير مسارك المهني والعمل كمدير منتجات، لكنك لا تدرك كيفية بدء هذا التحوّل؟ الجواب في هذه المقالة. لقد شهد دور مدير المنتج صعودًا بارزًا في مدةٍ لا تزيد عن 20 سنة، لذا من الطبيعي ألا تملك فهمًا واضحًا عن الدور الذي يلعبه مدير المنتج، وإليك هذا التعريف المثالي من شركة أتلاسيان للبرمجيات Atlassian، والذي يُوفر فهمًا أساسيًا حول وظيفة مدير المنتج. سنوفّر لك خطواتٍ وأدواتٍ بسيطة تتيح لك ارتقاء هذا المسار المهني المشوّق، حيث ستدرك بعد قراءة هذه المقالة المعرفة الكاملة حول ماهية مدير المنتج، والوظائف التي لا يؤديها، إضافةً إلى الأنواع الأربعة من مديري المنتجات، وهم مدير منتجات المستهلك ومدير المنتجات الداخليّة ومدير منتجات التعاملات بين الشركات ومدير المنتجات التقنيّة، كما سنتعرّف على الصفات الأربع التي يتسم بها مدير المنتجات المتفوّق وهي التواصل والمعرفة التقنية وعقلية حلّ المشكلات والقيادة. وظائف مدير المنتج أصبحت رائجة أكثر من أي وقت مضى نقتبس مقولةً رائعة من إحدى المقالات على موقع Productboard: كان هذا المسمى الوظيفي مبهمًا لفترةٍ ليست بالبعيدة، ولم يكن بمقدور الناس وضع تعريفٍ له؛ أما اليوم، فقد أصبح مدير المنتج أكثر الوظائف رواجًا في السوق، فعلى سبيل المثال، نَمَت نسبة وظائف مدير المنتج في الولايات المتحدة بنسبة 32%، منذ شهر أغسطس عام 2017 وحتى شهر يونيو عام 2019، وهذا النمو أعلى بمعدل 5 مراتٍ موازنةً بنمو باقي الوظائف إجمالًا (وفق دراسة أجراها نيل آير ونشرها على ميديام). أظهرت وظائفُ معينةٌ نسبًا مختلفة، فتزايد متوسط الاهتمام الشهري بمنصب مدير المنتج بنسبة 425% على مدار السنوات الخمس الماضية، وفق ما أظهرته مؤشرات جوجل (حسب منظمة Product-Led) مصدر الصورة: نيل آير على ميديام قبل التعرّف على النصائح التي ستتيح لك الحصول على منصبٍ وظيفيٍ في إدارة المنتج، لنتعرّف على وظيفة مدير المنتج ونحدّد دوره. وظائف لا يؤديها مدير المنتج بعد أن تعرّفنا على وظائف مدير المنتجات، سنتطرّق للأمور التي لا تُعَد من واجباته وما ليس عليه القيام به. مدير المنتج ليس مديرا لنبدأ بالدور الأكثر غموضًا، إذ لا يبدو الفرق واضحًا من ناحية المسمى الوظيفي، لكنّ مدير المنتج ليس مديرًا (إذا أخذنا المصطلح التقليدي للمدير بالحسبان)، حيث لا تتمحور وظيفة مدير المنتج الرئيسة حول إدارة الفريق ونموّه فحسب، بل يهتم بإدارة ونمو المنتج، وتشمل وظيفة مالك المنتج تعظيم قيمة المنتج، وفقًا لما جاء بموقع scrum.org. لو عملت مدير منتجات، ستدرك فورًا أن مهنتك تتطلب امتلاك فريقٍ مُتحفّز يحقّق مستويات أداءٍ عالية، فمن الضروري حينها أن يشعر أعضاء الفريق بقيمة الإنجازات التي يحققونها في العمل، والشغف بالمهمة التي يؤدونها. مدير المنتج ليس مهندسا جاء هذا التصوّر الخاطئ من حقيقةٍ مفادها أن معظم أرباب العمل ومنشورات الوظائف أيضًا، يطلبون من المتقدمين امتلاك درجةٍ جامعيةٍ في علوم الحاسوب، وينطبق هذا الأمر على نوعٍ محدّد من مديري المنتجات الذين سنتحدث عنهم لاحقًا، حيث يُعَدّ امتلاك خلفيةٍ تقنيةٍ قويةٍ ميّزةً إضافيةً بارزة عند التقدّم للوظيفة، وتساعدك الخلفية التقنية على فهم كيفية إنجاز المنتج، والعراقيل المحتملة التي قد تواجهها. مع ذلك، مدير المنتج ليس مهندسًا، فهو مسؤولٌ عن ماهية المنتج، لا كيفية عمله، ويحدّد مدير المنتج نوع المنتج الذي ترغب الشركة في إنجازه لتعظيم قيمته بنظر المستخدمين، وفي المقابل، تقع مسؤولية تنفيذ المنتج وإنجازه على عاتق فريق التطوير، بالإضافة إلى اختيار التقنية المستخدمة لتنفيذ هذا المنتج. مدير المنتج ليس مصمما يحتاج مدير المنتج الكفء إلى فهم احتياجات المستخدم، فيساعد امتلاك خلفيةٍ كافية في تجربة المستخدم على تصميم منتجٍ مشابهٍ لما يطلبه المستخدمون؛ مع ذلك، لا يحتاج مدير المنتج إلى إتقان التصميم أو احتراف برامج أدوبي وسكتش، لكن يُمكن لمدير المنتج، حسب الشركة التي يعمل ضمنها والدور المنوط به، تفويض مصمّمي واجهة المستخدم وتجربة المستخدم لأداء هذه المهمات. مصدر الصورة: AltexSoft الفروقات في أدوار مدير المنتج بين مختلف الشركات سنوضح في هذا القسم الالتباس بين وظيفة مدير المنتج لدى شركات البرمجيات، والوظيفة التقليديّة لمدير المنتج لدى شركات السلع الاستهلاكيّة سريعة التداول، حيث يعمل الأخير ضمن قسم التسويق، كما لا يملك مدير المنتج -والمسؤول عن خط إنتاج مستحضر غسول الشعر (شامبو) لدى شركة لوريال مثلًا- أدنى فكرة عن مبادئ تطوير البرمجيات أجايل أو تجربة المستخدم أو مفهوم تعظيم قيمة المنتج بنظر المستخدم. بدلًا من ذلك، يستطيع مدير المنتج هذا إخبارك عن سوق مستحضرات غسول الشعر وآخر الحملات التسويقيّة للشركة أو استراتيجية البيع بالتجزئة التي تتبعها الشركة على سبيل المثال، إذ يرتقي مدير المنتج هذا في السلم الوظيفي ليحصل على منصب مدير خط الإنتاج، مثل (منتج HairColor من لوريال باريس)، ثم يحصل أخيرًا على ترقيةٍ وظيفيّة ليصبح مدير علامةٍ تجارية (علامة لوريال باريس)، حيث يعمل مدير المنتج هنا ضمن مسارٍ مهنيٍ ينتمي إلى قسم التسويق، ومع أن المسمى الوظيفي هو ذاته، لكن يختلف دوره بشدّة عن دور مدير منتج أجايل مثلًا، والذي يعمل غالبًا لدى شركات البرمجيات، وهو الدور الوظيفي الذي سنتحدث عنه في مقالتنا. من هو مدير المنتج؟ مدير المنتج هو الشخص المسؤول عن نجاح المنتج، وقد يكون هذا المنتج تطبيقًا أو خطًا من المنتجات أو قسمًا فرعيًا من المنتج (قسم من الموقع الإلكتروني). إليك أيضًا بضع مسؤوليات يتحملّها مدير المنتج: بحث المستخدم. إعداد خارطة الطريق. تحليل البيانات. إدارة المشروع. وضع الاستراتيجية. إدارة أصحاب المصلحة. إدارة الوظائف المتداخلة. الأنواع الأربعة من مديري المنتجات هناك أربعة أنواع من الوظائف التي يؤديها مديرو المنتجات؛ تختلف تلك الأنواع حسب نوع المنظمة التي يعمل مدير المنتج لديها، والمنتجات التي توفرها تلك المنظمة. 1. مدير منتجات المستهلك وهو المسؤول عن تصميم المنتجات لعموم المستهلكين، حيث يُعَد تطبيق تيك توك مثالًا عن تلك المنتجات. مسؤولياته الرئيسة خلق الأفكار. الاختبار والتجريب. تحليل البيانات. بحث المستخدم لترتيب أولويات العمل على المزايا الجديدة. 2. مدير المنتجات الداخلية يصمّم المنتجات للمستخدمين داخل الشركة ذاتها، مثل البرمجيات التي يستخدمها قسم خدمة العملاء. مسؤولياته الرئيسية إدارة المشاريع. التنفيذ. لا يحتاج إلى الكثير من التصميم، بافتراض أن البرنامج داخلي. 3. مدير منتجات التعاملات بين الشركات يصمّم هذا المدير المنتجات التي ترغب شركاتٌ أخرى باستخدامها، ويُعَد مدير المنتجات في منصة هبسبوت HubSpot مثالًا ملائمًا، وهي منصة تسويقٍ ضمنيٍ مؤتمتة. وتتسم منتجات التعاملات بين الشركات بالمزايا الرئيسية التالية: التداول المحدود والتكلفة العالية، والبيع عبر فرق المبيعات. عددٌ أقل من المستخدمين النهائيين (موازنةً بمنتجات المستهلكين العاديين)، مع أن عدد المستخدمين النهائيين قد يكون ضخمًا لدى بعض الشركات الناجحة، مثل شركة سيلز فورس. التركيز على الجانب التنفيذي. التحدّي الرئيسي الذي يواجهه مدير المنتج هو اختلاف المستخدم النهائي للمنتج (المبيعات) عن صنّاع قرار الشراء (التمويل). 4. مدير المنتجات التقنية يركّز على إدارة المنتج تقنيًا، ويُعَد مدير منتجات جوجل الذي يعمل على خوارزمية البحث مثالًا. تخصصات مدير المنتج التقني قد يكون التخصص نوعًا ثانويًا متفرعًا عن أنواع أخرى. حل التحديات التقنيّة المعقّدة. إليك هذا العرض البياني من شركة ماكنزي، والذي يقدّم تصنيفًا مختلفًا: مصدر الصورة: Academy Xi أربع صفات يتحلى بها مدير المنتج المتفوق قرأنا سابقًا عن أنواع المختلفة لمديري المنتجات، وتعرّفنا على المهارات والخلفيات المتنوّعة التي يجب أن يمتلكها كلّ نوع، ومع ذلك، يجب على مدير المنتج أن يتحلّى ببعض الصفات الشاملة، والتي تساعد أيّ شخصٍ على تقلّد منصب مدير المنتج. التواصل: يمكنك التواصل عبر البريد الإلكتروني أو الهاتف أو الاجتماعات الشخصية، إذ يجب عليك إذا كنت تعمل مديرَ منتجات، التحدّث مرارًا مع أصحاب المصلحة الخارجيين والداخليين، بهدف جمع ردودهم والتعبير عن رؤيتك، وتذكّر أن أفضل المحاوِرين هم الأشخاص القادرون على الإصغاء. المعرفة التقنية: لا يُشترط امتلاك درجةٍ جامعيةٍ في مجال الهندسة، لكن امتلاك المعرفة التقنية يُسهّل عمل مدير المنتج، ويحتاج الأخير إلى فهم التقنية ومصطلحاتها، والقدرة على التحدّث عن تلك التقنية مع أعضاء الفريق. حل المشكلات: يجب على مدير المنتج امتلاك مهارة حل المشكلات، فهو يبحث عن التحديات المعقّدة التي تتطلب التفكير خارج الصندوق؛ فالابتكار والمرونة ليست مجموعةً من المهارات، بل هي عقليّةٌ وطريقة تفكير. القيادة: صحيحٌ أن مدير المنتج ليس مديرًا، لكنه يتمتّع بمهارات القيادة؛ فالقائد البارع يحرص على الاستماع إلى الفريق والتعرّف على مخاوفه، ويتقبّل الانتقادات ويحاول تحسين دوره والمنتجَ المسؤول عنه، كما يطبّق القائد الشفافية داخل الفريق والمنظمة ككل، وهو الذي يتحمّل مسؤولية المنتج، إلى جانب حرصه أيضًا على تقدير جهود الآخرين حتى يتمكّن من تأسيس فريقٍ يتميّز بدرجةٍ عاليةٍ من التحفيز. مصدر الصورة: Kent-Teach نتمنى أن تساعدك المقالة في حال أردت الانتقال إلى منصب مدير المنتج. إذا كنت مهتمًا بتعلّم المزيد، اقرأ الجزأين الثاني والثالث من هذه السلسلة. ترجمة -وبتصرُّف- لمقالة What Product Managers do and How to become one Part 1 — From Zero to Product Manager لصاحبها Eduardo Mignot. اقرأ أيضًا كيف تصبح القائد الذي يعزز الأصالة في العمل؟ كيف تصبح مديرا ناجحا؟ الأساليب الستة للقيادة وطرق تطبيقها على فريق موظفيك شرح عملية القيادة مفهوم القيادة: الفرق بين القائد والمدير1 نقطة
-
عندما أقوم بتنفيذ أي إستعلام بـ sqlalchemy فيتم إرجاع النتائج من قاعدة البيانات وقد تحتوي هذه القائمة على نتيجة واحدة فقط أو قد تحتوي على مجموعة من النتائج وأحيانًا أخرى قد لا توجد أي نتائج على الإطلاق. ما أريد القيام به هو الحصول على أول نتيجة فقط إذا لم يكن هناك غيرها، أو تنفيذ دالة ما في حالة كان هناك أكثر من نتيجة أو لا يوجد نتائج على الإطلاق ما أريد القيام به مشابهة للتالي: result = session.query(User) if result > 1: return "more than one" elif result == 0: return "no results found" return jsonify(result) كيف أقوم بالتحقق مما إذا كان هناك نتيجة واحدة أو مجموعة من النتائج أو لا يوجد نتائج أبدًا؟1 نقطة
-
أحاول الحصول على اسم الدالة التي يتم تنفيذها عند زيارة أحد المسارات حتى أقوم بعمل logger بسيط يخزن المسار واسم الدالة التي تم تنفيذها عند زيارة هذا المسار، فعلى سبيل المثال: @app.route("/") def foo(): # أريد الحصول على الكلمة foo كيف أحصل على اسم الدالة foo عند زيارة هذا المسار؟1 نقطة
-
في مشروع فلاسك Flask لدي، يعمل أكثر من مسار على نفس الدالة، بالشكل التالي: @app.route("/users/") @app.route("/show-users/") def show_users(): .... كيف يمكنني الحصول على المسار الحالي الذي إستخدمه العميل للوصول إلى هذه الدالة؟ أي كيفية الحصول علىأحد المسارين /show-users/ أو /users/ داخل الدالة؟1 نقطة
-
أقوم بالحصول على قائمة من المستخدمين عبر sqlalchemy من خلال الكود التالي: users = session.query(user.name).filter(and_(user.email == email, user.password == password_hash)) لكن كيف يمكنني أن أقوم بإرجاع أول مستخدم فقط يتم إيجاده وليس كل المستخدمين؟1 نقطة
-
لكي يتم إضافة التدوينات في الأقسام المحددة، تحتاج إلى تعديل الدالة lastPosts و الدالة createLastPosts وكذلك تحتاج إلى إنشاء مصفوفة عامة global بالشكل التالي: var IDs = []; function lastPosts(json) { var ID = IDs.shift(); // للحصول على أول عنصر في المصفوفة وحذفه من المصفوفة الأصلية // ... var printall = document.getElementById(ID); printall.innerHTML += printArticle; } function createLastPosts(url, count, printId) { IDs.push(printId); // لإضافة عنصر جديد إلى المصفوفة // ... } الآن يمكنك أن تقوم بتشغيل الدالة createLastPosts أكثر من مرة وسوف يتم إضافة التدوينات في الأماكن المحددة: <div class="container"> <div class="wrapper"> <div class="content"> <div id="demo1" class="show-sidebar grid grid-3"></div> <div id="demo2" class="show-sidebar grid grid-3"></div> <div id="demo3" class="show-sidebar grid grid-3"></div> </div> </div> </div> <script> createLastPosts('https://mo-222.blogspot.com', '1', 'demo1'); createLastPosts('https://mo-222.blogspot.com', '2', 'demo2'); createLastPosts('https://mo-222.blogspot.com', '3', 'demo3'); </script> طريقة أفضل لكن الطريقة السابقة متوقفة على ترتيب تنفيذ السكريبتات في المتصفح ولا يمكننا أن نضمن أن يتم تنفيذهم بالترتيب (قد يقوم المتصفح بتنفيذ السكريبتات حسب سرعة تحميل كل سكريبت) وبالتالي يمكن أن نجد أن التدوينات يتم إضافتها في أماكن عشوائية، ولحل هذه المشكلة يمكن أن تقوم بتجربة طريقة مختلفة عن إستخدام مصفوفة عامة، حيث نقوم بإضافة خاصية باسم data-position في كل عنصر script وتحمل id الخاص بالعنصر الذي سيتم إضافة التدوينات فيه، بالشكل التالي: function lastPosts(json) { // للحصول على الخاصية data-position من السكريبت الحالي (الذي قام بإستدعاء هذه الدالة) var ID = document.currentScript.getAttribute('data-position') // ... var printall = document.getElementById(ID); printall.innerHTML += printArticle; } } function createLastPosts(url, count, printId) { // ... ثم يتم إستدعاء الدالة createLastPosts بنفس الطريقة: <div class="container"> <div class="wrapper"> <div class="content"> <div id="demo1" class="show-sidebar grid grid-3"></div> <div id="demo2" class="show-sidebar grid grid-3"></div> <div id="demo3" class="show-sidebar grid grid-3"></div> </div> </div> </div> <script> createLastPosts('https://mo-222.blogspot.com', '1', 'demo1'); createLastPosts('https://mo-222.blogspot.com', '2', 'demo2'); createLastPosts('https://mo-222.blogspot.com', '3', 'demo3'); </script> الملف بعد تعديله: last-post.html1 نقطة
-
لا يوجد فترة زمنية محددة فالأمر غير محدد بتوقيت معين لأن كل شخص لديه قدرات وامكانيات تختلف من شخص لآخر , تستطيع انجازها في اسبوع وربما في شهر وربما أكثر , الذي يحدد الوقت هو جهدك الذي تبذله فيها , بالطبع ترتيب الموضوعات مهم حيث تم إعداد الدورة على أيدي نخبة من المطورين على أسس صحيحة ينبغي الالتزام بها لكي تصل الى الهدف المرجو من الدورة, بالنسبة لاختبار نفسك في الدورة سوف تلاحظ أنه يوجد بعض المسارات تقدم بعض التمارين والتطبيقات, اذا قمت بتنفيذها بشكل جيد وصحيح فذلك يعتبر أمر جيد , وأيضا ينبغي أن تكون قد فهمت كل شيء تم ذكره في الدروس فإذا كنت بالفعل تفهم كل شيء فذلك جيد أيضا1 نقطة
-
إن لم يكن لديك composer مسبقًا، فستحتاج أولًا إلى تثبيت composer من خلال الأمر التالي: pkg install composer ثم يمكنك أن تقوم بمحاولة تثبيت أحد الحزم كتجربة في مجلد جديد، من خلال مثل هذا الأمر: composer require monolog/monolog ثم يمكنك التأكد من أن عملية التثبيت تمت بنجاح من خلال عرض محتويات الملف composer.json، حيث سيحتوي على اسم الحزمة وإصدارها أيضًا. أما إن لم تستخدم composer من الأساس، فستحتاج إلى أن تقوم بعمل ما يقوم به composer يدويًا، مثل البحث عن المكتبات وإختيار الإصدار المناسب والمتوافق مع إصدار php لديك وكذلك الإصدار المتوافق مع باقي المكتبات المثبته بالفعل .. إلخ.1 نقطة
-
الملف vendor/autoload.php يتم توليده من قِبل Composer وهو عبارة عن أداة تقوم بإدارة الحزم والمكتبات الخاصة بلغة PHP في المشروع، يمكنك تحميل وتثبيت Composer من هنا لماذا تستعمل Composer؟ إذا كنت تعمل على أحد المشاريع وتستخدم العديد من الحزم والمكتبات في مشروع، فستجد أنك تقوم ببعض الخطوات الروتينية في كل مرة تريد إضافة أو تحديث مكتبة في المشروع، حيث تقوم بتحميل المكتبة من الإنترنت وتضيفها إلى المشروع حسب طريقة الإستدعاء الخاصة بها، وعليك أن تتأكد بنفسك من الإصدار الذي تريد أن تعمل عليه (وتبحث عنه في الإنترنت أيضًا)، كما يجب أن تقوم بإستخدام الدالة require و الدالة include لإستدعاء المكتبة ولن تستطيع إستخدام namespace بسهولة. وهنا يأتي دور Composer حيث يقوم بكل هذه الخطوات بصورة تلقائية، فعلى سبيل المثال لتثبيت المكتبة monolog/monolog (مكتبة خاصة بالتسجيل logging)، كل ما عليك هو تنفيذ الأمر التالي في مجلد المشروع: composer require monolog/monolog وسيقوم Composer بالبحث في موقع packagist (أكبر موقع يحتوي على مكتبات لغة PHP بشكل مجاني) إلى أن يجد المكتبة المطلوبة ويتأكد من الإصدار المستخدم في المشروع (يمكنك تحديد إصدار معين عند عملية التثبيت)، وسيقوم Composer بتحميل الإصدار الصحيح في مجلد vendor حيث سيتم عمل المجلد monolog/monolog في داخله وستجد فيه كل ملفات المكتبة، ثم سينشئ Composer ملف autoloader.php لجميع المكتبات المحمّلة وسيحمّل الاعتمادية كاملةً في المشروع الذي تعمل عليه، وبالتالي ليس عليك سوى إستدعاء هذا الملف فقط لتستطيع إستخدام كل المكتبات التي تم تثبيتها بشكل مباشر. الملفات التي يتم إنشائها تلقائيًا من خلال Composer يقوم Composer بإنشاء بعض الملفات الإضافية، وهي ملفات أساسية لكي يعمل (يتم إنشاء هذه الملفات أول مرة فقط، وبعد ذلك يتم التعديل عليها فقط عند تثبيت أي مكتبة جديدة). من ضمن تلك الملفات ستجد الملف composer.json وهو يحتوي على كل المكتبات التي تم تثبيتها في المشروع مع إصدارها الحالي. محتوى الملف composer.json: { "require": { "monolog/monolog": "^2.3" } } وستجد الملف composer.lock، وهو ملف يحتوي على كل المكتبات والحزم التي تم تثبيتها بالتفصيل (إصدار PHP المطلوب لتعمل المكتبة ومصدر المكتبة ومالك المكتبة وحقوقها .. إلخ)، وكذلك المكتبات والحزم الإضافية التي تحتاجها المكتبات. يمكنك الآن أن تستعمل المكتبة monolog/monolog كالتالي: <?php require __DIR__ . '/vendor/autoload.php'; $log = new Monolog\Logger('name'); $log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING)); $log->warning('Foo'); لاحظ تم إستخدام الأصناف Monolog\Logger و Monolog\Handler\StreamHandle بشكل مباشر، وذلك لأننا إستخدمنا الملف vendor/autoload.php في بداية الملف، حيث يقوم بتجهيز وإستدعاء كل الملفات المطلوبة تلقائيًا. الكود السابق سوف ينشيء ملف باسم app.log وستم كتابة بعض البيانات فيه: [2021-12-01T19:11:32.629010+00:00] name.WARNING: Foo [] [] إدارة المكتبات من خلال Composer يمكنك أيضًا أن تقوم بتحديث المكتبات من خلال الأمر التالي: composer update عند تنفيذ هذا الأمر، فسيبحث composer عند أحدث إصدار لكل مكتبة يمكن تثبيته، ويقوم بتحديث المكتبات. أو يمكن حذف أحد المكتبات من خلال الأمر remove واسم المكتبة التالي: composer remove monolog/monolog لمزيد من المعلومات عن Composer يمكنك أن تقرأ هذه المقالات هنا:1 نقطة
-
يمكنك أن تستعمل الحزمة chromedriver-binary لإضافة chromedriver إلى PATH في البداية عليك تثبيت الحزمة من خلال الأمر التالي: pip install chromedriver-binary ثم يمكنك أن تستعملها بالشكل التالي: from selenium import webdriver import chromedriver_binary # عليك إستدعاء الحزمة كالتالي driver = webdriver.Chrome() driver.get("https://www.google.com")1 نقطة
-
يمكن أيضًا إستعمال التابع extract والذي يقوم بإستخراج العنصر من المستند: from bs4 import BeautifulSoup content = """<!DOCTYPE html> <html> <head><title>html</title></head> <body> <div id="x"> <p> This is child of div with id = "x". <span>Child of "P"</span> </p> <div> Another Child of div with id = "x". </div> </div> <p> aaadas </p> </body> </html> """ soup = BeautifulSoup(content, features='lxml') e = soup.select_one('div#x') # تحديد العنصر من خلال محدد CSS # إزالة العنصر من الصفحة e.extract() # المستند بعد إزالة العنصر منه print(soup.prettify()) """ <!DOCTYPE html> <html> <head> <title> html </title> </head> <body> <p> aaadas </p> </body> </html> """ # العنصر الذي تم إزالته print(e.prettify()) """ <div id="x"> <p> This is child of div with id = "x". <span> Child of "P" </span> </p> <div> Another Child of div with id = "x". </div> </div> """1 نقطة
-
يمكنك أن تستعمل خدمات مثل wappalyzer لفحص المواقع ومعرفة التقنيات التي تستخدمها، وستجد أن الموقع يستعمل Webpack و Angular كأدوات لإتصدير ملفات المشروع بالإضافة إلى مكتبة Bootstrap الإصدار v4.3.1 و مكتبة أيقونات font-awesome الإصدار 5.15.3 وستجد أن الموقع يستعمل خدمات أخرى مثل Google Analytics و TypeScript و ipify و recaptcha وتستطيع التأكد من كل هذه الأداوات والمكتبات من خلال تفحص الكود المصدري للموقع، حيث ستجد في الأعلى روابط لمكتبة font-awesome وملف styles الذي يحتوي على إصدار Bootstrap في داخله. وعند قراءة أي ملف JavaScript يستعمله الموقع فستجد أن كل أكواده موجودة بشكل مضغوط للغاية ولكن عند البحث عن كلمة Angular على سبيل المثال في ملف main.js (آخر ملف JavaScript مرفق في الصفحة) ستجد أن الكلمة موجودة فعلًا في أكثر من كائن مما يثبت أن الموقع يستعمل Angular كواجهة للموقع. تستطيع أن تقرأ عن التطبيقات ذات الصفحة الواحدة وتجربة إنشاء واححدة بإستخدام jQuery فقط من خلال هذه المقالة:1 نقطة
-
تستطيع تحديد ما إذا كان العنصر فارغًا أم لا من خلال الخاصية contents الخاصة به، وبالتالي يمكننا المرور على كل عناصر الصفحة عبر التابع find_all والتحقق مما إذا كان العنصر فارغ أم لا: def checkEmpty(tag): return not tag.contents and not tag.name == 'br' # يقبل التابع find_all تمرير دالة له [tag.decompose() for tag in soup.find_all(checkEmpty)] لكن إن أحتوى العنصر على بعض المسافات فسوف يتم تخطيه ولن يتم حذف، لذلك يمكن أن نغير في الكود السابق ليتم حذف العناصر التي تحتوي على مسافات فقط في داخلها: from bs4 import BeautifulSoup content = """ <p> <p></p> <strong>some<br>text<br>here</strong> </p> """ soup = BeautifulSoup(content, features='lxml') def checkEmpty(tag): return (not tag.contents or len(tag.get_text(strip=True)) <= 0) and not tag.name == 'br' # إزالة كل العناصر الفارغة [tag.decompose() for tag in soup.find_all(checkEmpty)] print(soup) """ <html><body> <strong>some<br/>text<br/>here</strong> </body></html> """ لاحظ أن عنصر p لا يجوز أن يحتوي على عنصر p آخر في داخله، لذلك تقوم مكتبة BeautifulSoup بإغلاق العناصر تلقائيَا،و يتم تحويل المستند إلى هذا الشكل: <html><body><p> </p><p></p> <strong>some<br/>text<br/>here</strong> </body></html> وبالتالي سيتم حذف العنصر p الأول والثاني وسيتبقى فقط العنصر strong داخل جسم الصفحة1 نقطة
-
يمكنك إستخراج البيانات بشكل بسيط ومرتب من خلال تحديد كل الكتب ثم المرور على كل كتاب وإستخراج بياناته، كالتالي: from bs4 import BeautifulSoup import pandas as pd content = open("books.xml",'r', encoding="utf-8").read() soup = BeautifulSoup(content, features='lxml') # هنا سيتم تخزين بيانات كل كتاب data = [] # نحدد كل الكتب books = soup.find_all('book') # الآن نقوم بالمرور على كل كتاب وتخزين بياناته في القائمة data for book in books: book_d = {} # بيانات الكتاب الواحد book_d['author'] = book.find('author').get_text() book_d['title'] = book.find('title').get_text() book_d['genre'] = book.find('genre').get_text() book_d['price'] = book.find('price').get_text() book_d['publish_date'] = book.find('publish_date').get_text() book_d['description'] = book.find('description').get_text() data.append(book_d) # الآن نقوم بتحويل القائمة data إلى dataFrame df = pd.DataFrame(data) # لا نحتاج إلى تحديد الأعمدة لأننا أستخدمنا قائمة من القواميس dictionaries print(df) وستكون النتيجة كالتالي: author title genre price publish_date description 0 Gambardella, Matthew XML Developer's Guide Computer 44.95 2000-10-01 An in-depth look at creating applications with... 1 Ralls, Kim Midnight Rain Fantasy 5.95 2000-12-16 A former architect battles corporate zombies, ... 2 Corets, Eva Maeve Ascendant Fantasy 5.95 2000-11-17 After the collapse of a nanotechnology society... 3 Corets, Eva Oberon's Legacy Fantasy 5.95 2001-03-10 In post-apocalypse England, the mysterious age... 4 Corets, Eva The Sundered Grail Fantasy 5.95 2001-09-10 The two daughters of Maeve, half-sisters, batt... 5 Randall, Cynthia Lover Birds Romance 4.95 2000-09-02 When Carla meets Paul at an ornithology confer... 6 Thurman, Paula Splish Splash Romance 4.95 2000-11-02 A deep sea diver finds true love twenty thousa... 7 Knorr, Stefan Creepy Crawlies Horror 4.95 2000-12-06 An anthology of horror stories about roaches,c... 8 Kress, Peter Paradox Lost Science Fiction 6.95 2000-11-02 After an inadvertant trip through a Heisenberg... 9 O'Brien, Tim Microsoft .NET: The Programming Bible Computer 36.95 2000-12-09 Microsoft's .NET initiative is explored in det... 10 O'Brien, Tim MSXML3: A Comprehensive Guide Computer 36.95 2000-12-01 The Microsoft MSXML3 parser is covered in deta... 11 Galos, Mike Visual Studio 7: A Comprehensive Guide Computer 49.95 2001-04-16 Microsoft Visual Studio 7 is explored in depth...1 نقطة
-
يمكنك أن تقوم بعمل عنصر جديد من خلال التابع new_tag ثم تقوم بإضافة العنصر العناصر الأبناء إليه، كالتالي: from bs4 import BeautifulSoup soup = BeautifulSoup('<p> sometexthere </p>', features='lxml') # إنشاء العناصر div_element = soup.new_tag('div', **{"class":"container"}) # يمكنك أن تضيف خصائص أيضًا من هنا i_element = soup.new_tag('i') # إحاطة النص بالعنصر i soup.p.string.wrap(i_element) # إضافة كل العناصر إلى العنصر div الذي قمنا بإنشائه body_children = list(soup.body.children) for child in body_children: div_element.append(child) # إزالة كل العناصر من جسم الصفحة soup.body.clear() # إضافة العنصر div الذي يحتوي على كل العناصر إلى جسم الصفحة soup.body.append(div_element) print(soup.prettify()) """ <html> <body> <div class="container"> <p> <i> sometexthere </i> </p> </div> </body> </html> """1 نقطة
-
إذا كانت الأرباح يتم توزيعها بناءً على نسبة الإستثمار في المشروع فقط فيمكن حساب نسبة كل فرد من الأرباح كالتالي: إجمالي رأس المال في سؤالك هو 15000 + 10000 + 4000 = 29000 على سبيل المثال، نسبة الشخص الذي دفع 15 ألف تساوي 51.72% من صافي الأرباح كالتالي: (15000/29000)*100 = 51.72% // نسبة الشخص الأول (10000/29000)*100 = 34.48% // نسبة الشخضص الثاني (4000/29000)*100 = 13.79% // نسبة الشخص الثالث هذا بفرض أن المساهمة في المشروع تكون برأس المال فقط.1 نقطة
-
يمكن القيام بذلك من خلال جلب كل العناصر التي تحتوي على الخاصية class فقط، ثم المرور على هذه العناصر وإستخراج الأصناف منها، بعد ذلك نقومك بإزالة كل الأصناف المكررة من خلال إستخدام set كالتالي: from bs4 import BeautifulSoup import requests URL = "https://en.wikipedia.org/wiki/2019_Ballon_d%27Or" response = requests.get(URL).content soup = BeautifulSoup(response, features='lxml') # إستخراج العناصر التي لديها الخاصية class classes = [tag.get('class') for tag in soup.find_all(class_=True)] # تستطيح القائمة (جعلها تحتوي على عناصر نصية فقط) classes = sum(classes, []) # السطر السابق يمكن عملة كالتالي أيضًا # classes = [item for sublist in classes for item in sublist] # إزالة كل الأصناف المكرر من خلال إستخدام set class_set = set(classes) print(class_set) """ {'printfooter', 'wb-langlinks-edit', ..... 'tocsection-2', 'mw-selflink', 'mw-portlet-interaction'} """1 نقطة
-
درسنا حتى الآن البرامج جزئية التوجه batch oriented programs، والتي تستدعى بإحدى طريقتين: جزئية التوجه batch oriented، حيث تبدأ البرامج بالعمل ثم تنفذ شيئًا ما ثم تتوقف، أو مدفوعة بالأحداث أو حدثية التوجه event driven، أي تبدأ البرامج وتنتظر وقوع أحداث بعينها؛ ولا تتوقف إلا حين يأمرها حدث آخر بالتوقف. كيف ننشئ برنامجًا مقودًا بالأحداث؟ سنفعل ذلك بطريقتين، حيث سنحاكي أولًا بيئةً حدثيةً، ثم سننشئ برنامجًا رسوميًا بسيطًا يستخدم نظام التشغيل والبيئة لتوليد أحداث. وسنغطي في هذا المقال النقاط التالية: أوجه اختلاف البرامج المدفوعة بالأحداث عن البرامج جزئية التوجه. كيفية كتابة حلقة حدث تكرارية. كيفية استخدام إطار عمل أحداث مثل Tkinter. محاكاة حلقة أحداث تكرارية يحتوي البرنامج المدفوع بالأحداث أو البرنامج الحدثي event driven program على حلقة تلتقط الأحداث المستَلمة وتعالجها، وقد تولد بيئة التشغيل الأحداث -كما يحدث في جميع البرامج الرسومية تقريبًا-، أو يبحث البرنامج عن الأحداث -كما يحدث في أنظمة التحكم المدمجة التي في الكاميرات وغيرها-، وسنكتب برنامجًا يبحث عن نوع واحد من الأحداث، وهو مدخلات لوحة المفاتيح، ويعالج النتائج إلى أن يستلم حدث خروج quit، والذي سيكون حالتنا مفتاح المسافة space على لوحة المفاتيح، وسنعالج الأحداث المدخلة بطريقة سهلة، إذ سنطبع ترميز آسكي ASCII الخاص بالمفتاح الذي ضغطه المستخدم، وسنستخدم بايثون لأنها تحوي دالة getch() سهلة الاستخدام، وسنقرأ المفاتيح مفتاحًا تلو الآخر، وتأتي هذه الدالة في صورتين وفقًا لنظام التشغيل الذي نستخدمه، فنجدها في لينكس في وحدة curses، أما في ويندوز فستكون في وحدة msvcrt، وسنستخدم نسخة ويندوز أولًا ثم نناقش خيار لينكس بالتفصيل، ويجب أن نشغل هذه البرامج من سطر أوامر النظام، لأن بيئات التطوير -مثل IDLE- ستلتقط ضربات لوحة المفاتيح التقاطًا مختلفًا. وسنطبق دوال معالجة الأحداث أولًا، والتي تُستدعى عند التقاط ضغطة أحد المفاتيح، ثم متن البرنامج الرئيسي الذي يبدأ حلقة جمع الأحداث؛ ويستدعي دالة معالجة الحدث المناسبة عند التقاط حدث صالح. تطبيق المثال في ويندوز إليك التطبيق التالي: import msvcrt import sys # معالجات الأحداث أولًا def doKeyEvent(key): if key == '\x00' or key == '\xe0': key = msvcrt.getch() print ( ord(key), ' ', end='') sys.stdout.flush() # make sure it appears on screen def doQuit(key): print() # أدخل سطرًا جديدًا raise SystemExit # أخل مساحة على الشاشة أولًا lines = 25 for n in range(lines): print() # والآن حلقة الحدث الأساسية while True: ky = msvcrt.getch() if len(str(ky)) != 0: # we have a real event if " " in str(ky): doQuit(ky) else: doKeyEvent(ky) لاحظ أن ما نفعله مع الأحداث لا علاقة له بالمتن الرئيسي، الذي يجمع الأحداث ويمررها إلى معالجات الأحداث وحسب، وهذه الاستقلالية في التقاط الأحداث معالجتها هي الميزة الأساسية في البرمجة الحدثية. كما نلاحظ أن getch() تعيد بايتات، لذا نحتاج إلى تحويلها إلى سلسلة نصية، وإلى استخدام اختبار in للتحقق متى يمكن الخروج بما أن السلسلة الناتجة لن تكون حرفًا. وفي الحالة التي لا يكون فيها المفتاح محرف آسكي، كأن يكون مفتاحًا وظيفيًا مثلًا، وهي المفاتيح التي تحمل حرف F في أولها أعلى لوحة المفاتيح، فسنحتاج إلى جلب محرف ثانٍ من لوحة المفاتيح، لأن هذه المفاتيح الخاصة تولد أزواجًا من البايتات، أما getch() فلا تجلب إلى بايتًا واحدًا في كل مرة، وتكون القيمة المهمة التي نريدها هي البايت الثاني فعليًا. تطبيق المثال في لينكس وماك لا يستطيع المبرمجون الذين يستخدمون أنظمة تشغيل لينكس وماك استخدام مكتبة msvcrt، لذا يستخدمون وحدةً أخرى تسمى curses، وتكون الشيفرة الناتجة شبيهةً بشيفرة ويندوز، مع بعض التعديلات التي يجب إجراؤها، كما يلي: import curses as c def doKeyEvent(key): if key == '\x00' or key == '\xe0': # non ASCII key key = screen.getch() # fetch second character screen.addstr(str(key)+' ') # uses global screen variable def doQuitEvent(key): c.resetty() # set terminal settings c.endwin() # end curses session raise SystemExit # امسح الشاشة واحفظ الإعدادات الحالية # وأوقف الطباعة الآلية للمحارف على الشاشة # ثم أخبر المستخدم ما يفعله للخروج screen = c.initscr() c.savetty() c.noecho() screen.addstr("Hit space to end...\n") # والآن تعمل الحلقة الأساسية بلا نهاية while True: ky = screen.getch() if ky != -1: # send events to event handling functions if ky == ord(" "): # check for quit event doQuitEvent(ky) else: doKeyEvent(ky) c.endwin() لا تعمل أوامر الطباعة العادية في وحدة curses، بل يجب أن نستخدم دوال معالجة الشاشة الخاصة بوحدة curses، كما تعيد getch هنا -1 إذا لم يُضغط على أي مفتاح -بدلًا من سلسلة فارغة-، ويطابق منطق البرنامج نسخة ويندوز السابقة فيما عدا ذلك. ويجب أن تستعيد curses.endwin() شاشتنا إلى الحالة العادية، لكنها قد لا تعمل أحيانًا، فإذا اختفى المؤشر أو لم نحصل على محرف إرجاع لبداية السطر أو غير ذلك، فسنصلح المشكلة بالخروج من بايثون باستخدام Ctrl+D واستخدام الأمر التالي: $ stty echo -nl تشير nl إلى "سطر جديد"، وينبغي أن يصلح هذا السطر المشكلة أعلاه حال حدوثها. إذا كنا ننشئ هذا مثل إطار عمل لاستخدامه في مشاريع عدة، فسنضيف استدعاءً إلى دالة تهيئة initialization function في البداية ودالة تنظيف في النهاية، ثم يستطيع المبرمج عندئذ استخدام الجزء الخاص بالحلقة وتوفير دواله الملائمة للتهيئة والمعالجة والتنظيف، وهذا ما تفعله البيئات الرسومية تحديدًا، حيث يكون الجزء الخاص بالحلقة مضمَّنًا في بيئة التشغيل أو إطار العميل، ويكون على البرامج أن توفر دوال معالجة الأحداث الخاصة بها وتربطها بحلقة الأحداث بشكل ما، لنر ذلك عمليًا أثناء دراسة مكتبة Tkinter الرسومية. برنامج رسومي سنستخدم صندوق أدوات Tkinter من بايثون في هذه التدريب، وهو مغلف بايثون لصندوق أدوات Tk، والذي كُتب في البداية امتدادًا للغة Tcl، كما أنه متاح للغتي Perl وروبي أيضًا، ونسخة بايثون منه هي إطار عمل كائني التوجه، العمل فيه أسهل من نسخة Tk الأصلية، وسننظر بتفصيل أكثر في مبادئ برمجة الواجهات الرسومية فيما بعد في مقال قادم، لذا لن نسهب كثيرًا في الحديث عن مبادئ الواجهات الرسومية في هذا المقال، لأننا نريد التركيز على نمط البرمجة نفسه، وهو استخدام Tkinter لمعالجة حلقة الأحداث، ونترك المبرمج ينشئ الواجهة الرسومية الابتدائية، ثم يعالج الأحداث حال وصولها. وفي مثالنا هنا ننشئ صنف تطبيق application class اسمه KeysApp ينشئ الواجهة الرسومية في التابع __init__، ويربط مفتاح المسافة بالتابع doQuitEvent، كما يعرّف الصنف تابع doQuitEvent المطلوب، أما الواجهة الرسومية نفسها فتتكون ببساطة من ودجِت widget (تطبيق مُصغَّر) لإدخال النصوص؛ سلوكها الافتراضي هو طباعة المحارف المدخَلة على الشاشة. إن إنشاء صنف تطبيق في البيئات الحدثية كائنية التوجه أمر شائع، بسبب الارتباط بين مفاهيم الأحداث المرسَلة إلى برنامج ما والرسائل المرسَلة إلى كائن، إذ يرتبطان ببعضهما بسهولة كبيرة، وبذلك تصبح دالة معالجة الحدث تابعًا لصنف التطبيق، وبعد أن عرّفنا الصنف سننشئ نسخةً منه ونرسل إليها رسالة mainloop، وستبدو الشيفرة كما يلي: ً # للحفظ، بما أن علينا تقديم كل شيءfrom X import * استخدم # tkinter.xxx كـ from tkinter import * import sys # أنشئ صنف التطبيق الذي يعرف الواجهة الرسومية وتوابع # معالجة الأحداث class KeysApp(Frame): def __init__(self): # use constructor to build GUI super().__init__() self.txtBox = Text(self) self.txtBox.bind("<space>", self.doQuitEvent) self.txtBox.pack() self.pack() def doQuitEvent(self,event): sys.exit() # والآن أنشئ نسخة وابدأ تشغيل حلقة الحدث myApp = KeysApp() myApp.mainloop() نلاحظ أن البرنامج لن يغلق إغلاقًا صحيحًا عند تشغيل هذه الشيفرة من داخل IDLE، بل سيطبع رسالة إغلاق في نافذة الصدفة، وهو أسلوب IDLE حين يريد أن يساعدنا! أما إذا شغلناها في سطر الأوامر فسيعمل كل شيء بسلاسة. كما نلاحظ أننا لا نطبق معالج أحداث المفاتيح، لأن السلوك الافتراضي لودجِت أو تطبيق النص هو طباعة المفاتيح المضغوطة، لكن هذا يعني أن برامجنا ليست متكافئةً وظيفيًا، فقد طبعنا رموز آسكي في الطرفية بدلًا من طباعة النسخة الأبجدية من المفاتيح القابلة للطباعة كما فعلنا هنا، فليس ثمة شيء يمنعنا من التقاط جميع ضغطات المفاتيح وفعل نفس الشيء، فإذا أردنا تنفيذ هذا فسنضيف السطر التالي إلى تابع __init__: self.txtBox.bind("<Key>", self.doKeyEvent) كما سنضيف التابع التالي لمعالجة الحدث: def doKeyEvent(self,event): str = "%d\n" % event.keycode self.txtBox.insert(END, str) return "break" نلاحظ تخزين قيمة المفتاح في حقل keycode للحدث، وقد اضطررت إلى العودة إلى الشيفرة الأساسية للملف Tkinter.py لمعرفة ذلك، تذكر أن الفضول هو الصفة المميزة للمبرمج. كما نلاحظ أن return "break" هي إشارة سحرية تخبر Tkinter بأن لا يستدعي معالجة الأحداث الافتراضية لهذه الودجِت، وبدون هذا السطر سيعرض الصندوق النصي رمز آسكي متبوعًا بالمحرف الحقيقي الذي ضُغط، وليس هذا ما نريده. إلى هنا يكفي الحديث عن Tkinter، فلم نكن ننو شرحه هنا وإنما في مقال تالٍ. البرمجة الحدثية في VBScript وجافاسكربت نستطيع تطبيق البرمجة الحدثية في كل من جافاسكربت وVBScript عند برمجة متصفح، فعادةً إذا حُمِّلَت صفحة ويب تحتوي على سكربت فإن السكربت ينفَّذ جزءًا جزءًا مع تحميل الصفحة، لكن إذا لم يحوِ السكربت إلا تعريفات الدوال فلن يفعل التنفيذ شيئًا إلا تعريف الدوال على أنها جاهزة للاستخدام، أما الدوال نفسها فلن تُستدعى هنا ابتداءً، بل ستكون مقيدةً إلى عناصر HTML في الجزء الخاص بشيفرة HTML في الصفحة -داخل عنصر Form غالبًا-، بحيث تُستدعى هذه الدوال عند وقوع الأحداث، وقد رأينا هذا في مثال جافاسكربت الخاص بالحصول على مدخلات المستخدم عندما نقرأ المدخلات من استمارة HTML، لننظر في هذا المثال مرةً أخرى؛ ونر كيف أنه تطبيق عملي على برمجة حدَثية في صفحة ويب: <form name='entry'> <p>Type value then click outside the field with your mouse</p> <input Type='text' Name='data' onChange='alert("We got a value of " + document.entry.data.value);'/> </form> نلاحظ عدم وجود تعريف لدالة جافاسكربت، بل مجرد استدعاء لـ alert المرتبطة بسمة onChange لعنصر input، وهذه السمة هي إحدى الأحداث التي تستطيع عناصر HTML توليدها، ونستطيع ربط أي شيفرة جافاسكربت عشوائية لتنفيذها في كل مرة تقع فيها هذه الأحداث، كما يمكن إنشاء دالة واستدعاؤها بدلًا من استدعاء alert كما يلي: <script type="text/javascript"> function echoValue(){ alert("We got a value of " + document.entry.data.value); } </script> <form name='entry'> <p>Type value then click outside the field with your mouse</p> <input Type='text' Name='data' onChange='echoValue()'/> </form> يعرّف الجزء الخاص بالسكربت دالة جافاسكربت هي echoValue تحاكي استدعاء alert الذي كان معنا من قبل، ويحتوي عنصر input الآن على دالة مسندة على أنها معالج الأحداث للسمة onChange، ثم تنفَّذ الدالة عند تغير قيمة الدخل، وتكون حلقة الحدث التي تلتقط الأحداث مضمنةً داخل المتصفح. ورغم أن دالتنا هذه استدعت alert إلا أنها تستطيع أكثر من ذلك، بل من الممكن جعلها برنامجًا معقدًا بذاتها، وذلك وفقًا لما نضعه في متنها. يمكن استخدام VBScript بنفس الطريقة، عدا أن تعريفات الدوال ستكون بـ VBscript بدلًا من جافاسكربت، كما يلي: <script type="text/vbscript"> Sub EchoInput() MsgBox "We got a value of " & Document.entry2.data.value End Sub </script> <form name='entry2'> <p>Type value then click outside the field with your mouse</p> <input Type='text' Name='data' onChange='EchoInput()'/> </form> وبهذا نرى أن الشيفرة الموجهة للمتصفحات يمكن كتابتها في صورة أجزاء batches أو في صورة حدَثية event driven، أو بهما معًا، وفق متطلبات كل حالة. خاتمة نأمل في هذا المقال أن تكون تعلمت ما يلي: لا تبالي حلقات الأحداث بالأحداث التي تلتقطها. تعالج معالجات الأحداث حدثًا واحدًا في كل مرة. توفر أطر العمل -مثل Tkinter- حلقة أحداث، وبعض معالجات الأحداث الافتراضية أحيانًا. يمكن كتابة الشيفرة لمتصفحات الويب بأسلوب مدفوع بالأحداث، أو بالأجزاء، أو بهما معًا. ترجمة -بتصرف- للفصل الثامن عشر: Event Driven Programming، من كتاب Learn To Program لصاحبه Alan Gauld. اقرأ أيضًا المقال التالي: برمجة الواجهات الرسومية باستخدام Tkinter المقال السابق: البرمجة كائنية التوجه تعلم البرمجة ما هي البرمجة ومتطلبات تعلمها؟1 نقطة
-
يمكنك استخدام أي لغة من اللغتين، لغة جافاسكريبت قديمًا كانت تعمل في المتصفح فقط في بناء الواجهات الأمامية للمواقع. و لكن الآن أصبحت تستخدم في البرمجة الخلفية أيضا بفضل بيئة العمل node.js"و هي runtime environment مكنتنا من استخدام جافاسكريبت خارج المتصفح" و إطار العمل express"إطار عمل لبناء ال backend". و لكن يوجد فرق يجعل javascript تتفوق على php في برمجة المواقع. و هي طريقة تنفيذ الأكواد حيث أن لغة جافاسكريبت non blocking أي أنها لا تنتظر أن يتم تنفيذ عملية ما حتى تنتقل إلى التي تليها مثل php مما يجعل أداؤها أفضل من php. كما أن لغة javascript مجالها أوسع حيث يمكنك بناء تطبيقات ويب و تطبيقات هواتف باستخدام مكتبة react native و تطبيقات سطح مكتب باستخدام مكتبة electron و بناء تطبيقات virtual reality و يمكن استخدامها في مجال تعلم الآلة و مجال برمجة المتحكمات الدقيقة و الروبوتات و غيرها كل هذا باستخدام لغة واحدة فقط و هذا ما لا توفره لغة php. و في النهاية اختيار التقنيات يتوقف على المشروع الذي تريد بناؤه.1 نقطة