لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 12/07/22 في كل الموقع
-
لدي دومين مستضاف لدى HostGator والايميل استخدمت فيه 365 اوفيس يوجد مشكلة مع ايميل واحد تفشل الرسائل الصادرة مني دوما بالرغم من اني استقبل اي رسالة من قبلهم وعند السؤال كان التبرير هو وجود IP ضمن قوائم الحظر وقد الملفات ذات الصلة فكيف يمكن حل هذه المشكلة Undeliverable_ Emails .eml1 نقطة
-
سنتناول خلال هذا المقال برنامجًا معقدًا بعض الشئ عما رأيناه مسبقًا، فقد كانت غالبية الأمثلة التي تعرَّضنا إليها مجرد أمثلة بسيطة هدفها توضيح تقنية برمجية أو اثنتين على الأكثر؛ أما الآن، فقد ان الوقت لتوظيف كل تلك الأفكار والتقنيات معًا ضمن برنامجٍ واحدٍ حقيقي. سنناقش أولًا البرنامج وتصميمه بصورةٍ مُبسطة، وبينما نفعل ذلك، سنتحدث سريعًا عن بعض خاصيات جافا التي لم نتمكَّن من الحديث عنها أثناء المقالات السابقة؛ إذ تتضمَّن تلك الخاصيات أمورًا يُمكِن تطبيقها على جميع البرامج وليس فقط على برامج واجهات المُستخدم الرسومية GUI. سنتحدث تحديدًا عن برنامج عرض مجموعة ماندلبرو Mandelbrot، والذي يَسمَح للمُستخدِم باكتشاف تلك المجموعة الشهيرة، إذ سنبدأ أولًا بشرح ما يعنيه ذلك. تتوفَّر نسخةٌ أقوى من هذا البرنامج؛ ونسخةٌ أخرى منه مكتوبةٌ بلغة JavaScript، وقابلةٌ للتشغيل بمتصفحات الإنترنت. مجموعة ماندلبرو Mandelbrot مجموعة ماندلبرو هي مجموعةٌ من النقاط الواقعة على سطح مستوى xy، التي تُحسَب مواضعها بواسطة عمليةٍ حسابية؛ وكل ما تحتاج إلى معرفته لكي تتمكَّن من تشغيل البرنامج هو أن تَعرِف إمكانية استخدام هذه المجموعة لصناعة مجموعةٍ من الصور الرائعة. سننتقل الآن إلى التفاصيل الحسابية: لنفترض لدينا نقطة (a,b)، وكان الإحداثي الأفقي والرأسي لتلك النقطة مكونين من أعدادٍ حقيقية، عندها يُمكِننا إذًا تطبيق العمليات التالية عليها: Let x = a Let y = b Repeat: Let newX = x*x - y*y + a Let newY = 2*x*y + b Let x = newX Let y = newY تتغير إحداثيات النقطة (x,y) أثناء تنفيذ حلقة التكرار loop بالأعلى، ويَنقلنا ذلك إلى السؤال التالي: هل تزداد قيم إحداثيات النقطة (x,y) دون أي قيد أم أنها تقتصر على منطقة نهائية ضمن المستوى؟ إذا كانت (x,y) تذهب إلى اللانهاية (أي تزداد بدون قيد)، فإن نقطة البداية (a,b) لا تنتمي إلى مجموعة ماندلبرو؛ أما إذا كانت النقطة (x,y) مقتصرةً على منطقة نهائية، فإن نقطة البداية (a,b) تنتمي إلى المجموعة. من المعروف أنه لو أصبح 'x2 + y2' أكبر من '4' ضمن أي لحظة، فإن النقطة (x,y) تذهب إلى اللانهاية، وبالتالي لو أصبح 'x2 + y2' أكبر من '4' بالحلقة المُعرَّفة بالأعلى، فيُمكِننا أن نُنهِي الحلقة، ونستنتج أن النقطة (a,b) ليست ضمن مجموعة ماندلبرو بلا شك. في المقابل، بالنسبة لنقطة (a,b) تنتمي إلى تلك المجموعة، فإن الحلقة لن تنتهي أبدًا. إذا شغَّلنا ذلك على حاسوب، فإننا بالطبع لا نريد أن نحصل على حلقة لا نهائية تَعمَل إلى الأبد، ولذلك سنضع حدًا أقصى على عدد مرات تنفيذ الحلقة، وسيُمثِّل maxIterations ذلك الحد. ألقِ نظرةً إلى ما يلي: x = a; y = b; count = 0; while ( x*x + y*y < 4.1 ) { count++; if (count > maxIterations) break; double newX = x*x - y*y + a; double newY = 2*x*y + b; x = newY; y = newY; } بعد انتهاء الحلقة، وإذا كانت قيمة count أقل من أو تُساوِي maxIterations؛ فعندها يُمكِننا أن نستنتج أن النقطة (a,b) لا تنتمي إلى مجموعة ماندلبرو؛ أما إذا كانت قيمة count أكبر من maxIterations، فقد تنتمي النقطة (a,b) إلى المجموعة أو لا؛ وفي العموم كلما كانت قيمة maxIterations أكبر، كلما زادت احتمالية انتماء النقطة (a,b) إلى المجموعة. سنُنشِئ صورةً باستخدام العملية الحسابية السابقة على النحو التالي: سنَستخدِم شبكةً مستطيلةً من البكسلات لتمثيل مستطيلٍ واقعٍ على سطح المستوى، بحيث يتوافق كل بكسل مع إحداثيات قيمها حقيقية (a,b)، إذ تُشير قيم الإحداثيات إلى مركز البكسل. سنُشغِّل حلقة التكرار بالأعلى لكل بكسل؛ فإذا تعدَّت قيمة count قيمة الحد الأقصى maxIterations، فسنُلّون البكسل باللون الأسود، مما يعني أن تلك النقطة قد تنتمي إلى مجموعة ماندلبرو؛ أما إذا لم يتعداه، فسيعتمد لون البكسل على قيمة count بعد انتهاء الحلقة، مما يَعنِي أننا سنَستخدِم ألوانًا مختلفةً للقيم المختلفة من count. كلما ازدادت قيمة count، كانت النقطة أقرب إلى مجموعة ماندلبرو؛ وبالتالي تُعطِي الألوان بعض المعلومات عن النقاط الواقعة خارج المجموعة وعن شكل المجموعة، ولكن من المهم أن تدرك أن تلك الألوان عشوائية وأن النقاط الملونة لا تنتمي إلى المجموعة. تَعرِض الصورة التالية لقطة شاشة من برنامج عرض مجموعة ماندلبرو، الذي يَستخدِم نفس تلك العملية الحسابية؛ إذ تُمثِّل المنطقة السوداء مجموعة ماندلبرو، باستثناء أن بعض النقاط السوداء قد لا تكون ضمن المجموعة فعليًا. إذا شغَّلت البرنامج فبإمكانك تكبير الصورة حول أي منطقة صغيرة ضمن سطح المستوى، وذلك بنقر زر الفأرة على الصورة مع السحب؛ إذ يؤدي ذلك إلى رسم "صندوق تكبير" مستطيل الشكل حول جزء الصورة ذاك كما هو مُوضَّح بالأعلى. وعندما تُحرِّر زر الفأرة، سيزداد حجم جزء الصورة الموجود داخل صندوق التكبير لكي يملأ الشاشة بالكامل؛ أما إذا نقرت على أي نقطة ضمن الصورة، فسيزداد حجم الصورة أيضًا عند النقطة التي نقرت عليها بمعدل تكبير يساوي الضعف. انقر على "Shift" أو اِستخدِم زر الفأرة الأيمن لكي تُصغرّ حجم الصورة. تُعدّ النقاط الموجودة عند الحدود الفاصلة لمجموعة ماندلبرو هي النقاط الأكثر تشويقًا، وفي الحقيقة، يُعَد ذلك الفاصل معقدًا تعقيدًا لا نهائيًا؛ فإذا كبَّرت الصورة إلى حدٍ بعيد، فلن يمنعك البرنامج من فعل ذلك، ولكنك ستتعدّى إمكانيات نوع البيانات double، وستبدأ بكسلات الصورة في الظهور وستصبح الصورة بلا معنى. يُمكِنك استخدام قائمة "MaxIterations" لزيادة الحد الأقصى لعدد مرات تكرار الحلقة، وتذكّر أن البكسلات السوداء قد تقع أو لا ضمن المجموعة؛ فإذا أزدت قيمة MaxIterations، فقد تجد أن بعض المناطق السوداء قد أصبحت ملونةً هي الأخرى. تُحدِّد قائمة "Palette" مجموعة الألوان المُستخدَمة، ويؤدي استخدام لوحات ألوان مختلفة إلى إنتاج صورٍ مختلفة، ولكنها مُجرّد مجموعة ألوان عشوائية فقط. وتحدِّد قائمة "PaletteLength" عدد الألوان المختلفة المُستخدَمة؛ فإذا اِستخدَمت الإعدادات الافتراضية، فإن كل قيمة ممكنة للمتغير count يُقابلها قيمة لونٍ مختلفة. قد تحصل أحيانًا على صور أفضل بكثير باستخدام عددٍ مختلف من الألوان؛ فإذا كان عدد الألوان الموجودة بلوحة الألوان المُستخدَمة أقل من قيمة maxIterations، فسيتكرَّر نفس اللون لكي يشمل جميع القيم المحتملة للمتغير count؛ أما إذا كان عددها أكبر من قيمة maxIterations، فسيُستخدَم فقط جزءٌ من الألوان المُتاحة. لذلك، إذا كانت غالبية بكسلات الصورة من خارج المجموعة مُكوَّنة من درجات مختلفة من لون واحد تقريبًا، فقللّ عدد ألوان لوحة الألوان؛ إذ يؤدي ذلك إلى اختلاف الألوان بصورةٍ أسرع مع تغيُّر قيمة count؛ أما إذا وجدتها مُكوَّنةً من ألوان عشوائية تمامًا بدون أي انسيابية بين الألوان، زِد عدد ألوان لوحة الألون. يحتوي البرنامج على قائمة "File" يُمكِن استخدامها لحفظ الصورة مثل ملف صورة بامتداد PNG؛ ويُمكِنك أيضًا أن تحفظ ملف "param" لحفظ إعدادات البرنامج التي أنتجت الصورة الحالية؛ وتستطيع لاحقًا قراءة هذا الملف إلى البرنامج باستخدام الأمر "Open". يعود اسم مجموعة ماندلبرو إلى Benoit Mandelbrot، وهو أول من لاحظ ذلك التعقيد المذهل لتلك المجموعة، إذ من الرائع الحصول على هذا التعقيد والجمال من تلك الخوارزمية البسيطة. تصميم تطبيق مجموعة ماندلبرو نجد معظم الأصناف بلغة جافا مُعرَّفةً ضمن حزم packages؛ فعلى الرغم من أننا استخدمنا بعض الحزم القياسية، مثل javafx.scene.control و java.io بكثرة، إلا أن معظم الأمثلة التي تعرَّضنا إليها كانت تَستخدِم الحزمة الافتراضية، ويَعنِي ذلك أننا لم نُصرِّح بانتمائها إلى أي حزمةٍ مسماة. وفي المقابل، عند إنجاز أي برمجة جدية، فمن الأفضل دومًا أن نُنشِئ حزمةً لاحتواء الأصناف المُستخدَمة بالبرنامج. تُوصِي مؤسسة Oracle بأن تكون أسماء الحزم مبنيةً على اسم نطاق domain name الإنترنت للمؤسسة المُنتِجة للحزمة؛ فبالنسبة لحاسوب المؤلف، فإن اسم النطاق الخاص به هو eck.hws.edu، ولا يُفترَض لأي حاسوب آخر بالعالم عمومًا أن يكون له نفس الاسم؛ ووفقًا لمؤسسة Oracle، ينبغي أن يكون اسم الحزمة في تلك الحالة هو edu.hws.eck، أي سيكون ترتيب عناصر اسم النطاق معكوسًا. علاوةً على ذلك، ينبغي أن تُسمَى الحزم الفرعية ضمن تلك الحزمة بأسماءٍ، مثل edu.hws.eck.mdbfx، وهو في الواقع الاسم الذي اختاره المؤلف لتطبيق عرض مجموعة ماندلبرو. ويَضمَن ذلك ألا يَستخدِم أي شخصٍ آخر -شرط أن يتبِع نفس نمط التسمية- نفس اسم الحزمة، وبناءً على ذلك، يُمكِن استخدام ذلك الاسم الفريد لتحديد هوية التطبيق. ناقشنا باختصار طريقة استخدام الحزم بمقال بيئات البرمجة programming environment في جافا، وكذلك أثناء شرح بعض الأمثلة البرمجية بمقال أمثلة برمجية على الشبكات في جافا: إطار عمل لتطوير الألعاب عبر الشبكة. باختصار، يُمثِل التالي كل ما ينبغي أن تعرفه بخصوص تطبيق عرض مجموعة ماندلبرو، فالبرنامج مُعرَّفٌ ضمن 7 ملفات شيفرة مصدرية تجدها بالمجلد edu/hws/eck/mdbfx الموجود داخل مجلد source بالموقع؛ أي أن الملفات موجودة بمجلدٍ اسمه mdbfx، والموجود بدوره بمجلدٍ اسمه eck، المتواجد بمجلد hws، ضمن مجلد edu. لا بُدّ أن تتبِع المجلدات اسم الحزمة بتلك الطريقة. يحتوي نفس ذلك المجلد على ملفٍ اسمه strings.properties المُستخدَم بالبرنامج والذي سنناقشه بالأسفل. ويحتوي مجلد examples على ملفات الموارد التي تَستخدِمها قائمة "Examples". ونظرًا لاعتماد البرنامج على مكتبة JavaFX، فينبغي أن تتأكّد من توفُّر المكتبة عند تصريف البرنامج أو تشغيله كما ناقشنا بمقال بيئات البرمجة (programming environment) في جافا المشار إليه في الأعلى؛ وإذا كنت تَستخدِم بيئة تطوير متكاملة Integrated Development Environment مثل Eclipse، فكل ما عليك فعله هو إضافة مجلد "edu" إلى المشروع، مع ضبطه لكي يَستخدِم مكتبة JavaFX؛ وإذا أردت استخدام سطر الأوامر، فينبغي أن يشير مجلد العمل working directory إلى المجلد المُتضمِّن لمجلد edu؛ وإذا لم تكن تَستخدِم إصدارًا قديمًا من JDK والذي تكون فيه مكتبة JavaFX مبنيةً مسبقًا، فستحتاج إلى إضافة خيارات مكتبة JavaFX إلى أمري javac و java. إذا كنت قد عرَّفت الأمرين jfxc و jfx المكافئين للأمرين javac و java مع خيارات مكتبة JavaFX، فيُمكِنك ببساطة أن تُصرِّف الشيفرة المصدرية باستخدام الأمر التالي: jfxc edu/hws/eck/mdbfx/*.java أو الأمر التالي إذا كنت تَستخدِم نظام التشغيل Windows: jfxc edu\hws\eck\mdbfx\*.java ستَجِد صنف التطبيق الرئيسي مُعرَّفًا بالصنف Main. اِستخدِم الأمر التالي لتشغيل البرنامج: jfx edu.hws.eck.mdbfx.Main يجب أن يُنفِّذ هذا الأمر بالمجلد المُتضمِّن لمجلد edu؛ وإذا كان إصدار JDK المُستخدِم يتضمَّن مكتبة JavaFX مُسبَقًا، فيُمكِنك ببساطة أن تَستخدِم الأمرين javac و java بدلًا من jfxc و jfx. يتضمَّن الملف MandelbrotCanvas.java غالبية العمليات المطلوبة لحساب صور مجموعة ماندلبرو وعرضها؛ إذ يُعدّ الصنف MandelbrotCanvas صنفًا فرعيًا من الصنف Canvas، ويُمكِنه حساب صور مجموعة ماندلبرو وعرضها كما ناقشنا بالأعلى. تعتمد الصورة الناتجة على النطاق الظاهر من قيم x و y، وعلى الحد الأقصى لعدد مرات تكرار الخوارزمية، وعلى لوحة الألوان المُستخدَمة لتلوين البكسلات خارج المجموعة. وتأتي جميع تلك المُدْخَلات من مكان آخر ضمن البرنامج، ويقتصر دور الصنف MandelbrotCanvas على حساب الصورة وعرضها بناءً على قيم المْدخَلات المُعطاة له. بالإضافة إلى تلوين بكسلات الصورة، يَستخدِم الصنف MandelbrotCanvas مصفوفةً ثنائية الأبعاد لتخزين قيمة count لكل بكسلٍ في الصورة؛ إذ تُحدِّد قيمة count لبكسل معين مع لوحة الألوان المُستخدَمة، اللون المستعمل لتلوين ذلك البكسل كما ناقشنا بالأعلى. وفي حالة تغيير لوحة الألوان المُستخدَمة، سيَستخدِم البرنامج قيمة المتغير count لكل بكسل لإعادة ضبط الألوان، دون أن يعيد حساب مجموعة ماندلبرو مرةً أخرى. في المقابل، إذا تغير نطاق قيم x و y، أو إذا تغير حجم النافذة، فسيضطّر البرنامج لإعادة حساب قيم count لجميع البكسلات. قد تستغرِق عملية حساب تلك القيم وقتًا طويلًا، ولأنه من غير المُفترَض أن نُعطِّل block واجهة المُستخدِم أثناء إجراء تلك الحسابات، سيُجرِي البرنامج تلك العمليات بخيطٍ عاملٍ worker thread منفصل كما ناقشنا بمقال البرمجة باستخدام الخيوط threads في جافا، إذ يَستخدِم البرنامج تحديدًأ خيطًا عاملًا وحيدًا لكل معالج. عند بدء عملية حساب تلك القيم، ستكون الصورة شفافة، أي أنه يُمكِنك رؤية الخلفية الرمادية للنافذة. تُقسَّم العملية إجمالًا إلى مجموعةٍ من المهام tasks، وتتكوَّن كل مهمة من عملية حساب صف واحد من الصورة. وبعد انتهاء أي مهمة، تُطبِّق الألوان الناتجة على البكسلات الموجودة بالصف الخاص بتلك المهمة. نظرًا لأنه من الممكن تعديل الحاوية فقط من خلال خيط تطبيق JavaFX، فستَستدعِي كل مهمة التابع Platform.runLater() لإجراء التغييرات المطلوبة، وهذا يُمكِّن المُستخدِم من الاستمرار باستخدام القوائم وحتى الفأرة أثناء عملية حساب الصورة. يحتوي الملف MandelbrotPane.java على كامل محتوى نافذة تطبيق عرض مجموعة ماندلبرو، إذ يُعدّ الصنف MandelbrotPane صنفًا فرعيًا من الصنف BorderPane. يحتوي منتصف كائن الحاوية المنتمي للصنف BorderPane على كائنٍ من النوع MandelbrotCanvas. وفي الحقيقة، يَستخدِم البرنامج حاويةً ثانيةً شفافةً فوق الحاوية المتضمِّنة للصورة؛ فعندما يَرسِم المُستخدِم "صندوق تكبير" باستخدام الفأرة، فإن ذلك الصندوق يُرسَم فعليًا بالحاوية العلوية لكي لا يشوه الصورة (ألقِ نظرةً على مقال أمثلة عن رسوميات فاخرة باستعمال جافا). في المقابل، تحتوي المنطقة السفلية من الحاوية على عنوان من النوع Label، يَعمَل مثل شريطٍ لعرض حالة البرنامج، إذ يُستخدَم لعرض بعض المعلومات التي قد تَهِم المُستخدِم. أخيرًا، يحتوي البرنامج على شريط قوائم أعلى الحاوية. يُعرِّف الصنف Menus.java -المُشتَق من الصنف MenuBar- شريط القوائم الخاص بالتطبيق. (ألقِ نظرةً على مقال بناء تطبيقات كاملة باستعمال مكتبة جافا إف إكس JavaFX لمزيدٍ من المعلومات عن القوائم وعناصر القوائم، كما يُعرِّف الصنف Menus مجموعةً من التوابع والأصناف الفرعية المتداخلة nested subclasses لتمثيل جميع عناصر القائمة، وكذلك الأوامر التي تُمثِّلها تلك العناصر؛ إذ تشتمل تلك الأوامر على أوامرٍ مُتعلِّقة بمعالجة الملفات وتَستخدِم التقنيات التي تَعرَّضنا لها بمقالات مدخل إلى التعامل مع الملفات في جافا ومقدمة مختصرة للغة XML واستعمالها في تطبيقات جافا وأمثلة عن رسوميات فاخرة باستعمال جافا المشار إليه بالأعلى. تحتوي القوائم "MaxIterations" و "Palette" و"PaletteLength" على مجموعةٍ من الكائنات المنتمية إلى النوع RadioMenuItems. يُعرِّف البرنامج صنفًا متداخلًا داخل الصنف Menus لتمثيل كل مجموعة؛ فعلى سبيل المثال، يُعرِّف الصنف PaletteManager عناصر قائمة "Palette" على هيئة متغيرات نسخة instance variables، ويُسجِّل معالج حدث لكل عنصر، كما يُعرِّف القليل من البرامج المفيدة لمعالجة القائمة. تتشابه الأصناف الخاصة بالقوائم الثلاثة، جتى أنه من الأفضل تعريفها على أنها أصنافٌ فرعيةٌ مشتقةٌ من صنفٍ أكثر عمومية. ويحتوي البرنامج أيضًا على قائمة "Examples" التي تتضمَّن الإعدادات الخاصة ببعض العينات لقطعٍ من مجموعة ماندلبرو. يُنفِّذ الصنف MandelbrotPane كثيرًا من العمل الذي يتطلّبه البرنامج؛ فهو يُهيئ معالجات لأحداث الفأرة MousePressed و MouseDragged و MouseReleased بالحاوية العلوية، ليُمكِّن المُستخدِم من تكبير الصورة وتصغيرها؛ كما يُهيئ معالجًا للحدث MouseMoved، الذي يُحدِّث شريط الحالة ويجعله يَعرِض إحداثيات النقطة المقابلة للمكان الحالي لمؤشر الفأرة على الصورة. يُولَّد الحدث MouseMoved عندما يُحرِّك المُستخدِم مؤشر الفأرة دون أن يضغط باستمرار على زرها. ويُستخدم كذلك الحدث MouseExited لإعادة ضبط شريط الحالة إلى كلمة "Idle" عندما يقع مؤشر الفأرة خارج الحاوية. بالإضافة إلى ما سبق، يُنفِّذ البرنامج أوامر قوائم أخرى كثيرة باستدعاء توابع مُعرَّفةٍ بالصنف MandebrotPane. ويحتوي الصنف Menus على متغير نسخة اسمه owner، يشير إلى الحاوية -من النوع MandelbrotPane- المُتضمِّنة لشريط القوائم، وبالتالي يُمكِنه استخدام ذلك المتغير لاستدعاء أي توابع مُعرَّفة بالصنف MandelbrotPane؛ إذ يضبُط التابع setLimits() مثلًا، نطاق قيم x و y الظاهرة بالصورة؛ كما أن هناك توابعٌ أخرى لضبط كُلٍ من لوحة الألوان المُستخدَمة وعدد الألوان الموجودة بلوحة الألوان، والحد الأقصى لعدد مرات تكرار الخوارزمية. بمجرد تغيُّر أي من تلك الخاصيات، لا بُدّ من تعديل الصورة المعروضة لمجموعة ماندلبرو؛ فعلى سبيل المثال، عندما تتغير لوحة الألوان المُستخدَمة أو عدد الألوان الموجودة باللوحة، يَحسِب الصنف MandelbrotPane لوحةً جديدةً من الألوان ويَستدعِي تابعًا مُعرَّفًا بالصنف MandelbrotCanvas ليُبلِّغه بأن عليه استخدام تلك اللوحة الجديدة. وفي المقابل، عندما يتغير الحد الأقصى لعدد مرات تكرار الخوارزمية، تكون إعادة حساب الصورة بالكامل ضرورية، ولهذا يَستدعِي الصنف MandelbrotPane التابع startJob() المُعرَّف بالصنف MandelbrotCanvas ليُبلِّغه بأن عليه أن يبدأ وظيفةً جديدة، ويتولى الصنف MandelbrotCanvas كل العمل اللازم لتهيئة تلك الوظيفة وإدارتها. يُمرَّر كائن الصنف MandelbrotPane المُستخدَم بالبرنامج مثل معاملٍ إلى باني الصنف Menus، ويُخزِّن بدوره كائن الصنف Menus المُمثِّل للقوائم نسخةً من ذلك الكائن بهيئة متغير نسخة، اسمه owner. في الواقع، يُعالِج الصنفان MandelbrotPane و MandelbrotCanvas غالبية أوامر القوائم، ولكي يتمكَّن الكائن المُمثِّل للقوائم من تنفيذ تلك الأوامر، فإنه يحتاج إلى مرجع reference إلى كائن الصنف MandelbrotPane. وبالمثل من الصنف MandelbrotCanvas، يُعرِّف كائن الصنف MandelbrotPane التابع getDisplay() الذي يعيد مرجعًا إلى الحاوية التي يحتويها، وبالتالي يستطيع الكائن المُمثِّل للقوائم الحصول على مرجعٍ إلى الحاوية باستدعاء owner.getDisplay(). كنا نضع شيفرة البرنامج بالكامل بالأمثلة السابقة من هذه السلسلة بملفٍ واحدٍ كبير، وبالتالي كانت جميع الكائنات متاحةً لكل أجزاء الشيفرة مباشرةً. وفي المقابل، عند تقسيم البرنامج إلى مجموعة من الملفات، لا يكون الوصول إلى الكائنات الضرورية بهذه السهولة. تُعدّ الأصناف MandelbrotPane و MandelbrotCanvas و Menus أكثر الأصناف أهميةً بتطبيق عرض مجموعة ماندلبرو؛ إذ يُعرِّف الصنف Main.java الصنف الفرعي المُشتَق من الصنف Application، والذي ينبغي تشغيله عند تنفيذ البرنامج؛ ويضع تابعه start() كائنًا من النوع MandelbrotPane داخل المرحلة stage الرئيسية للبرنامج. يحتوي البرنامج على ثلاثة أصناف أخرى، إذ يُعرِّف الصنفان SetImageSizeDialog.java و SetLimitsDialog.java صناديق نوافذ مخصَّصة، والتي لن نناقشها هنا أكثر من ذلك؛ أما الصنف الأخير، فهو I18n، والذي سنناقشه بالأسفل. أظهرت هذه المناقشة القصيرة لتصميم تطبيق عرض مجموعة ماندلبرو أنه يَستخدِم تشكيلةً واسعةً من التقنيات التي تعرَّضنا لها مُسبقًا خلال مقالات هذه السلسلة، وسنفحص بالجزء المُتبقِي من هذا المقال القليل من الخاصيات الجديدة المُستخدَمة ضمن البرنامج. الأحداث ومستمعي الأحداث والارتباطات تعاملنا مع الأحداث events ومستمعي الأحداث بكثرة، وكذلك مع ارتباط binding الخاصيات القابلة للمراقبة observable ببعض الأمثلة، وسيكون من الرائع لو رأينا طريقة استخدام تلك التقنيات ضمن تطبيق عرض مجموعة ماندلبرو، إذ سنَستخدِم مجموعةً من الأصناف. لنبدأ الآن من الحقيقة التالية: لا يَعرِف الصنف MandelbrotCanvas أي شيء عن الصنف Menus مع أن شريط القوائم يحتوي على عناصر يبدو وكأنها تَعرِف ما يحدث بصنف الحاوية MandelbrotCanvas. بالتحديد، تُعطَّل بعض عناصر القائمة عندما تكون عملية حساب الصورة قيد التنفيذ. بما أن الحاوية لا تستدعِي أي توابع أو تَستخدِم أيًا من متغيرات صنف القوائم Menus، فكيف تمكَّنت القوائم من معرفة ما إذا كانت هناك عمليةً حسابيةً قيد التنفيذ بالحاوية؟ الإجابة بالطبع هي من خلال استخدام الأحداث أو على نحوٍ أكثر دقة، وذلك من خلال استخدام الارتباط (ألقِ نظرةً على مقال البواني وتهيئة الكائنات Object Initialization في جافا). يحتوي الصنف MandelbrotCanvas على خاصية قابلة للمراقبة من النوع boolean اسمها working، إذ تحتوي تلك الخاصية على القيمة true عندما يكون هناك عملية معالجة قيد التنفيذ. وينبغي أن تكون عناصر القائمة مُعطَّلةً عندما تكون قيمة تلك الخاصية مساويةً للقيمة true، وهو ما يُمكِننا إجراؤه بسطر شيفرةٍ واحد يربُط خاصية عنصر قائمة disableProperty بخاصية الحاوية workingProperty. على سبيل المثال، يُمكِننا تطبيق ذلك على عنصر القائمة "saveImage" بكتابة ما يَلي داخل باني الصنف Menus: saveImage.disableProperty().bind(owner.getDisplay().workingProperty()); إذ يشير owner هنا إلى كائن الصنف MandelbrotPane؛ بينما تشير القيمة المُعادة من التابع owner.getDisplay() إلى كائن الصنف MandelbrotCanvas الموجود به. وبالمثل، يُعيد عنصر القائمة "Restore Previous Limits" ضبط نطاق قيم x و y الظاهرة إلى قيمها السابقة قبل آخر تحديث؛ إذ يَستخدِم الصنف Menus متغير النسخة previousLimits من النوع double[] ليتذكر نطاق القيم السابق، ولكن السؤال هو: كيف يحصل على تلك المعلومات؟ عندما يُكبّر المُستخدِم الصورة أو يُصغرّها، يحدث ذلك التغيير بالصنف MandelbrotPane؛ بالتالي لا بُدّ إذًا من وجود طريقة تُمكِّن القوائم من ملاحظة ذلك التغيير، ويكْمُن الحل طبعًا في استخدام خاصيةٍ قابلةٍ للمراقبة، وتكون تلك الخاصية من النوع ObjectProperty<double[]> في هذه الحالة. يُضيف باني الصنف Menus مستمع حدث من النوع ChangeListener إلى الخاصية limitsProperty المُعرَّفة بالصنف MandelbrotPane على النحو التالي: owner.limitsProperty().addListener( (o,oldVal,newVal) -> { // خزِّن القيمة القديمة للمتغير limitsProperty لاستخدامها بالأمر "Restore Previous Limits" previousLimits = oldVal; undoChangeOfLimits.setDisable( previousLimits == null ); }); نظرًا لأننا نَستخدِم الأحداث هنا للتواصل، فإن الصنفين MandelbrotCanvas و MandelbrotPane خفيفا الترابط loosely coupled مع الصنف Menus. في الحقيقة، يُمكِننا استخدامهما دون أي تعديل ببرامج أخرى لا تحتوي على نفس الصنف Menus من الأساس؛ وبدلًا من استخدام الأحداث والارتباط، كان من الممكن جعل صنفي الحاوية والعرض يَستدعِيان توابعًا، مثل limitsChanged() و computationStarted() مُعرَّفين بالصنف Menus. يكون الترابط بين الأصناف في تلك الحالة قويًا strong coupling، وبالتالي سيضطّر أي مبرمج يرغب باستخدام الصنف MandelbrotCanvas إلى استخدام الصنف Menus أيضًا، أو إلى تعديل الصنف MandelbrotCanvas كي لا يُشير إلى الصنف Menus. لا يُمكِننا طبعًا معالجة جميع المشاكل باستخدام الأحداث، كما أنه ليس من الضروري أن يكون أي ترابط قوي شيئًا سيئًا، إذ يشير الصنف MandelbrotPane مثلًا إلى الصنف MandelbrotCanvas مباشرةً ولا يُمكِننا استخدامه بدونه، ولكن بما أن الغرض من كائن الصنف MandelbrotPane هو حمل كائنٍ آخر من الصنف MandelbrotCanvas، فلا يُمثِّل هذا الترابط مشكلةً هنا. وفي المقابل، يُمكِننا استخدام الصنف MandelbrotCanvas على نحوٍ مستقل عن الصنف MandelbrotPane. يُوظِّف الصنف MandelbrotPane الأحداث لغرضٍ آخر؛ إذ تقع حاوية الصورة والحاوية الشفافة ضمن الكائن displayHolder من النوع StackPane، وعندما يُغيّر المُستخدِم حجم النافذة، سيتغير حجم الكائن displayHolder ليتناسب مع الحجم الجديد، وينبغي عندها أن يُضبَط حجم الحاوية لكي يتناسب مع حجم العرض الجديد؛ أي لا بُدّ من بدء عملية المعالجة لحساب صورةٍ جديدة. ولذلك، يُهيئ الصنف MandelbrotPane مستمعين إلى الخاصيات height و width المُعرَّفة بالكائن displayHolder؛ وذلك حتى يتمكَّن من الاستجابة للتغييرات بالحجم. ولكن، عندما يُغيّر المُستخدِم حجم النافذة ديناميكيًا، قد يتغير حجم displayHolder عدة مرات بكل ثانية. ونظرًا لأن بدء عملية حساب صورة جديدة يتطلَّب كثيرًا من الوقت، فإننا بالتأكيد لا نرغب في فعل ذلك عدة مراتٍ بالثانية الواحدة. في الواقع، سيبدأ البرنامج عملية حساب صورة جديدة فقط بعد مرور حوالي ثلث ثانية من توقُّف التغيير بحجم النافذة؛ وبالتالي إذا جرَّبت تغيير حجم نافذة البرنامج، فستلاحظ أن الحاوية لا تُغيِّر حجمها تلقائيًا بتُغيُّر حجم النافذة، ويَعرِض البرنامج نفس الصورة طالما كان الحجم هو نفسه. تُوضِح الشيفرة التالية طريقة فعل ذلك. يُهيئ البرنامج مستمعي أحداث تغيُّر الحجم displayHolder، ليستدعوا التابع startDelayedJob() على النحو التالي: displayHolder.widthProperty().addListener( e -> startDelayedJob(300,true) ); displayHolder.heightProperty().addListener( e -> startDelayedJob(300,true) ); إذ يُمثِّل المعامل الأول للتابع startDelayedJob() الزمن بوحدة الميلي ثانية، الذي لا بُدّ من انتظاره قبل إعادة ضبط حجم الحاوية وبدء عملية حساب جديدة؛ بينما يشير المعامل الثاني إلى أن ضبط حجم الحاوية ليس ضروريًا قبل بدء عملية المعالجة. يَستخدِم البرنامج كائنًا من النوع Timer المُعرَّف بحزمة java.util لكي يتمكَّن من تأجيل تنفيذ العملية؛ إذ يُمكِننا ببساطة أن نُمرِّر كائنًا من النوع TimerTask يُمثِّل مهمةً مؤجَلّةً إلى كائنٍ من النوع Timer، وذلك لكي تُنفَّذ المهمة بعد زمنٍ معين؛ كما يُمكِننا أيضًا إلغاء المهمة إذا لم يكن ذلك الزمن قد مرّ بعد، إذ يَستخدمِ البرنامج التابع startDelayedJob() لإضافة مهمة تغيير حجم الحاوية إلى المؤقت، لتُنفَّذ بعد 300 ميلي ثانية. وفي حالة استدعاء التابع startDelayedJob() مرةً أخرى قبل مرور 300 ميلي ثانية، فستُلغَى المهمة السابقة تلقائيًا وتُضاف المهمة الجديدة بدلًا منها إلى المؤقت. وبذلك، تكون مُحصلة ما سبق هو عدم تنفيذ أي مهمة إلى أن تَمرّ 300 ميلي ثانية دون أي استدعاء جديدٍ للتابع ()startDelayedJob. يتيح البرنامج خيار ضبط الصورة لتكون ثابتة الحجم؛ إذ لا ينبغي تلك الحالة أن يتغير حجم displayHolder نهائيًا. وبناءً على ذلك، قد تكون الصورة صغيرةً، ولا تتمكَّن من ملئ النافذة بالكامل، وستَظهَر عندها أجزاءٌ من الخلفية الرمادية الموجودة وراءها. في المقابل، قد تكون الصورة كبيرةً جدًا على النافذة، وفي تلك الحالة، ينبغي أن تظهر أشرطة تمرير يُمكِن اِستخدامها للمرور عبر كامل الصورة؛ ويُنفِّذ البرنامج ذلك باستخدَام الصنف ScrollPane الذي يُمثِّل حاويةً تحتوي على مكوِّن واجهة معين، ويُوفِّر أشرطة تمرير إذا اقتضت الضرورة؛ أما عندما يكون حجم الصورة ثابتًا، فسيُحذَف displayHolder من الصنف MandelbrotPane، ويُوضَع بكائن الصنف ScrollPane المُمثِّل للحاوية، ثم يُوضَع كائن الحاوية ذلك بمنتصف كائن الصنف MandelbrotPane. المزيد عن واجهات المستخدم الرسومية أخيرًا، سنذكر هنا بعض التفاصيل المُتعلّقة ببرمجة واجهات المُستخدِم الرسومية التي لم نتمكَّن من عرضها بالمقالات السابقة. ذكرنا من قبل أن ملفات الموارد تُمثِّل جزءًا من البرنامج ولكنها لا تتضمَّن أي شيفرة، وقد رأينا على سبيل المثال في مقال التعرف على بعض أصناف مكتبة جافا إف إكس JavaFX البسيطة مدى سهولة اِستخدَام الصور مثل ملفات موارد مع الصنف Image؛ ولكن يُمكِننا عمومًا استخدام أي نوعٍ من البيانات مثل ملفات موارد. يحتوي تطبيق عرض مجموعة ماندلبرو مثلًا، على قائمة "Examples"؛ وعندما يختار المُستخدِم أمرًا من تلك القائمة، تُحمَّل الإعدادات الخاصة بعرضٍ معيَّن لمجموعة ماندلبرو إلى البرنامج؛ وتكون هذه الإعدادات مُخزَّنةً في الواقع بهيئة ملفات موارد بصيغة XML. كيف يَصِل البرنامج إلى تلك الملفات؟ لسوء الحظ، ليس الأمر بنفس سهولة استخدام صورة على أنها ملف مورد لإنشاء كائنٍ من النوع Image. تُخزَّن الموارد بملفات تقع إلى جانب ملفات الأصناف المُصرَّفة الخاصة بالبرنامج؛ إذ يُحدِّد بلغة جافا كائنٌ من النوع ClassLoader -ويُعرَف باسم مُحمِّل أصناف class loader- مكان ملفات الأصناف ويُحمِّلها للبرنامج عند الحاجة. يملُك مُحمِّل الأصناف قائمةً بالمسارات التي ينبغي عليه البحث فيها عن ملفات الأصناف؛ إذ تُعرَف تلك القائمة باسم مسارات الأصناف class path، والتي تتضمَّن موضع تخزين أصناف جافا القياسية، كما تتضمَّن المجلد الحالي. إذا كان البرنامج مُخزَّنًا داخل ملف jar، فسيكون ذلك الملف أيضًا ضمن مسارات الأصناف. وبالإضافة إلى ملفات الأصناف، تستطيع كائنات الصنف ClassLoader العثور على ملفات الموارد الواقعة بمسارات الأصناف أو بمجلداتٍ فرعية داخل مسارات الأصناف. علينا أولًا أن نحصل على كائنٍ من النوع ClassLoader لكي نتمكَّن من استخدام ملفات الموارد؛ إذ يمكننا باستخدام ذلك الكائن أن نُحدِّد موضع ملف موردٍ معين. في العموم، يحتوي أي كائن على تابع النسخة getClass()، الذي يعيد كائنًا يُمثِّل الصنف الذي ينتمي إليه الكائن؛ ويحتوي الكائن المُمثِّل للصنف بدوره على التابع getClassLoader()، الذي يعيد الكائن -من النوع ClassLoader- الذي حمَّل الصنف المَعنِي. وبالتالي، يُمكِننا كتابة ما يلي بأي تابع نسخة لأي كائن لكي نحصل على مُحمِّل الأصناف الذي نريده. ClassLoader classLoader = getClass().getClassLoader(); يُمكِننا بدلًا من ذلك استخدام ClassName.class، إذ يشير ClassName إلى اسم الصنف الذي نريده، لكي نحصل على مرجع reference إلى الكائن المُمثِّل لذلك الصنف. على سبيل المثال، كان بإمكاننا استخدام Menus.class.getClassLoader() بتطبيق عرض مجموعة ماندلبرو لكي نسترجع مُحمِّل الأصناف. بمجرد حصولنا على مُحمِّل الأصناف، يُمكِننا اِستخدَامه للعثور على أي ملف مورد؛ إذ يعيد مُحمِّل الأصناف مُحدِّد الموارد الموحَّد URL الخاص بالملف، وهو ما يُمكِّننا من قراءة البيانات الموجودة بالملف. وكما هو الحال مع ملفات الصور، يحتاج مُحمِّل الأصناف إلى مسار الملف لكي يتمكَّن من إيجاده؛ إذ يتضمَّن المسار اسم الملف، بالإضافة إلى أي مجلدات ينبغي التنقُل عبرها للوصول إلى الملف. تقع ملفات الموارد المُتضمِّنة للأمثلة بالنسبة لتطبيق عرض مجموعة ماندلبرو داخل سلسلة المجلدات edu/hws/eck/mdbfx/examples، وبالتالي يكون مسار أحد تلك الملفات "settings1.mdb" هو edu/hws/eck/mdbfx/examples/settings1.mdb. يُعيد الأمر التالي مُحدِّد الموارد المُوحد الخاص بذلك الملف: URL resourceURL = classLoader.getResource("edu/hws/eck/mdbfx/examples/settings1.mdb"); والآن، بعد أن حصلنا على مُحدِّد الموارد الموحد الخاص بالملف، يُمكِننا ببساطة استخدام كائن مجرى من النوع InputStream لفتح الملف وقراءة بياناته على النحو التالي: InputStream stream = resourceURL.openStream(); وهذا بالضبط هو ما يفعله تطبيق عرض مجموعة ماندلبرو لكي يُنفِّذ قائمة "Examples". إذًا، باستخدامنا لمجرى دخل، يُمكِننا قراءة أي نوعٍ من البيانات الموجودة بملف مورد، وأن نفعل بها أي شيء نريده، ولكن تذكّر أن جافا تُوفِّر أساليبًا أفضل لتحميل بعض أنواع البيانات، مثل الصور إلى البرنامج. سنناقش الآن موضوعًا آخر عن استخدام المُسرِّعات accelerators لعناصر القائمة؛ إذ أن المُسرِّع ببساطة هو مفتاح، أو عدة مفاتيح بلوحة المفاتيح يُمكِن اِستخدَامها لاستدعاء عنصر قائمة معين بدلًا من اختيار عنصر القائمة عن طريق الفأرة. يشيع استخدام المُسرِّع "Control-S" مثلًا لحفظ ملف؛ كما يُستخدَم الصنف KeyCombination المُعرَّف بحزمة javafx.scene.input لتمثيل اتحاد مجموعةٍ من المفاتيح المُمكِن اِستخدَامها مثل مُسرِّع؛ ويُمكِننا إنشاء كائنٍ من هذا الصنف من سلسلةٍ نصية، مثل "ctrl+S". ينبغي أن تحتوي السلسلة النصية على مجموعة عناصرٍ تفصل بينها إشارة الجمع، بحيث يُمثِّل كل عنصر منها -باستثناء الأخير- مفتاح مُعدِّل، مثل "ctrl"، أو "alt"، أو "meta"، أو "shift"، أو "shortcut"؛ ويُمكِن كتابة تلك المفاتيح باستخدام حروفٍ كبيرة أو صغيرة. يُمثِّل المفتاح "shortcut" استثناءً، إذ يُكافئ المفتاح "meta" بنظام التشغيل Mac؛ بينما يُكافئ المفتاح "ctrl" بأنظمة Linux و Windows، وبالتالي نحصل على المُعدِّل المناسب لأوامر القائمة بحسب النظام المُشغَّل عليه البرنامج. في المقابل، لا بُدّ أن يكون العنصر الأخير بالسلسلة النصية المُمثِّلة لاتحاد مجموعة المفاتيح من نوع التعداد KeyCode؛ إذ يُمثِّل ذلك النوع في العموم حرفًا أبجديًا مكتوبًا بالحالة الكبيرة، ويُمثِّل مفتاح ذلك الحرف، ولكنه قد يكون أيضًا مفتاح دالة مثل "F9" (لا يَعمَل جميعها بالمناسبة). على سبيل المثال، تُمثِّل السلسلة النصية "ctrl+shift+N" الضغط باستمرار على مفتاحي "control" و "shift" مع الضغط على مفتاح "N"؛ بينما تُمثِّل السلسلة النصية "shortcut+S" الضغط باستمرار على المُعدِّل المناسب للحاسوب الذي يَعمَل عليه البرنامج مع الضغط على مفتاح "S". نستطيع تمرير تلك السلاسل النصية إلى التابع الساكن KeyCombination.valueOf() لنُنشِئ منها كائنًا من النوع KeyCombination، وذلك لنَستخدِمه لتهيئة مُسرِّعٍ لأي عنصرٍ من عناصر القائمة، إذ تضيف الشيفرة التالية مثلًا مُسرِّعًا لعنصر القائمة "Save Image" الموجود بتطبيق عرض مجموعة ماندلبرو: saveImage.setAccelerator( KeyCombination.valueOf("shortcut+shift+S") ); يُمكِننا استخدام المُسرِّعات مع أي نوع من أنواع عناصر القائمة، بما في ذلك RadioMenuItem و CheckMenuItem. وفي جميع الحالات، عندما ينقر المُستخدِم على مجموعة المفاتيح المُمثِّلة لمُسرِّع معين، ينبغي أن يكون لذلك نفس تأثير النقر بالفأرة على عنصر القائمة المقابل. يكون المُسرِّع الخاص بعنصر قائمة معين مكتوبًا عادةً إلى جانب نص عنصر القائمة؛ وبالنسبة لتطبيق عرض مجموعة ماندلبرو، تملُك جميع الأوامر الموجودة بقائمتي "File" و "Control" مُسرِّعات خاصة بها. التدويل Internationalization سنناقش خلال ما هو متبقي من هذا المقال موضوعين يُمكِن تطبيقهما على جميع البرامج وليس على برامج واجهات المُستخدِم الرسومية فقط؛ إذ لا نُطبِّقهما عادةً بالبرامج الصغيرة، ولكنهما مهمان جدًا للتطبيقات الضخمة. يُقصَد بالتدويل كتابة البرنامج كتابةً يَسهُل معها تهيئته ليَعمَل بمختلف أنحاء العالم. تُستخدَم كلمة "I18n" عادةً للإشارة إلى التدويل، إذ يمثّل "18" عدد الأحرف بين الحرف الأول "I" والحرف الأخير "n" من كلمة "Internationalization". في المقابل، يُطلَق على مهمة تهيئة البرنامج ليَعمَل بمنطقة معينة اسم التوطين localization؛ في حين يُطلَق اسم المحليات locales على تلك المناطق. تختلف المحليات عن بعضها بجوانب كثيرة، مثل نوع العملة المُستخدَمة، والصيغة المُستخدَمة لكتابة الأعداد والتواريخ، ولكن الاختلاف الأبرز والأكثر وضوحًا هو اللغة. سنناقش هنا طريقة كتابة البرامج لنتمكَّن من ترجمتها إلى لغاتٍ أخرى بسهولة. تتمحور الفكرة الأساسية في عدم كتابة السلاسل النصية التي تَظهَر للمُستخدِم ضمن الشيفرة المصدرية للبرنامج؛ لأننا لو فعلنا ذلك، فسنحتاج إلى مترجم قادر على البحث داخل الشيفرة بأكملها، ويَستبدِل كل سلسلةٍ نصية بترجمتها، وسنضطّر بعدها إلى إعادة تصريف البرنامج. في المقابل، إذا أردنا كتابة برنامج يدعم خاصية التدويل، فلا بُدّ أن نُخزِّن كل السلاسل النصية معًا ضمن ملفٍ واحد أو أكثر على نحوٍ منفصلٍ تمامًا عن ملفات الشيفرة المكتوبة بلغة جافا، وبالتالي نستطيع أن نعثر عليهم بسهولة ونترجمهم. ونظرًا لأننا لم نُعدِّل ملفات الشيفرة، فإننا لا نحتاج حتى إلى إعادة تصريف البرنامج. والآن لكي نُنفِّذ تلك الفكرة، علينا أن نُخزِّن السلاسل النصية داخل ملف خاصيات properties file واحدٍ أو أكثر؛ وهو ملفٌ بسيط يحتوي على قائمة أزواج مفتاح / قيمة، ولأن الغرض منها هنا هو الترجمة، فستُشير القيم إلى السلاسل النصية المعروضة للمُستخدِم، وهي ببساطة السلاسل النصية التي ينبغي أن نترجمها؛ أما المفاتيح فهي أيضًا سلاسلٌ نصية، ولكن لا حاجة لترجمتها، لأنها لن تَظهَر أبدًا للمُستخدِم. نظرًا لأننا لن نُعدِّل المفاتيح، يُمكِننا استخدامها ضمن الشيفرة المصدرية. وينبغي عمومًا أن يُقابِل كلُّ سلسلةٍ نصيةٍ مفتاحًا فريدًا يُعرِّف هويتها، وبناءً على ذلك، يستطيع البرنامج استخدام المفاتيح للعثور على ما يقابلها من سلاسل نصية من ملف الخاصيات؛ أي يحتاج البرنامج إلى معرفة المفاتيح فقط، بينما يرى المُستخدِم القيم المقابلة لتلك المفاتيح. وعند ترجمة ملف الخاصيات، سيتمكَّن المُستخدِم من رؤية قيمٍ مختلفة لنفس المفاتيح. تُكتَب أزواج مفتاح / قيمة بملفات الخاصيات على النحو التالي: key.string=value string لا ينبغي أن تحتوي السلسلة النصية المُمثِّلة للمفتاح قبل إشارة التساوي على أيّ فراغات، إذ تُستخدَم عادةً النقاط للفصل بين الكلمات المُكوِّنة لها. في المقابل، بإمكان السلسلة النصية المُمثِّلة للقيمة أن تحتوي على مسافات أو أيّ محارف أخرى. إذا انتهى السطر بمحرف "\"، ستستمر القيمة إلى السطر التالي، وتُهمَل الفراغات الموجودة ببداية ذلك السطر في تلك الحالة. لسوء الحظ، يمكن أن يحتوي ملف الخاصيات على محارف من مجموعة محارف ASCII فقط؛ إذ تدعم تلك المجموعة الأحرف الأبجدية الإنجليزية فقط. ومع ذلك، بإمكان القيمة أن تتضمَّن محارف UNICODE عشوائية، مما يَعنِي ضرورة تشفير المحارف من خارج مجموعة محارف ASCII. يتضمَّن JDK البرنامج native2ascii، الذي يَستطيع تحويل الملفات التي تَستخدِم محارف من خارج مجموعة محارف ASCII إلى ملف خاصيات بصيغةٍ مناسبة. لنفترض أننا نريد عرض سلسلة نصية للمُستخدِم، مثل اسم أمر ضمن قائمة ببرنامج معين. عندها، سيحتوي إذًا ملف الخاصيات على زوج مفتاح/قيمة على النحو التالي: menu.saveimage=Save PNG Image... إذ أن "Save PNG Image…" هي السلسلة النصية التي ينبغي أن تَظهَر بالقائمة. والآن، يستطيع البرنامج استخدام المفتاح "menu.saveimage" للعثور على قيمة المفتاح، ومن ثُمَّ يَستخدِمها مثل نص عنصر القائمة. تُجرَى عملية العثور تلك بواسطة الصنف ResourceBundle، إذ يمكنه استرجاع ملفات الخاصيات واستخدامها. قد تحتوي السلسلة النصية المعروضة للمُستخدِم -في بعض الأحيان- على سلاسل نصية فرعية لا يُمكِن تحديدها قبل تشغيل البرنامج، مثل اسم ملف معين؛ فقد يرغب البرنامج بإبلاغ المُستخدِم مثلًابالرسالة التالية "Sorry, the file, filename, cannot be loaded"، علمًا أن filename هو اسم ملفٍ اختاره المُستخدِم أثناء تشغيل البرنامج. لمعالجة تلك الحالة، بإمكان القيم -بملفات الخاصيات- أن تتضمَّن عنصرًا زائفًا placeholder؛ إذ يُستبدَل ذلك العنصر بسلسلةٍ نصيةٍ يُحدِّدها البرنامج بعد تشغيله، ويُكْتَب ذلك العنصر الزائف على النحو التالي "{0}"، أو "{1}"، أو "{2}". بالنسبة لمثال خطأ الملف، قد يحتوي ملف الخاصيات على القيمة التالية: error.cantLoad=Sorry, the file, {0}, cannot be loaded يسترجِع البرنامج القيمة المقابلة للمفتاح error.cantLoad، ثم يستبدل اسم الملف الفعلي بالعنصر الزائف "{0}". قد يختلف ترتيب الكلمات عند ترجمة السلسلة النصية، ولكن نظرًا لأننا نَستخدِم عنصرًا زائفًا لتمثيل اسم الملف، سيتمكَّن المترجم من وضع اسم الملف بالمكان النحوي الصحيح بالنسبة للغة المُستخدَمة. في الواقع، لا يُعالِج الصنف ResourceBundle عملية الاستبدال تلك، وإنما يتولَّى الصنف MessageFormat تلك المهمة. يَستخدِم تطبيق عرض مجموعة ماندلبرو ملف خاصيات، اسمه strings.properties؛ إذ لا بُدّ أن ينتهي اسم أي ملف خاصيات بكلمة ".properties". يقرأ البرنامج أي سلسلة نصية تراها عند تشغيل التطبيق من ذلك الملف. بَرمَج الكاتب الصنف I18n.java لقراءة قيم المفاتيح، ويحتوي ذلك الصنف على التابع الساكن static التالي: public static tr( String key, Object... args ) يُعالِج التابع السابق العملية بالكامل؛ إذ يَستقبِل التابع المعامل key الذي يُمثِّل المفتاح الذي ينبغي أن يبحث عنه التابع ضمن ملف الخاصيات strings.properties؛ بينما تُمثِل المعاملات الإضافية القيم التي ينبغي أن تحلّ محل العناصر المزيفة -إن وجدت- بالقيمة المقابلة للمفتاح. تذكّر أن التصريح عن المعامل باستخدام "Object…"، وهذا يَعنِي احتمالية تمرير أيّ عددٍ من المعاملات الفعلية بعد المعامل key. ألقِ نظرةً على مقال تعرف على المصفوفات (Arrays) في جافا. تشمل الاستخدامات النموذجية ما يلي: String saveImageCommandText = I18n.tr( "menu.saveimage" ); String errMess = I18n.tr( "error.cantLoad" , selectedFile.getName() ); في الواقع، سترى استدعاءاتٍ كثيرةً مشابهة ضمن شيفرة تطبيق عرض مجموعة ماندلبرو؛ إذ كُتِبَ الصنف I18n بطريقة عامة لتتمكَّن من استخدامه بأي برنامجٍ آخر، وكل ما تحتاج إليه هو توفير ملف خاصيات على أنه ملف مورد، وأن تُخصِّص اسمه بالملف I18n.java، وأخيرًا أن تضع الصنف ضمن الحزمة الخاصة بك. يُمكِننا أيضًا استخدام أكثر من ملف خاصيات ضمن نفس البرنامج. فعلى سبيل المثال، قد نُضمِّن نسخةً فرنسية وأخرى يابانية من ملف الخاصيات إلى جانب النسخة الإنجليزية، فإذا كان اسم الملف بالنسخة الإنجليزية هو strings.properties، فينبغي أن تكون أسماء الملفات بالنسختين الفرنسية واليابانية strings_fr.properties و strings_ja.properties؛ إذ تملُك كل لغة ترميزًا مكوَّنًا من حرفين، مثل "fr" و "ja"، ويُستخدَم هذا الترميز باسم ملف الخاصيات الخاص بتلك اللغة. بدايةً، يَستخدِم البرنامج الاسم البسيط لملف الخاصيات "strings"؛ فإذا كان البرنامج مُشغَّلًا بنظام جافا وكانت اللغة المُفضلة هي الفرنسية، فسيحاول البرنامج أن يُحمِّل ملف خاصيات باسم "strings_fr.properties"؛ وإذا فشل، فسيحاول أن يُحمِّل ملف خاصيات باسم "strings.properties". يَعنِي ذلك، أن البرنامج سيستَخدِم ملف الخاصيات الخاص باللغة الفرنسية في الموضع الفرنسي، وسيستخدِم ملف الخاصيات الخاص باللغة اليابانية في موضع اللغة اليابانية، وأخيرًا، سيَستخدِم ملف الخاصيات الافتراضي في الحالات الأخرى. الإعدادات المفضلة تَسمَح غالبية البرامج للمُستخدِم بضبط إعداداته المفضلة؛ إذ تُمثِّل تلك الإعدادات جزءًا من حالة البرنامج، التي ينبغي أن يتذكرها عند تشغيله مرةً أخرى، وليتمكَّن من ذلك، عليه أن يُخزِّنها بملفٍ ضمن المجلد الرئيسي للمُستخدِم، كما أن عليه أن يتمكَّن من تحديد موقعها بعد ذلك. ينبغي إذًا تسمية الملف بطريقة تُجنِّبنا أيّ تعارضٍ مع أسماء الملفات المُستخدَمة بواسطة البرامج الأخرى. هناك مشكلةٌ أخرى، وهي أننا بتلك الطريقة سنملأ المجلد الرئيسي للمُستخدِم بملفاتٍ لا ينبغي أن يُعرَف بوجودها أساسًا. تتعامل جافا مع تلك المشاكل بتوفير طريقةٍ قياسية لمعالجة الإعدادات المفضلة؛ إذ تُعرِّف جافا الصنف Preferences ضمن حزمة java.util.prefs، وهذا الصنف هو كلُّ ما تحتاج إليه. يحتوي ملف تطبيق عرض مجموعة ماندلبرو Main.java على مثالٍ لاستخدام الصنف Preferences. يضبُط المُستخدِم عادةً إعداداته المفضلة بمعظم البرامج عبر صندوق نافذة مُخصَّص، ولكن لا يحتوي تطبيق ماندلبرو على إعدادات يتناسب معها هذا النوع من المعالجة. وبدلًا من ذلك، يُخزِّن البرنامج بعضًا من جوانب حالة البرنامج أوتوماتيكيًا على أنها إعداداتٌ مفضلة؛ وبكل مرة يُشغِّل المُستخدِم بها البرنامج، فإنه يقرأ تلك الإعدادات إن وُجدت؛ وبكل مرة يُغلِق المُستخدِم بها البرنامج، فإنه يُخزِّن تلك الإعدادات. تؤدي الحاجة إلى حفظ الإعدادات المفضلة إلى مشكلةٍ شيقةٍ نوعًا ما. ينتهي البرنامج بمجرد غلق النافذة، ولكننا نحتاج إلى طريقةٍ لحفظ الإعدادات عند حدوث ذلك، ويَكْمُن الحل عادةً في استخدام الأحداث: يُسجِّل التابع start() المُعرَّف بالصنف Main مستمعًا إلى حدث غلق النافذة، وعند وقوع ذلك الحدث، يُعالِجه معالج الحدث بحفظ الإعدادات المفضلة. تُخزَّن الإعدادات المفضلة ببرامج جافا بصيغةٍ تعتمد على المنصة المُستخدَمة، وبمكانٍ يعتمد أيضًا على تلك المنصة، ولكن بصفتك مبرمج جافا، فلا حاجة للقلق بشأن ذلك، إذ يعرف نظام التفضيلات بجافا تمامًا مكان تخزين البيانات، ولكن ما يزال هناك مشكلة فصل الإعدادات المفضلة لبرنامج معين عن إعدادات بقية برامج جافا التي قد تكون مُشغَّلة على نفس الحاسوب. تحلّ جافا تلك المشكلة بنفس الطريقة التي حلّت بها مشكلة تسمية الحزم؛ إذ تُعرَّف ببساطة الإعدادات المفضلة لبرنامج معين من خلال اسم الحزمة الخاصة بالبرنامج مع اختلافٍ بسيط بالترميز. على سبيل المثال، تطبيق عرض مجموعة ماندلبرو مُعرَّف بحزمة edu.hws.eck.mdbfx، وإعداداته مُعرَّفة عبر السلسلة النصية "/edu/hws/eck/mdbfx". حلَّ المحرف "/" محلَّ النقاط مع إضافة "/" إلى البداية. تُخزَّن الإعدادات المفضلة لأي برنامج داخل عقدة node، ويُمكِن استرجاع العقدة المقابلة للسلسلة النصية المُعرِّفة لإعدادات برنامج معين على النحو التالي: Preferences root = Preferences.userRoot(); Preferences node = root.node(pathName); يشير المعامل pathname إلى السلسلة النصية المُعرِّفة للعقدة، مثل "/edu/hws/eck/mdbfx". تتكوَّن العقدة نفسها من قائمةٍ بسيطة من أزواج مفتاح / قيمة، ويتكوَّن كلٌ من المفتاح والقيمة من سلاسل نصية. في الحقيقة، يستطيع البرنامج أن يُخزِّن أي سلاسل نصية ضمن تلك العقد، فهي تُمثِّل فقط طريقةً للاحتفاظ بالبيانات من عملية تشغيل برنامج لأخرى، ولكن يُحدِّد المفتاح عمومًأ هوية عنصرٍ معيّنٍ من الإعدادات المفضلة، وتكون قيمته المقابلة هي القيمة المفضلة. إذا كان prefnode كائنًا من النوع Preferences، فإنه يحتوي على التابع prefnode.get(key) لاسترجاع القيمة المرتبطة بمفتاح معين، والتابع prefnode.put(key,value) لضبط القيمة الخاصة بمفتاح معين. يَستخدِم البرنامج "Main.java" الإعدادات المفضلة لتخزين شكل نافذة البرنامج وموضعها، إذ يَسمَح ذلك بالإبقاء على حجم النافذة وشكلها بين عمليات التشغيل المتتالية لنفس البرنامج. وعندما يُشغِّل المُستخدِم البرنامج، سيجد النافذة بنفس المكان الذي تركها فيه آخر مرة شغَّل خلالها البرنامج. يُخزِّن البرنامج أيضًا اسم المجلد الذي كان يحتوي على آخر ملف فتحه المُستخدِم أو حفظه، وهذا في الواقع أمر مهم؛ لأن السلوك الافتراضي لصندوق نافذة فتح ملف هو عرض مجلد العمل الحالي، وهو نادرًا ما سيكون المكان الذي يرغب المُستخدِم بحفظ ملفات البرنامج به. بفضل خاصية الإعدادات المفضلة، سيتمكَّن المُستخدِم من الانتقال إلى المجلد الصحيح عند أول تعاملٍ له مع البرنامج، وبعد ذلك سيجد المُستخدِم نفسه بالمجلد الصحيح أوتوماتيكيًا عند استخدامه للبرنامج مرةً أخرى. تستطيع الإطلاع على الشيفرة المصدرية بالملف Main.java لمزيدٍ من التفاصيل. يُمكِننا بالتأكيد قول المزيد عن لغة جافا وعن البرمجة عمومًأ، ولكن هذه السلسلة لا تمثِّل سوى مقدمةً إلى البرمجة باستخدام لغة جافا، وقد حان الوقت لانتهاء رحلتنا. التي نتمنى أنها كانت رحلةً مُبهِجةً لك وأنك قد استفدت منها ببناء أساسٍ قوي تستطيع استخدامه قاعدةً لاكتشافاتك التالية. ترجمة -بتصرّف- للقسم Section 5: Finishing Touches من فصل Chapter 13: GUI Programming Continued من كتاب Introduction to Programming Using Java. اقرأ أيضًا المقال السابق: النوافذ وصناديق النافذة في جافا الخاصيات والارتباطات في جافا كيفية كتابة برامج صحيحة باستخدام لغة جافا مقدمة إلى صحة البرامج ومتانتها في جافا1 نقطة
-
إضافة ز ّرين لتحديث وحذف المقالة، التحديث والحذف من صالحيات مدراء الموقع الذين يملكون صلاحيات كافية. يتم التأكد من صالحيات المستخدم الذي يحاول تحديث او حذف مقالة عن طريق ادخال كود التصريح1 نقطة
-
هل بامكاني بناء تطبيق او موقع متكامل باك اند و فرونت اند فقط بإستعمال لغة بايثون ؟؟؟1 نقطة
-
ما هو نظام ادارة قواعد البيانات التي تستعملها؟ الأمر عادي، ويماثل اي طريقة أخرى لحذف وتحديث سجل من قواعد البيانات. فهو عموما سيتبع المنطق التالي: انشاء ملف عرض يصف الهيكلة المطلوبة، ويحتوي نموذجين كل منهما يوجه الى مسارين A و B. يمثل المساران A و B نقطتي وصول تشيران الى تابعي متحكم خاص بالمنشور وليكن delete و update. عند استهداف التابع الاول يتم تنفيذ شيفرة الحذف الخاصة بالمنشور. وعند استهداف التابع الثاني يتم تنفيذ شيفرة التحديث الخاصة بالمنشور. الآن لن تحتاج الا حماية هاذين المسارين وليكن عن طريق الطبقات الوسيطة Middlewares. اين ستحتاج التحقق من هوية المرسل للطلب ومن صلاحياته. بالطبع فإن هذا هو المنطق العام للعملية. لن تحتاج الا نمذجتها عن طريق الشيفرة الخاصة بك، وذلك يختلف بحسب اطار عمل الواجهة الامامية او محرك القالب المستعمل، نظام قاعدة البيانات وما الى ذلك.1 نقطة
-
1 نقطة
-
انا عملت موقع fullstack واريد رفعه علي استضافة heroku الbackend: node js1 نقطة
-
مرحبا ماهي التقنية او الفريم وورك المستخدمة في برمجة منيو الكتروني لمطعم مثلا.. مربوط بالواتس اب لاستلام الطلبات1 نقطة
-
عند قولك منيو الكتروني فهو عبارة عن موقع ويمكنك ربط هذا الموقع بالواتس اب لاستقبال الطلبات من خلال استخدام API الخاص بالواتس اب. بالنسبة للتقنيات المستخدمة وإطارات العمل هما كالاتي. أولا - نبدأ بتقنيات الواجهة الأمامية وهي: HTML CSS JavaScript Bootstrap React ثانياً - تقنيات الواجهة الخلفية وهي: Express JS ثالثاً - بالنسبة لقاعدة البيانات المستخدمة يفضل أن تستخدم mongoDB وهذه التقنيات ليست الوحيدة التي يمكن استخدامها ولكنها من أفضل التقنيات في مجال بناء المواقع بالنسبة للتقنيات الأخري مثلا يمكنك إستخدام لغة البرمجة PHP وإطار العمل Laravel فهما من أشهر التقنيات المستخدمة في هذا المجال أيضاً.1 نقطة
-
السلام عليكم ورحمة الله وبركاته... عندي استفسار عن هل يمكن تحميل الدورة لمشاهدتها بن دون انترنت؟ وإذا يوجد ماهي الطريقة وشكرا.1 نقطة
-
للاسف لا يتوفر هذا الأمر ولا يمكنك تحميل دروس الدورات لعدة أسباب أهمها ضمان عدم نشر مقاطع الدورات في مواقع مختلفة غير الاكاديمية وبالتالي يصبح لا فائدة من بقاء الاكاديمية لان الدروس تصبح في أيدي الجميع بالمجان ويمكنهم الوصول اليها في أي وقت, يجب ان يتوفر الانترنت لتستطيع الوصول الى الدروس ولكي تستطيع مشاهدتها1 نقطة
-
قسم الفرونت اند يحتاج الى تقنيات يمكن من خلالها بنء الواجهات الامامية للموقع مثل html و css و جافاسكربت فهي ما تكون الواجهات الأمامية ولا يمكننا فعل ذلك باستخدام بايثون, ولكن الموقع يحتاج الى لغة ليتم بناء الواجهات الخلفية الخاصة به مثل بايثون باطار العمل جانغو او فلاسك, اذن ما يمكننا فعله باستخدام بايثون هو فقط بناء الواجهات الخلفية من الموقع اي قسم باك اند فقط, أما الواجهات الأمامية تحتاج الى التقنيات التي ذكرتها سلفا1 نقطة
-
هل يمكنك الاشارة الى اطار العمل او اللغة التي تستعملها؟ ربما تبحث عن أحد مكتبات faker. مثل fakerphp في PHP أو fakerjs في جافاسكربت أو Faker في بايثون. وموجودة حتى في Perl و Ruby. على اختلاف هاته اللغات، كل هاته المكتبات توفر تابعا email يهتم بتوليد عناوين بريد الكتروني مزيفة او اختبارية. يمكنك بذرها الى قواعد البيانات واستعمالها لذات الغرض. قد تحتاج ايضا خادم SMTP اختباري من مثل Mailtrap، لاستقبال هاته الرسائل.1 نقطة
-
أهلًا محمد، ماشاء الله عليك عمل أكثر من جيد يامحمد، معظم من في سنك لايجيدون استخدام الحاسوب حتى، العمل الذي أنجزته خلال الدورة ومرورك الاختبار وجميع الأعمال الموجودة لديك في الموقع الشخصي تنم عن مدى خبرتك في هذا المجال. أعتقد أنه سيكون لك مستقبل جيد في مجال البرمجة وستكون مبرمج متميز. مستواك إلى الآن ممتاز، اطلعت على جميع المشاريع الموجودة في موقعك الشخصي، جميعها جيدة ولايوجد أخطأ فيها. أرجو أن تبقى مستمر بنفس الحماس الموجود لديك الآن. أتمنى لك كل التوفيق،1 نقطة
-
تعلّمنا في المقال السابق ما هو فن البِكسل Pixel Art وماهي أنواعه وأشهر استخداماته. في هذا الدرس سنتعلّم أساسيات الرسم بهذا الفن من نوعية غير-إيزمترك Non-Isomertic وهو النوع المسطّح البسيط بدون أبعاد متساوية أو شبه ثلاثي أبعاد بل رسومات عادية بسيطة تُظهر الموضوع المرسوم من جهة واحدة فقط. هناك عدة برامج يمكنها القيام برسم هذه النوعية من الرسومات، بعضها بسيط وسهل للغاية وبعضها احترافي ومخصّص لرسم هذه النوعية. أسهل وأبسط هذه البرامج برنامج الرسّام MS Paint من مايكروسوفت والذي يأتي عادة مع نظام التشغيل ويندوز Windows ولاستخدام هذا البرنامج يكفي أن نستخدم أداة القلم مع أصغر قياس لحجم الخط وتكبير المنظور إلى أكبر درجة وهي 800% لتتمكن من رسم رسومات بِكسل. وفي هذا الدرس سنتعلّم كيفية تخصيص الفوتوشوب لرسم رسومات بِكسل. بدايةً سنرسم شيئًا بسيطًا لفهم الفكرة الأساسية. افتح برنامج الفوتوشوب وأنشئ مستندًا جديدًا، عليك اختيار حجم العمل الذي سنرسمه، وبما أننا سنرسم بفن البكسل الصغير نسبيًّا فإن حجم العمل سيكون صغيرًا أيضًا، وفي هذه المرحلة سنرسم رسمًا صغيرًا جدًّا، لذلك اختر حجم 24x24 بكسل ولتكن الخلفية بيضاء. طبعًا يمكننا رسم رسومات بِكسل كبيرة وحتى ضخمة بالقياسات الكبيرة وستكون مذهلة ورائعة تلك الرسومات، ولكن رسمها سيستغرق وقتًا وهذا ما ستقوم به لاحقًا بعد أن تتعلم كيفية القيام بذلك. كما سنشاهد ستكون مساحة العمل صغيرة للغاية وسيكون الرسم بداخلها مستحيلًا لذلك سيتوجب علينا تكبير منظور العمل من خلال أداة المُكبّر أو بالضغط على أيقونة حجم المنظور أسفل يسار البرنامج والذي سيكون حاليًّا 100% لنجعله 800% على الأقل. وأنا في هذه الحالة هنا جعلته 1600%. الآن اختر أداة القلم والتي ستظهر بعد الضغط بالفأرة باستمرار على أداة الفرشاة ثم اختيار أداة القلم Pen Tool. لا يمكننا استخدام أداة الفرشاة لأنها تتميز بحواف ناعمة بينما تتميز رسومات البِكسل بحواف خطوطها ونقاطها القاسية والحادة تمامًا. سيصبح شكل المؤشر بشكل مربع وهو بحجم 1 بكسل حيث يجب أن نتأكد من أن حجم أداة القلم هو 1 بكسل من شريط أدوات أداة القلم. وللتأكد من حدّة وقساوة الحواف اجعل قيمة Hardness عند 100%. في البداية قد تجد صعوبة في فهم أين سيتم وضع كل نقطة ستقوم برسمها لذلك من الأفضل أن تقوم بإظهار الشبكة Grid حتى تكون الأمور واضحة أكثر وستقوم لاحقًا بالاستغناء عن هذه الخطوة حيث سيكون الأمر أكثر سهولة وستفهم كيفية العمل بدونها، لذلك فعّل الشبكة عبر القائمة View > Show > Grid أو بالاختصار Ctrl + ‘ وستظهر الشبكة على حقل العمل. قد تكون الشبكة غير منتظمة وتحوي مربعات فاتحة الحدود داخل مربعات داكنة الحدود، في هذه الحالة عليك بالذهاب إلى القائمة Edit > Preferences > Guides, Grid and Slices ثم ضع القيمة 1 في خانة Gridline Every وفي خانة Subdivisions وتأكد من أن وحدة قياس Gridline Every هي Pixels. في بعض الأحيان قد يقوم الفوتوشوب بتنعيم الحواف قليلًا لجعل الصورة تبدو بدقّة عالية ولتجنّب ذلك عليك بالذهاب إلى قائمة Edit > Preferences > General أو بالضغط على الاختصار Ctrl + K ثم ضع خيار Image Interpolation عند البند Nearest Neighbor (Preserve Hard Edges) وبذلك نحافظ على قساوة الحواف في كافة الظروف. الآن سنبدأ عملية الرسم. ابدأ برسم الخطوط الخارجية لشكل فاكهة ولتكن كمثرى على سبيل المثال. اتبع خطوات الرسم في الأسفل لتحصل على شكل الفاكهة المطلوب. حصلنا على شكل الفاكهة الخارجي والآن سنقوم بتلوين هذا الشكل باستخدام ذات الأداة أداة القلم مع تغيير اللون واختيار اللون المناسب. ورسم جميع المناطق الداخلية للفاكهة. شكل الفاكهة حتى هذه اللحظة سيبدو بهذا الشكل بدون خطوط شبكة (يمكنك إظهار خطوط الشبكة وإخفاءها بالاختصار Ctrl + ‘). وبالحجم الطبيعي ستبدو بهذا الشكل. تظهر بهذا الحجم وكأنها أحد الرموز التعبيرية المستخدمة في برامج المحادثة عبر أجهزة الهواتف المحمولة وهي تقريبًا بحجم مشابه لها. من الممكن اعتبار هذا الرسم جاهزًا في حال أردت رسم تصميم بِكسل مسطّح أو بسيط ولكن نستطيع جعل هذا الرسم أكثر جمالًا وروعة عبر إضافة آثار الظلال والإضاءة ما يضفي مزيدًا من الحيوية على التصميم وللقيام بذلك سنقوم باستخدام لون داكن أكثر قليلًا من لون الفاكهة الأساسي ثم سنرسم ظلالًا من جهة واحدة كما يلي. والآن سنقوم بوضع آثار انعكاس الإضاءة على الجهة المقابلة من الفاكهة عبر استخدام ألوان فاتحة أكثر من اللون الأساسي وقد نستخدم اللون الأبيض للتعبير عن شدة الإضاءة واللمعان في نقطة معينة. شكل التصميم بدون شبكة سيكون بهذا الشكل. والشكل النهائي بالحجم الطبيعي. طبعًا يمكن تعديل الحجم بما يتناسب مع رغبتك والهدف من التصميم بحيث يمكن استخدام هذا التصميم كرمز تعبيري لبرنامج محادثة أو كعنصر من عناصر لعبة ما تقوم ببرمجتها أو أي استخدام آخر ويمكن أيضًا إلغاء الخلفية بحيث تقوم بالرسم على مستند جديد بدون خلفية أو قص هذا الشكل من الخلفية بسهولة. هذا الدرس لتعلّم أساسيات تصميم رسومات Pixel Art من نوع Non-Isometric باستخدام برنامج الفوتوشوب وسيكون هناك دروس قادمة لرسومات أكثر تقعيدًا وأكبر حجمًا كذلك لدروس من نوعية Isometric الرائعة والممتعة. يمكنكم القيام برسومات مشابهة وإدراجها في التعليقات أسفل الدرس لنتشارك هذه الخبرة الجديدة.1 نقطة
-
1 نقطة
-
سنحاول هنا أن نحيط قدر الإمكان بجوانب هذا الفن الرائع ورسوماته المذهلة عبر معرفة ماهيته، أنواعه، تطورّه، تاريخه وأهم البرامج المستخدمة في رسمه. وسنتعلّم لاحقًا كيفية رسم رسومات البكسل المتنوعة. ما هو فن البِكسل؟ فن البِكسل أو Pixel Art هو فن رسومات قديم يشبه من حيث المبدأ الحياكة الصوفية كالرسومات على الملابس الصوفية إلا أن بداية هذا النوع من الفنون بدأ مع بدء ظهور أجهزة اللعب والحواسيب حيث لم يكن بمقدور تلك الأجهزة القديمة عرض رسوميات معقدّة وكثيرة الألوان وثلاثية الأبعاد كما في يومنا هذا لذلك اعتمد مطوّرو الألعاب في ذلك القوت على هذا النوع الوحيد المتوفّر لرسم خلفيات وشخصيات الألعاب بألوانها البسيطة، وعلى الرغم من بساطتها في ذلك الوقت إلا أنها كانت رائعة وممتعة للغاية ولعّل ألعاب Pacman و Mario وStreet Fighters وKingKong وSonic أبرز ألعاب تلك المرحلة شاهد ومثال رائع على روعة تلك الرسومات في تلك الحقبة من الزمن. ويعتمد الرسم هنا على رسم النقاط الصغيرة بجانب بعضها بحيث تكون النقطة الواحدة بحجم بكسل واحد فقط ما يجعل هذه الرسومات دقيقة وصغيرة نسبيًّا بالنسبة إلى المقاسات المعتمدة في الأجهزة المتطوّرة في أيامنا هذه بينما كانت أكثر من كافية لأجهزة تلك الحقبة القديمة. لذلك عند الرسم بأي برنامج للرسم عليك تكبير منظور العمل إلى أكبر درجة ممكنة تستطيع معها التعامل مع نقاط البكسل بسهولة وعند العودة للحجم الطبيعي ستشاهد نتيجة العمل الذي قمت به. هذا مثال رسمته باستخدام برنامج الرسام Paint الموجود ضمن نظام الويندوز Windows حيث تظهر نقاط البكسل بوضوح عند التكبير وفي شكل الرأس الصغير تظهر النتيجة بالحجم الطبيعي. وكانت رسومات البكسل بسيطة جدًّا في البداية حيث اعتمدت نظام ألوان 8bit لتتطور إلى 16bit ومنها إلى أكثر وأكثر حتى أصبحت متطورة جدًّا لدرجة أن الرسومات الحديثة منها أصبحت تتضمن تدرجات لونية عادية بعد زيادة حجم العمل وعدد الألوان المستخدم. على الرغم من انتشار الرسومات عالية الدقة والرسومات ثلاثية الأبعاد إلا أن رسومات البكسل مازالت موجودة بقوة وبخاصة في ألعاب الأجهزة المحمولة كالهواتف الذكية وغيرها. ومن الممكن رسم هذه الرسومات باستخدام تقنيات وأدوات اعتيادية كأدوات برنامج الرسّام Paint والفوتوشوب Photoshop أو يمكن رسمها بدون أدوات وبشكل يدوي وهو ما ينتج إبداعًا مميزًا قد لا تستطيع الأدوات تقديمه. أنواعه: تم تقسيم رسومات البِكسل إلى نوعين: الإيزومِترِك Isometric: ويُطلق عليها اسم ثلاثي البعاد أو متساوي القياس أيضًا وهي رسومات بكسل تبدو بثلاثة أبعاد وتظهر فيها ثلاث جوانب للأشكال المرسومة ما يعطي انطباعًا بأنها ثلاثية الأبعاد وعادة ما تكون بمنظور جانبي بزاوية معينة وتكون غالبًا 30 درجة وتستخدم لرسم مناظير معينة لمواقع شهيرة أو افتراضية وفي بعض الأحيان لرسم خلفيات لعبة من هذا النمط وقد ظهرت بعض الألعاب التي تعتمد على هذا النمط من الرسوميات خصوصًا للأجهزة المحمولة كالهواتف الذكية فيما يتباهى الآن المصممون برسوماتهم المعقّدة والرائعة باستخدام هذا النمط. وهذه صور لبعض الألعاب القديمة التي تستخدم هذا النمط سلسلة ألعاب Age of Empires Diablo Transport Tycoon وهذه صور لبعض ألعاب الهواتف المحمولة تستخدم هذا النمط من الرسومات Pocket Harvest Zombie Commando وهذه بعض الرسومات لهذا النمط لبعض المصممين للمصممة Sylvia Flores Espinoza للمصمم Robert Podgórski للمصمم Sergey Kostik غير الإيزومِترِك Non-Isometric: وهو نمط رسومات بِكسل عادي والأكثر انتشارًا وهو الصورة بشكل مباشرة من جهة واحدة من الأمام أو الجانب أو حتى من الأعلى بدون زوايا وهو معروف في عالم الألعاب القديمة وأيضًا الحديثة الخاصة بمنصات الأجهزة المحمولة عادة. ولعل أشهر الصور والألعاب المعروفة التي تستخدم هذا النمط هو ألعاب ماريو وسونيك وغيرها بالإضافة إلى الألعاب الجديدة على منصات الهواتف المحمولة وهذه بعض الأمثلة Random Heroes 2 Sword Of Xolan وقد اشتهرت مؤخرًا هذه الرسمات كثيرًا في رسم الوجوه التعبيرية (سمايلات) وخصوصًا في رسم الأيقونات التي تعتمد بشكل كبير على هذا النوع من الرسومات وبخاصة أيقونات مواقع الإنترنت. ولتوضيح الفرق بين نوعي فن البيكسل إليك هذا المثال Non-Isometric Isometric وسنتعلّم في الدرس التالي كيفية تصميم رسومات بفن Pixel Art بنوعية Non-Isometric وكذلك في الدرس الذي يليه سنتعلّم كيفية تصميم نوعية Isometric. مصادر الصور: صور المصممين من صفحاتهم على Behance. صورة التلفاز التوضيحية من موقع ويكيبيديا تحت رخصة CC BY-SA 3.0.1 نقطة