لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 01/17/24 في كل الموقع
-
إذا أردت أهم نصيحة فهي ألا تتابع كورسات البرمجة كما تتابع التلفاز، فالأمر مختلف تمامًا، الممارسة العملية واجبة، فبعد بعد مشاهدة الدرس والاستيعاب والفهم ثم الحفظ (الحفظ مهم في البداية فقط فهو جزء من عملية التعلم) يأتي وقت التطبيق، ولا مشكلة في البداية بالتطبيق مع المدرب، لكن يجب إعادة التطبيق الذي تم بالدرس بمفردك من البداية بجميع الخطوات أي من الصفر. واعتني بشدة بدراسة الأساسية ولا تهرع نحو تعلم المكتبات والإطارات، فستعاني بعد ذلك، لكن بالطبع وازن بين هذا وذاك فلو لديك وقت قليل وتريد دخول سوق العمل يمكنك إهمال تلك النقطة بنسبة معينة لكن لا أنصح بذلك مطلقًا. ثم لا تكتفي بما قمت به ابحث عن تطبيقات أخرى للمبتدئين على اليوتيوب مع ذكر اللغة أو الإطار الذي تتعلمه، وبعد الإنتهاء من الدورة لا تكتفي من المشروع الذي قمت به بل قم بالتطبيق على مشروع سواء بمحاولة تطبيق ما تريده بمفردك أو مشاهدة شرح لإنشاء مشروع بفكرة معينة على اليوتيوب أو دورة معينة. أيضًا يجب تخصيص وقت معين والإلتزام به يوميًا لا يقل عن ساعتين، وكل يوم سيتحسن مستواك والأمر صعب في البداية فلا تستعجل. وستجد شرح مفصل ذكرته من قبل هنا فالأمر بحاجة إلى تفصيل:2 نقاط
-
ماهي الطريقة الصحيحة لمتابعة وتطبيق كورسات البرمجة؟1 نقطة
-
السلام عليكم يوجد لدي DataFrame بها مجموعة من الأعمدة أحتاج أن أجمع قيم أحد الأعمدة وتقسيمها على مجموعة عمود أخر أيضا بعد جمعه1 نقطة
-
أشكرك أخي على التوضيح فعلا الأن تم تطبيق البرنامج ولاكن المشكلة في عملية الجمع تظهر خاطئة كما أن هناك هذه الأسطر لم أفهمها See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy names2['وحدة دراسية'] = pd.to_numeric(names2['وحدة دراسية'], errors='coerce') 4115460.0 قمت بتحويل إلى DataFrame وقد عالجة المشكلة الأخيرة وهذه مخرجات لاكن نتيجة الجمع لها خطأ 4115460.0 رقم القيد object رمز المقرر object الفصل الدراسي object الاعمال int64 النهائي float64 المجموعة int64 وحدة دراسية int64 cc float64 dtype: object1 نقطة
-
بالنسبة للخطأ الذي قد واجهته، يفضل دائما التحقق من أن أنواع البيانات في الأعمدة تسمح بالعمليات الحسابية، يمكنك استخدام dtype للتحقق من أن الأعمدة التي تشارك في العمليات الحسابية لديها نفس الأنواع. # التحقق من أنواع الأعمدة print(df3.dtypes) # تحويل الأعمدة إلى أنواع البيانات المناسبة إذا كانت لا تزال هناك مشكلة df3['النهائي'] = pd.to_numeric(df3['النهائي'], errors='coerce') df3['وحدة دراسية'] = pd.to_numeric(df3['وحدة دراسية'], errors='coerce') # القيام بالعمليات الحسابية بعد التأكد من صحة أنواع البيانات df3['cc'] = df3['النهائي'] * df3['وحدة دراسية'] # حساب المجموع الإجمالي Total = df3['cc'].sum() # طباعة النتيجة print(Total)1 نقطة
-
names2 =melted_df[['رقم القيد', 'رمز المقرر' , 'الفصل الدراسي' , 'الاعمال' , 'النهائي' , 'المجموعة' ]] names2['وحدة دراسية']=melted_df['المادة'].map(self.Subjects_SH).fillna('Other') names2['cc']=names2['النهائي'] * names2['وحدة دراسية'] df3 = pd.DataFrame(names2) Total = df3['cc'].sum() print(Total)1 نقطة
-
تأكد من أن العمود لا يحتوي على أي كلمات، اذا تستطيع أرفق الكود مع الملف لأطلع عليه أكثر.1 نقطة
-
1 نقطة
-
هل فيه كورس للشبكات و Malware Analysis / Reverse Engineering (RE)1 نقطة
-
السلام عليكم ورحمة الله وبركاته عندي سؤال لو تفضلتم هل لديكم طريقة دفع اخرى كطريقة الدفع بالفاتورة مثلا؟ جزاكم الله خيرا1 نقطة
-
1 نقطة
-
I have a new game I just finished rock paper scissors but with a different touch please,tell me you opinion, thank you rockPaperScissors.py1 نقطة
-
التطبيق جيد واستخدام الإيموجي أمر مستحسن بالنسبة للمستخدم، لكن هناك مشكلة بالكود عند إختيار أي خيار سواء r, s , p يتم إختياره من قبل الكمبيوتر أيضًا وتصبح النتيجة تعادل. أيضًا من الأفضل إتاحة إدخال حرف y بدلاً من yes للموافقة و n للرفض مع الإبقاء على yes و no أيضًا، كتسهيل للمستخدم. وبدلاً من إخبار المستخدم هل تريد جولة أخرى، أبقي اللعبة تعمل مع رسالة جولة جديدة قد بدأت، مع خيار لإيقاف اللعبة بإدخال حرف مثل q أو كتابة quite أو exit.1 نقطة
-
1 نقطة
-
في البداية، أطمئنك أن هذا شيء طبيعي، فالعالِم كلما ازداد علمًا اكتشف أنه زاد جهلًا، ذلك لأنه عندما يتعلم معلومة يكتشف أنه يجهل مقابلها ألف معلومة، لذلك يقال: "يظل العالِم عالمًا إلى أن يقول قد علمت، فقد جهل" أما من الناحية التقنية، فبالتأكيد التقدم الجنوني الذي نعيشه يرهق أصحاب الخبرة، فما بالك بالمبتدئين. إذّا، كيف يتجنب الإنسان التشتت؟ أولًا: الحصول على العلم من مصادر موثوقة، تقدم خدمة متكاملة مدعومة بالتدريب العملي. ويوجد بأكاديمية حسوب دورات رائعة يمكن الاستفادة منها. ثانيًا: لابد لابد لابد - وكررتها للتأكيد - أن يكتسب الشخص مهارة التعلم الذاتي، لأن التطور جنوني كما نعلم، وبالتالي إن انتظرت من يعطيك المعلومة فستتأخر كثيرًا. ثالثًا: خذ معلومة عن كل تخصص، ولكن تخصص في شيء واحد. رابعًا: وهذه النقطة هامة جدًا للمبتدئين، لا تعمل بمفردك في البداية، ابحث عن فريق أو شركة ليكون لك قائد يدعمك ويرشدك، وكذلك لاكتساب مهارات العمل الجماعي والتواصل. كذلك إذا بدأت في شركة صغيرة أو متوسطة يجب عليك الطموح للانتقال لشركة أعلى، لأن الشركات الصغيرة أو المتوسطة في أغلب الأحيان يغلب عليها طابع الهيمنة الفردية لصاحب المؤسسة أو أقدم الأشخاص بها. خامسًا: اللغة الإنجليزية هامة، ليس مطلوبًا منك أن تكون أجنبيًا، ولكن على الأقل تستطيع قراءة الكتب، فهم الفيديوهات ذات اللغة الواضحة، تستطيع عرض أفكارك باللغة الإنجليزية. أخيرًا، ما سبق كله أخذ بالأسباب، ولا ننس الاستعانة بالله فهو الموفق.1 نقطة
-
هذه هي الشيفرات كاملة # استيراد المكتبات اللازمة import customtkinter import tkinter as tk app = customtkinter.CTk() entry = customtkinter.CTkEntry(app) # ربط عمليتي النسخ واللصق باختصارات لوحة المفاتيح entry.bind("<Control-c>", lambda e: entry.event_generate("<<Copy>>")) entry.bind("<Control-v>", lambda e: entry.event_generate("<<Paste>>")) entry.pack() app.mainloop() ملحوظة: افتراضيًا تدعم أداة الإدخال Entry عملية القص، والنسخ، واللصق تلقائيًا باستخدام اختصارات لوحة المفاتيح دون الحاجة لكتابة هذه الشيفرات، لكن في حالة نظام التشغيل لا يدعمها يمكننا استخدام هذه الشيفرات. أما إذا كنت تريد إظهار قائمة منبثقة (تظهر بالنقر اليمين) وتظهر بها هذه الأوامر، يمكنك استخدام المثال التالي # Import the customtkinter and tkinter modules import customtkinter import tkinter as tk window = tk.Tk() entry = tk.Entry(window) entry.pack() # إنشاء القائمة المنبثقة (بالنقر الأيمن) وإضافة ثلاثة عناصر لها popup_menu = tk.Menu(window, tearoff=0) popup_menu.add_command(label="Cut", command=lambda: entry.event_generate("<<Cut>>")) popup_menu.add_command(label="Copy", command=lambda: entry.event_generate("<<Copy>>")) popup_menu.add_command(label="Paste", command=lambda: entry.event_generate("<<Paste>>")) # هذه الوظيفة لإظهار القائمة المنبثقة مكان النقر باليمين def show_popup_menu(event): popup_menu.post(event.x_root, event.y_root) # ربط القائمة المنبثقة بحدث النقر بالزر الأيمن entry.bind("<Button-3>", show_popup_menu) window.mainloop()1 نقطة
-
مجال علوم الكمبيوتر كبير جدا و بالتأكيد في كل مجال هناك مشاكل، فهمك لطبيعة علوم الكمبيوتر صحيح، حيث تتنوع هذه العلوم بشكل كبير وتشمل تفاصيل دقيقة. ومن خلال توظيفك كمبرمج، يمكنك تحديد نطاق معين أو مجال محدد للعمل عليه، لكن يمكن أن يكون التحدي هو في فهم التفاصيل العميقة في المشاريع وتنفيذها بشكل فعال. أهم شيء أن تحدد أهدافك بوضوح و تحدد المهارات و المجال الذي يثير إهتمامك و الذي تريد أن تستمر فيه، بالنسبة للمشاريع الكبيرة قسمها إلى مهام صغيرة وقابلة للإدارة، هذا يساعدك في التركيز على جزء صغير في كل مرة. لا تحاول فهم كل شيء في وقت واحد. قم بتعلم الأساسيات أولا ومن ثم انتقل إلى المفاهيم المتقدمة تدريجيا، و إذا كان لك زملاء في نفس المجال تعاون معهم للحصول على دعم ومساعدة عند الحاجة. و أهم ما يجب أن تعرف هو أن علوم الكمبيوتر تتطلب صبرا، فلا تحاول تحقيق كل شيء في وقت قصير. استمتع بعملك وابق ملتزما بتطوير نفسك، مع الوقت والتفاني، ستلاحظ تحسنا في مهاراتك وقدرتك على التعامل مع تفاصيل المشاريع بشكل أفضل.1 نقطة
-
تعلم بايثون من الصفر للمبتدئين: حل مشروع إنشاء كلمة سر عشوائية (youtube.com)1 نقطة
-
ما فهمته هو أنك تريد تمارين للتطبيق على الدرس، ها هي: كتابة حلقة "for loop" تقوم بطباعة الأرقام من 1 إلى 10. كتابة برنامج يقوم بطباعة جدول الضرب لرقم محدد، أي جدول الضرب للرقم 5 سيكون كالتالي: 5 x 1 = 5 5 x 2 = 10 ... 5 x 10 = 50 لديك سلسلة من الكلمات، قم بكتابة حلقة "for loop" تقوم بطباعة كل كلمة في السلسلة. كتابة برنامج يحدد ما إذا كان رقم معين هو عدد أولي أم لا. كتابة حلقة "for loop" تقوم بطباعة الأعداد من 1 إلى 20، ولكن تقوم بتخطي الأعداد الزوجية. في حال واجهتك مشكلة في حل أحد التمارين أرجو التعليق أسفل فيديو الدرس المتعلق بالسؤال لمساعدتك بشكل أفضل.1 نقطة
-
نوضح في هذا المقال الأسلوب الأمثل لدراسة تخصص هندسة البرمجيات، ونجيب على السؤال المهم حول إمكانية دراسة البرمجة بشكل ذاتي. ونسلط الضوء على أهم المميزات والتحديات التي ستواجهك إذا اخترت الدراسة الذاتية للبرمجة أو الدراسة الأكاديمية لها، ونختم المقال بجملة من النصائح التي تساعدك لتتعلم البرمجة باحترافية وكفاءة. هل يناسبني تخصص هندسة البرمجيات؟ لا شك أن البرمجة مهارة مميزة في عصرنا الحالي سريع التطور، وقرار تعلم البرمجة أو هندسة البرمجيات هو خيار ممتاز وخطوة صائبة، ويعد تخصص هندسة البرمجيات واحدًا من أسرع المجالات نموًا في العالم ويوفر للمرء فرص عمل متعددة ذات أجر مرتفع ويضمن له مستقبلًا مشرقًا، فكل الأعمال اليوم تحتاج لمن يطور لها البرامج والمواقع ويقوم بصيانتها وتحديثها بصورة مستمرة. إضافة لكونه مجالًا إبداعيًا ودائم التطور يمنح المتخصص فيه قدرة إبداعية ويحسّن طريقة تفكيره المنطقي ويمكّنه من تطوير برامج وتطبيقات بأفكار مميزة ونافعة، فإذا كنت مهتمًا بمجال البرامج الحاسوبية ولغات البرمجة ولديك رغبة دائمة في تطوير نفسك وصبر على تعلم الكثير من المفاهيم والأدوات والاطلاع على كل جديد في مجال التقنيات واستعداد لمواجهة التحديات وحل المشكلات وإتمام المهام وانتباه جيد للتفاصيل فدراسة تخصص هندسة البرمجيات خيار مناسب لك بكل تأكيد ويحقق لك الكثير من الفوائد والمميزات وأهمها: توفير العديد من فرص العمل في مختلف المجالات والقطاعات، فالطلب على المبرمجين ومهندسي البرمجيات في تزايد مستمر. الحصول على أجور مرتفعة مقارنة بالتخصصات والوظائف الأخرى. تعزيز القدرة على التفكير التحليلي والمنطقي وحل المشكلات بسهولة ومرونة. مرونة في اختيار ساعات ومكان العمل حيث يمكن العمل عن بعد من داخل المنزل أو أي مكان وخلال أي وقت تفضله. حرية العمل بشكل مستقل أو بدء عملك الخاص أو العمل في شركة مع فريق متكامل. القدرة على تحويل الأفكار إلى تطبيقات ومشاريع مبتكرة تفيد البشر وتسهّل عملهم. مواكبة التطور التقني وفهم طريقة التعامل مع التقنيات الحديثة بسرعة وسهولة. اكتساب مهارة التطوير الذاتي والقدرة على تعلم مهارات جديدة باستمرار. كيفية تعلم تخصص هندسة البرمجيات إذا قررت التخصص في مجال البرمجة وهندسة البرمجيات فسوف تجد أمامك خيارين أو نهجين متاحين للتعلم الأول هو الطريق الأكاديمية أي سيتوجب عليك الالتحاق بجامعة أو كلية تقنية متخصصة في هندسة البرمجيات أو علوم الحاسوب أو أي اختصاص مشابه، وهذا الخيار يمنحك تعليمًا شاملًا للبرمجة وفق منهج منظم ومحدد لكنه طريق طويل يلزمك بدراسة الكثير الكثير من المقررات ويمتد لفترة زمنية طويلة. الخيار الثاني هو الطريق المهني حيث يتوجب عليك دراسة تخصص هندسة البرمجيات بشكل ذاتي مستفيدًا من مصادر التعلم الكثيرة والمتنوعة من كتب ومقالات ودروس ومقاطع فيديو ودورات تدريبية متاحة عبر الإنترنت لتعلم لغات البرمجة والتقنيات وغيرها من الأدوات والمهارات المطلوبة لإتقان التخصص وهذا الخيار يمكّنك من التركيز على تخصصك واحترافه بوقت أقل. سنناقش في الفقرات التالية أبرز المميزات والعيوب لكل طريقة من هذه الطرق لنساعدك على اختيار الطريقة الأفضل والأنسب لك. الدراسة الجامعية لتخصص هندسة البرمجيات إذا قررت دراسة هندسة البرمجيات من خلال الالتحاق بإحدى الجامعات الأكاديمية حيث تستغرق الدراسة في هذا التخصص وسطيًا أربع سنوات وقد يختلف عدد السنوات والمواد من جامعة لأخرى. وهذا الخيار سيوفر لك عدة مميزات وسيضع أمامك جملة من العوائق والتحديات، لنناقش كلًا منها على حدا في فقراتنا التالية. مميزات الدراسة الجامعية لتخصص هندسة البرمجيات من أهم المميزات والإيجابيات التي ستحصل عليها عند دراسة تخصص هندسة البرمجيات في الجامعة ما يلي: الحصول على تعليم منهجي ومنظم وفق جدول زمني محدد فالمناهج الجامعية موضوعة بعناية ومرتبة وفق ترتيب منطقي يتدرج في تعليمك كافة الأساسيات التي تحتاجها والتي يمكنك من خلال تعلمها تطوير نفسك في أي مجال لاحقًا. الحصول على شهادة جامعية معترف بها تعزز مصداقيتك وتساعدك على الحصول على فرصة عمل. توفر دراسة البرمجة في الجامعة جو تعلّم جماعي وتمكّنك من التفاعل مع زملاء لهم نفس الاهتمامات وتنفيذ مشاريع جماعية ومناقشات مثمرة. توفر الجامعات مختبرات حواسيب ومكتبات ضخمة تضم الكثير من الكتب والمصادر البرمجية القيّمة التي تساعدك في عملية التعلم. صعوبات وتحديات الدراسة الجامعية لتخصص هندسة البرمجيات لا يخلو التعليم الجامعي في تخصص هندسة البرمجيات من بعض العوائق والصعوبات التي تحتاج لأن تأخذها بعين الاعتبار عند اختيار المسار الأفضل بالنسبة لك ومن أبرز هذه الصعوبات نذكر: تحتاج دراسة هندسة البرمجيات في الجامعة لمدة زمنية طويلة تستغرق 4 سنوات أو أكثر وتختلف المدة حسب كل جامعة. المقاعد الجامعية محدودة فقد لا تحصل على فرصة في دراسة هندسة البرمجيات في الجامعة التي تطمح لها وتضطر لدراسة تخصص آخر. يعتبر التعليم الجامعي مرتفع التكلفة مقارنة بالتعلم الذاتي حيث ستضطر لدفع تكاليف الرسوم الجامعية والكتب وأجور المواصلات وغيرها. ستضطر خلال فترة دراسة تخصص هندسة البرمجيات في الجامعة إلى تعلم الكثير من المواد النظرية المملة التي لن تفيد مطلقًا في سوق العمل. لا زال أسلوب تعليم البرمجة وتقييم معلومات الطالب في بعض الكليات معتمدًا على الأساليب التقليدية القديمة التي تختبر الحفظ بدل الفهم ولا تقيم مهارات الطالب تقييمًا صحيحًا، وهذا الأسلوب يناسب التخصصات النظرية لكنه لا يناسب تخصص هندسة البرمجيات والحاسوب الذي يعتمد على الفهم والإبداع والمنطق. لا تواكب بعض المناهج الجامعية التطور السريع في مجال البرمجة وتقنياتها أولًا بأول، وقد تعتمد لسنوات على نفس المناهج القديمة التي عفا عليها الزمن فإذا اكتفيت بما تعلمك إياه الجامعة ستخرج إلى سوق العمل وتفاجأ بالفجوة الكبيرة بين ما تعلمته في أروقة الجامعات وبين ما هو مستخدم في سوق العمل. الدراسة الذاتية لتخصص هندسة البرمجيات هل تعلم أن معظم المبرمجين الناجحين لم يكونوا خريجي كليات تقنية وليس لديهم شهادات جامعية في أحد التخصصات البرمجية بل اتبعوا أسلوب التعلم الذاتي وأثبتوا جدارتهم من خلال العمل على تطوير مشاريعهم البرمجية الخاصة حيث توفر الدراسة الذاتية لتخصص هندسة البرمجيات جملة من الفوائد والمميزات التي قد لا يوفرها لك خيار التعلم الجامعي التقليدي، وإذا كنت ترغب بالاطلاع على قصص نجاح لأشخاص تمكنوا من تحقيق النجاح والتميز من خلال دراسة هندسة البرمجيات بشكل ذاتي أنصحك بمطالعة قصص نجاح طلاب أكاديمية حسوب ففيها تجارب ملهمة حقًا. لكن الدراسة الذاتية لتخصص هندسة البرمجيات تتضمن أيضًا جوانب إيجابية وأخرى سلبية سنتعرف عليها في الفقرات التالية. مميزات الدراسة الذاتية لتخصص هندسة البرمجيات من أبرز الفوائد التي ستعود عليك في حال قررت دراسة هندسة البرمجيات بشكل ذاتي ما يلي: مرونة التعلم في أي وقت وتحديد عدد ساعات التعلم وجدول الدارسة وفق ما يناسب ظروفك. تعلم البرمجة من المنزل أو أي مكان يناسبك بدل تكبد عناء الذهاب لمقر الجامعة الذي قد يكون موجودًا في مكان بعيد عنك أو خارج مدينتك. التعلم بتكلفة بالمجّان أو دفع تكلفة منخفضة مقارنة بتكاليف التعليم الجامعي. التعلم بصورة أسرع من خلال التركيز على تعلم لغات البرمجة والتقنيات التي تفيدك في تخصصك البرمجي فقط. التعلم من مصادر حديثة ما يبقيك على اطلاع بأحدث التقنيات في تخصصك البرمجي بدل دراسة معلومات قديمة عفا عليها الزمن. توفر مصادر تعلم عديدة ومتنوعة تمكّنك من تحديد الأسلوب الذي تفضلّه وتحقيق أفضل استفادة. التركيز على التطبيق العملي وتنفيذ المشاريع المطلوبة في سوق العمل بدل التعلم النظري الممل. تعلم التنظيم وإدارة الوقت وتحديد أولويات التعلم يما يحقق أهدافك من التعليم. تطوير مهارة التواصل الفعال مع المجتمعات البرمجية عبر الإنترنت. زيادة الاعتماد على النفس واكتساب مرونة في مواجهة التحديات وحل أي مشكلات تواجهك بمفردك. صعوبات وتحديات الدراسة الذاتية لتخصص هندسة البرمجيات بالرغم مما توفره الدراسة الذاتية للبرمجة من مميزات ونقاط قوة إلا أنها لا تخلو من بعض الصعوبات والتحديات وإن لم تتعامل معها بشكل صحيح فقد تصاب بالإحباط وتفشل في تحقيق أهدافك، وإليك قائمة بأبرز هذه الصعوبات: التعلم الفوضوي وغير المنضبط فقد تشتتك كثرة المصادر ولا تعرف من أين تبدأ وما هي الخطوات الصحيحة التي عليك اتباعها. عدم وجود أستاذ يقيّمك ويمكّنك من تحديد مستواك ومعرفة نقاط ضعفك والعمل على تحسينها. عدم تنظيم الوقت والمماطلة وفقدان التركيز والانضباط والتحفيز. لا تحصل على شهادة أكاديمية تثبت ما تعلمته لكن يمكن حل هذه النقطة ببناء معرض أعمال قوي يثبت كفاءتك وهو الأهم حاليًا في سوق العمل والكل يبحث عنه بدلًا من الشهادة. إذا كنت شخصًا اجتماعيًا فقد يشعرك التعلم الذاتي بنوع من العزلة لعدم وجود زملاء أو فريق يشاركك التعلم ويتعاون معك في المشاريع، والانضمام للمجتمعات والمنتديات عبر الانترنت يساعدك في تخطي هذه الصعوبة. هل أتعلم البرمجة عبر الجامعة أم ذاتيًا؟ سواء اخترت تعلم البرمجة بشكل ذاتي أو في كلية أو جامعة فيجب أن تضع في الحسبان أن تعلم البرمجة ليس بالأمر اليسير بل يحتاج للكثير من الصبر والمثابرة وبذل الجهد والتعلم المستمر من مصادر حديثة والتطبيق العملي، ستحتاج إلى قضاء وقت كبير في البرمجة والعمل على مشاريع لتحسين مهاراتك لكن حلاوة الإنجاز ستهون عليك التعب. وختاًا إليك بعض النصائح لتصبح مهندس برمجيات ناجح: ابدأ بتعلم الخوارزميات والتفكير المنطقي قبل أن تبدأ في تعلم أساسيات البرمجة، ففهم هذه المواضيع أمر بالغ الأهمية لك كمبرمج أو مهندس برمجيات. تعرف على أهم مجالات البرمجة وأكثر التخصصات البرمجية المطلوبة في سوق العمل الذي تود التوجه له، وحدد بناءً على ذلك ما هو التخصص البرمجي الذي تود متابعة دراسته واحترافه، ثم ضع قائمة بأهم لغات البرمجة والتقنيات التي عليك تعلمها لتتقنها وضع خطة محكمة لتعلمها. طبق كل ما تتعلمه فكلما كتبت أكوادًا أكثر كلما استوعبت المفاهيم البرمجية بشكل أفضل، وتأكد أنك مهمًا قرأت من دروس وشاهدت من مقاطع فيديو فلن تصبح مبرمجًا كفؤًا إلا إذا مارست ما تتعلمه بشكل عملي وتدربت على حل المشكلات وتصحيح الأخطاء التي ستظهر عند تنفيذ الكود. فكّر ثم برمج ولا تكتب أي كود برمجي قبل أن تخطط وتحلل مشروعك بشكل جيد وتفهم الوظائف والمهام المطلوبة بوضوح ثم تنفذها من خلال الأكواد البرمجية. حدّث معلوماتك باستمرار فمهما كان مستوى خبرتك فهناك دومًا معلومات وتقنيات جديدة تحتاج لتعلمها. شارك في المنتديات والمجتمعات البرمجية وتبادل معلوماتك وتجاربك مع المبرمجين والمطورين الآخرين. من الجيد أن تتعلم أكثر من تقنية أو لغة لتطوير مشاريعك البرمجية فهذا يجعلك قابلًا للتكيف مع سوق العمل والتغير الكبير في المتطلبات. إذا كنت تشعر بالتشتت ولا تملك القدرة على تنظيم تعلمك وتقييم معلوماتك فإن اتباعك لدورات تدريبية منهجية في التخصص البرمجي الذي تحبه سيمكنك من اتباع الخطوات الصحيحة ويساعدك في التعلم بشكل أفضل وأسرع. كيف تساعدك دورات أكاديمية حسوب على دراسة هندسة البرمجيات تساعدك أكاديمية حسوب على تخطي العقبات التي قد تواجهها في في دراستك الذاتية لتخصص هندسة البرمجيات من خلال توفير تعلم منهجي منظم فهي توفر لك الكثير من مصادر التعلم المجانية لتعلم البرمجة من كتب ودروس ومقالات وسلاسل تعليمية تناسب كافة المستويات، كما توفر الأكاديمية دورات تعليمة متنوعة في أشهر التخصصات البرمجية المطلوبة في سوق العمل كما تضمن لك حداثة المحتوى التعليمي فالأكاديمية تضيف تحديثات مستمرة لمختلف الدورات لتواكب أحدث التطورات والاتجاهات. أضف إلى ذلك أنها توفر قسم الأسئلة والأجوبة كمجتمع برمجي فاعل يساعدك خلال رحلتك التعليمية. وعند اشتراكك في إحدى دورات أكاديمية حسوب ستضمن الحصول على شروحات برمجية ذات جودة عالية تدمج بين التعليم النظري والتطبيق العملي حيث تشمل دورات الأكاديمية العديد من المشاريع التطبيقية التي تمنحك خبرة عملية وتقيم مستواك البرمجي بشكل صحيح وتساعدك على بناء معرض أعمال احترافي. كما توفر لك دعمًا تفاعليًا حيث يمكنك التواصل مع فريق متخصص من المدربين المستعدين لشرح ما يصعب عليك خلال دراستك وإجابتك على على أي تساؤل تطرحه، والأهم أنك ستتمكن من خوص امتحان يقيّم ما تعلمته خلال الدورة لتتمكن من الحصول على شهادة معتمدة من أكاديمية حسوب في حال نجاحك فيه الأمر الذي يعزز فرصتك في الحصول على فرصة عمل مناسبة. وفي الختام تذكّر أن رحلة تعلم البرمجة قد لا تكون سهلة لكنها بالتأكيد ستكون رحلة مليئة بالتحديات والتشويق والمتعة إذا سلكتها بالطريقة المناسبة واتبعت الخطوات الصحيحة، لذا من المهم أن توازن إيجابيات وسلبيات كل طريقة من طرق تعلم تخصص هندسة البرمجيات وتختار الطريقة التي تناسب ظروفك ومتطلباتك وتلتزم بالنصائح التي ذكرتها لك، وستنجح بإذن الله في تحقيق كل أهدافك والتغلب على كل التحديات والصعوبات التي تواجهك. الخلاصة تعرفنا في مقال اليوم على أبرز الطرق التي يمكنك من خلالها دراسة تخصص هندسة البرمجيات ومميزات وتحديات كل طريقة منها، ويمكن أن نخلص في نهاية المقال بأن الدراسة الذاتية للبرمجة هي الخيار الأفضل لك إذا كنت شخصأ يسعى لدخول سوق العمل وكسب المال بسرعة وتريد أن تحافظ على حداثة معلوماتك وتبقى مطّلعًا على كل جديد لكن هذا يتطلب منك الكثير من الجهد والتنظيم والالتزام واعتماد مصادر تعلم جيدة والتحفيز الذاتي المتواصل لتجنب الفشل والإخفاق. أما الدراسة الجامعية فهي الأنسب لك إذا كان هدفك هو الحصول على شهادة جامعية والدراسة ضمن بيئة تعلم منظمة ومنهجية وإذا تمكنت من الجمع بين التعليم الجامعي والتعلم الذاتي فسيكون هذا أمرًا رائعًا بإمكانه تجاوز القصور الذي تعاني منه مناهج التعليم التقليدي. اقرأ أيضًا تعلم البرمجة ما هي فوائد تعلم البرمجة؟ أسهل لغات البرمجة مدخل إلى تطوير البرمجيات Software Development1 نقطة
-
تتكوَّن جميع برامج واجهات المُستخدِم الرسومية GUI التي تعرَّضنا إليها حتى الآن من نافذةٍ واحدة، ولكن غالبية البرامج تتكوَّن في الواقع من عدة نوافذ. ولذلك سنناقش في هذا المقال طريقة إدارة التطبيقات متعددة النوافذ؛ كما سنتناول صناديق النافذة dialog boxes، وهي نوافذٌ صغيرة تظهر وتختفي، وتُستخدَم عادةًً للحصول على مُدْخَلٍ من المُستخدِم. بالإضافة إلى ذلك، سنتعرَّض للصنف WebView، وهو أداة تحكُّم بمكتبة JavaFX، وتدعم كثيرًا وظائف متصفح الإنترنت. صناديق النافذة Dialog Boxes أيّ صندوق نافذة هو ببساطة نافذة تعتمد على نافذةٍ أخرى تَعمَل بمثابة أبٍ أو مالكٍ لصندوق النافذة؛ ويَعنِي ذلك، أنه لو أغلقنا النافذة المالكة، فسيُغلَق صندوق النافذة أوتوماتيكيًا. قد يكون صندوق النافذة شرطيًا modal أو غير شرطي modeless؛ فعند فتح صندوق نافذة شرطي، ستُعطَّل النافذة المالكة له، ولا يستطيع المُستخدِم التفاعل معها حتى يُغلِق صندوق النافذة. هناك أيضًا صناديق نافذة شرطية على مستوى التطبيق application modal؛ أي أنها تُعطِّل التطبيق بالكامل وليس فقط النافذة المالكة لها، وتُعدّ غالبية صناديق النافذة بمكتبة JavaFX من هذا النوع. تظهر صناديق النافذة الشرطية عادةً أثناء تنفيذ البرنامج، وذلك لكي تطلُب من المُستخدِم مُدْخَلًا معينًا، أو فقط لعرض رسالةٍ للمُستخدِم أحيانًا. في المقابل، لا تُعطِّل صناديق النافذة غير الشرطية تفاعل المُستخدِم مع نوافذها المالكة، ولكنها ما تزال تُغلَق أوتوماتيكيًا عند غلق النافذة المالكة. يُستخدَم هذا النوع عادةً لإظهار عرضٍ view مختلفٍ للبيانات الموجودة بالنافذة المالكة، أو لعرض أدوات تحكُّم إضافية تؤثر على النافذة المالكة. يُمكِننا ضبط مرحلةٍ من الصنف Stage لتَعمَل مثل صندوق نافذة، ولكن غالبية صناديق النافذة ببرامج JavaFX هي كائناتٌ مُنتمية إلى الصنف Dialog المُعرَّف بحزمة javafx.scene.control، أو أي من أصنافه الفرعية subclasses. فإذا كان dlg كائن صندوق نافذة من النوع Dialog، فسيتضمَّن توابع النسخ instance methods التالية لعرض صندوق النافذة: dlg.show() و dlg.showAndWait(). إذا استخدمنا التابع dlg.showAndWait() لعرض الصندوق، فسيكون صندوق النافذة شرطيًا، حتى على مستوى التطبيق. لا يعود التابع showAndWait() إلى أن يُغلَق صندوق النافذة، وتكون بالتالي قيمة مُدْخَل المُستخدِم متاحةً بعد استدعاء showAndWait() مباشرةً. في المقابل، إذا استخدمنا التابع dlg.show() لعرض الصندوق، فسيكون غير شرطي، إذ يعود التابع show() فورًا، ويستطيع المُستخدِم بالتالي التعامل مع النافذة والصندوق، والتبديل بينهما. وفي الواقع، يتشابه استخدام صناديق النافذة غير الشرطية مع البرمجة على التوازي parallel programming نوعًا ما؛ فهناك شيئان قيد التنفيذ بنفس الوقت. سنُركّز هنا على صناديق النافذة الشرطية فقط. يُعدّ الصنف Dialog<T> نوعًا ذا معاملاتٍ غير مُحدّدة النوع parameterized، إذ يُمثِّل معامل النوع type parameter نوع القيمة التي سيعيدها التابع showAndWait()، والتي تكون تحديدًا من النوع Optional<T>؛ ويَعنِي ذلك أنها قيمةٌ من النوع T، ولكنها قد تكون موجودةً أو غير موجودة. يتضمَّن الصنف Optional -المُعرَّف بحزمة java.util- التابع isPresent()، والذي يُعيد قيمةً من النوع boolean، التي تشير إلى ما إذا كانت القيمة موجودةً أم لا، كما يتضمَّن التابع get() الذي يعيد تلك القيمة إذا كانت موجودة. إذا لم تكن القيمة موجودةً، فسيؤدي استدعاء التابع get() إلى حدوث استثناءٍ exception؛ ويَعنِي ذلك أنه إذا أردنا استخدام القيمة المعادة من التابع showAndWait()، فعلينا أولًا استدعاء التابع isPresent() لنتأكّد من أن التابع قد أعاد قيمةً فعلًا. يحتوي أي صندوق نافذة عادةً على زرٍ واحدٍ أو أكثر لغلق الصندوق على الأقل، وتكون أسماء تلك الأزرار غالبًا هي: "OK" أو "Cancel" أو "Yes" أو "No". كما يُستخدَم نوع التعداد ButtonType لتمثيل الأزرار الأكثر شيوعًا، ويتضمَّن القيم ButtonType.OK و ButtonType.CANCEL و ButtonType.YES و ButtonType.NO؛ إذ يُعدّ النوع ButtonType القيمة المعادة الأكثر شيوعًا من صناديق النافذة المُمثَلة بالصنف Dialog، وذلك للإشارة إلى الزر الذي نقر عليه المُستخدِم لغلق الصندوق، ويكون صندوق النافذة من النوع Dialog<ButtonType> في تلك الحالة. يُعدّ Alert صنفًا فرعيًا من الصنف Dialog<ButtonType>، إذ يُسهِّل إنشاء صناديق النافذة التقليدية التي تَعرِض رسالةً نصيةً للمُستخدِم مصحوبةً بزرٍ واحدٍ أو اثنين، وتَعمَل بمثابة تنبيهٍ للمُستخدِم. كنا قد استخدمنا هذا الصنف فعليًا في مقال مدخل إلى التعامل مع الملفات في جافا لكي نَعرِض بعض رسائل الخطأ للمُستخدِم، ولكن دون أن نتعرَّض لطريقة عمله. يُمكِننا إنشاء كائنٍ من النوع Alert على النحو التالي: Alert alert = new Alert( alertType, message ); ينتمي المعامل الأول إلى نوع التعداد Alert.AlertType الذي يُمكِن لقيمته أن تكون واحدةً مما يلي: Alert.AlertType.INFORMATION. Alert.AlertType.WARNING. Alert.AlertType.ERROR. Alert.AlertType.CONFIRMATION. وعند تخصيص أي من القيم الثلاثة الأولى، سيحتوي الصندوق المعروض على زر "OK" وحيد ولا يفعل أكثر من مجرد عرض رسالةٍ نصيةٍ للمُستخدِم، وفي تلك الحالة، لا حاجة لفحص أو استرجاع القيمة المعادة من التابع alert.showAndWait(). عند تمرير القيمة الأخيرة، سيتضمَّن الصندوق زر "OK" و زر "Cancel"، ويُستخدَم عادةً لسؤال المُستخدِم عما إذا كان يريد الاستمرار بتنفيذ عملية يُحتمَل أن تكون خطيرةً، مثل حذف ملف؛ إذ يكون من الضروري فحص القيمة المعادة من التابع في تلك الحالة، وهذا هو ما تفعله الشيفرة التالية: Alert confirm = new Alert( Alert.AlertType.CONFIRMATION, "Do you really want to delete " + file.getName() ); Optional<ButtonType> response = confirm.showAndWait(); if ( response.isPresent() && response.get() == ButtonType.OK ) { file.delete(); } إضافةً إلى الأزرار، قد يحتوي صندوق النافذة على مساحةٍ للمحتوى وعنوانٍ رئيسي يَظهَر أعلى تلك المساحة، ورسمةٍ تَظهَر إلى جانب العنوان الرئيسي إن وجد، أو إلى جانب المحتوى؛ وبالتأكيد عنوان يَظهَر بشريط عنوان صندوق النافذة. تكون الرسمة عادةً أيقونةً صغيرةً؛ فبالنسبة لصناديق النافذة من النوع Alert، تُوضَع الرسالة بمساحة المحتوى، وتُضبَط الخاصيات الأخرى أوتوماتيكيًا بما يتناسب مع نوع التنبيه المُستخدَم، ويُمكِن مع ذلك تعديلها باستدعاء التوابع التالية المُعرَّفة بالصنف Dialog قبل عرض التنبيه: alert.setTitle( windowTitle ); alert.setGraphic( node ); alert.setHeaderText( headerText ); يُمكِن لأي من تلك القيم المُمرَّرة أن تكون قيمةً فارغةً null، كما يُمكِننا ضبط المحتوى إلى أي عقدة مبيان مشهد عشوائية لتحلّ محل الرسالة النصية، وذلك باستدعاء التابع التالي: alert.getDialogPane().setContent( node ); ولكننا نُطبِّق ذلك عادةً على صندوق نافذة عادي من النوع Dialog، لا على تنبيهٍ من النوع Alert. تُوضِّح تنبيهات التأكيد بالصورة التالية المكونات المختلفة الموجودة بأي صندوق نافذة، إذ يُمكِّننا ملاحِظة أن العنوان الرئيسي الخاص بالصندوق الموجود على يمين الصورة فارغ؛ وإذا أردت عَرض نصٍ متعدد الأسطر ضمن تنبيه، فلا بُدّ من إضافة محرف السطر الجديد ("\n") إلى النص: تُوفِّرمكتبة JavaFX الصنف الفرعي TextInputDialog المُشتَق من الصنف Dialog<String> لتمثيل صناديق النافذة التي تقرأ مُدْخَلًا من المُستخدِم. ويعيد التابع showAndWait() في تلك الحالة قيمةً من النوع Optional<String>، كما يحتوي صندوق النافذة المُستخدِم لذلك الصنف على حقلٍ نصي من النوع TextField، مما يُمكِّن المُستخدِم من إدخال سطر نصي؛ كذلك، يحتوي على زر "OK" و زر "Cancel". يَستقبِل الباني معاملًا من النوع String، والذي يُمثِّل المحتوى المبدئي للحقل النصي؛ فإذا أردنا أن نطرح سؤالًا على المُستخدِم أو أن نَعرِض رسالةً معينةً عليه، فيُمكِننا وضعها بالعنوان الرئيسي للصندوق. يعيد صندوق النافذة محتويات الحقل النصي إن وُجدت، والتي قد تكون مجرد سلسلةٍ نصيةٍ فارغة، وإذا نقر المُستخدِم على زر "Cancel" أو أغلق صندوق النافذة ببساطة، فستكون القيمة المعادة غير موجودة. ألقِ نظرةً على الشيفرة التالية: TextInputDialog getNameDialog = new TextInputBox("Fred"); getNameDialog.setHeaderText("Please enter your name."); Optional<String> response = getNameDialog.showAndWait(); if (response.isPresent() && response.get().trim().length() > 0) { name = response.get().trim(); } else { Alert error = new Alert( Alert.AlertType.ERROR, "Anonymous users are not allowed!" ); error.showAndWait(); System.exit(1): } إلى جانب الصنفين Alert و TextInputDialog، يُعرِّف الصنف SimpleDialogs.java -كتبه المؤلف- التوابع الساكنة static التالية التي يُمكِن استخدامها لعرض أكثر أنواع صناديق النافذة شيوعًا: SimpleDialogs.message(text): يعرِض صندوق نافذة يحتوي على رسالة نصية وزر"OK"، ولا يًعيد أي قيمة. لاحِظ أنك لا تحتاج إلى كتابة محرف السطر الجديد ضمن الرسالة إذا أردتها أن تكون متعددة الأسطر؛ لأن ذلك يحدث تلقائيًا مع الرسائل الطويلة. يَستقبِل التابع أيضًا معاملًا ثانيًا اختياريًا لتخصيص عنوان صندوق النافذة. SimpleDialogs.prompt(text): يعرِض صندوق نافذة يحتوي على رسالة نصية وحقل إدخال نصي مع زر "OK" و زر "Cancel"، ويُعيد قيمةً من النوع String تُمثِّل محتويات الحقل النصي إذا كان المُستخدِم قد نقر على "OK"؛ أو على القيمة الفارغة null إذا كان المُستخدِم قد أغلق الصندوق. يَستقبِل التابع أيضًا معاملين اختياريين آخرين لتخصيص عنوان صندوق النافذة، والمحتوى المبدئي للحقل النصي على الترتيب. SimpleDialogs.confirm(text): يَعرِض صندوق نافذة يحتوي على رسالة نصية مع زر "Yes" و زر "No" و زر "Cancel"، ويُعيد قيمةً من النوع String، والتي لا بُدّ أن تكون واحدةً من القيم التالية "yes" أو "no" أو "cancel". يَستقبِل التابع معاملًا ثانيًا اختياريًا لتخصيص عنوان صندوق النافذة مثل التابعين السابقين. يحتوي الصنف SimpleDialogs على بعض الخيارات الأخرى، مثل صندوق نافذة بسيط لاختيار لون، والتي يُمكِنك الإطلاع عليها بقراءة شيفرة الملف SimpleDialogs.java؛ كما يَسمَح أيضًا البرنامج TestDialogs.java للمُستخدِم بتجربة صناديق النافذة المختلفة المُعرَّفة بالصنف SimpleDialogs. الصنفان WebView و WebEngine سنناقش ببقية هذا المقال برنامج متصفح إنترنت مُتعدّد النوافذ، إذ تبدو كتابة متصفح إنترنت عمليةً معقدة، وهي كذلك فعلًا، ولكن مكتبة JavaFX تُسهِّل ذلك كثيرًا بتنفيذ غالبية العمل المطلوب ضمن مجموعةٍ من الأصناف القياسية؛ إذ يُمثِّل الصنف WebView المُعرَّف بحزمة javafx.scene.control أداة تحكُّم يُمكِنها تحميل صفحة إنترنت وعرضها، كما تستطيع تلك الأداة معالجة معظم صفحات الإنترنت جيدًا بما في ذلك تنفيذ شيفرة JavaScript، إذ تُستخدَم لغة البرمجة جافاسكربت JavaScript لبرمجة صفحات إنترنت ديناميكية، ولا علاقة لها بلغة Java). إضافةً لما سبق، يُمثِّل الصنف WebView العرض view ضمن نمط نموذج-عرض-مُتحكَّم Model-View-Controller الذي ناقشناه بمقال أمثلة عن رسوميات فاخرة باستعمال جافا؛ إذ يُنفَّذ غالبية العمل المطلوب لتحميل صفحات الإنترنت وإدارتها من خلال كائنٍ من النوع WebEngine، والذي يُمثِّل جزءًا من المُتحكِّم controller؛ أما النموذج، فهو هيكل بياني data structure يتضمَّن محتويات صفحة الإنترنت. ويُنشِئ الصنف WebEngine النموذج عند تحميل الصفحة، ثم يَعرِض الصنف WebView محتوياتها. يجب أن نَعرِض كائن الصنف WebView ضمن نافذة؛ إذ يُمثِّل الصنف الفرعي BrowserWindow.java -المُشتَق من صنف النافذة القياسي Stage- نافذة متصفح إنترنت كاملة، وهو يحتوي على كائنٍ ينتمي إلى الصنف WebView، بالإضافة إلى شريط قوائم وبعض أدوات التحكُّم الأخرى، مثل صندوق إدخال نصي يُمكِّن المُستخدِم من كتابة محدّد موارد مُوحد URL لصفحة إنترنت معينة، وزر "Load" ينقر عليه المُستخدِم لتحميل صفحة الإنترنت من محدّد الموارد الموحد إلى كائن الصنف WebView. بالإضافة إلى ذلك، يُمكِن لباني الصنف BrowserWindow أن يَستقبِل معاملًا إضافيًا لتخصيص محدِّد موارد موحد مبدئي يُحمَّل تلقائيًا عند فتح النافذة. يتضمَّن كل كائن من النوع WebView كائنًا آخرًا من النوع WebEngine، والذي يُمكِننا استرجاعه باستدعاء التابع webEngine = webview.getEngine(). يمكننا الآن تحميل load صفحة إنترنت باستدعاء ما يلي: webEngine.load( urlString ); إذ أن urlString سلسلةٌ نصيةٌ تُمثِّل محدِّد موارد موحد -ألقِ نظرةً على مقال تواصل تطبيقات جافا عبر الشبكة-. ويجب أن تبدأ تلك السلسلة ببروتوكول، مثل "http:" أو "https:"، ولهذا يضيف البرنامج كلمة "http://" إلى مقدمة السلسلة النصية المُتضمِّنة لمُحدّد الموارد الموحد، إذا لم تكن تحتوي على بروتوكول فعليًا. إضافةً لما سبق، يُحمِّل البرنامج صفحة إنترنت جديدة أوتوماتيكيًا، وذلك إذا نقر المُستخدِم على رابط ضمن الصفحة المعروضة حاليًا. تُحمَّل صفحات الإنترنت بصورةٍ غير متزامنة asynchronous؛ أي أن التابع webEngine.load() يعود فورًا، في حين تُحمَّل صفحة الإنترنت ضمن خيط thread مُشغَّلٍ بالخلفية. وعند اكتمال عملية التحميل، تُعرَض صفحة الإنترنت داخل كائن الصنف WebView؛ أما في حالة فشل التحميل لسببٍ ما، فلا يحدث أي تنبيه تلقائي، ولكن ما يزال بإمكاننا الحصول على بعض المعلومات بإضافة مستمعي أحداث إلى الخاصيتين location و title -من النوع String- القابلتين للمراقبة observable، والمُعرَّفتين بالصنف WebEngine؛ إذ تُمثِّل الخاصية location محدد الموارد الموحد لصفحة الإنترنت المعروضة حاليًا، أو التي يُجرَى تحميلها؛ بينما تُمثِّل الخاصية title عنوان صفحة الإنترنت الحالية، والتي تَظهَر بشريط عنوان النافذة التي تَعرِض صفحة الإنترنت. على سبيل المثال، يستمع الصنف BrowserWindow إلى الخاصية title ويَضبُط عنوان النافذة ليتوافق مع محتوياتها كما يلي: webEngine.titleProperty().addListener( (o,oldVal,newVal) -> { if (newVal == null) setTitle("Untitled " + owner.getNextUntitledCount()); else setTitle(newVal); }); يستمع البرنامج إلى الخاصية location أيضًا، ويَعرِض قيمتها داخل عنوان من النوع Label أسفل النافذة؛ في حين سنناقش owner لاحقًا بالأسفل. يُمكِننا أيضًا أن نضيف مستمعًا إلى خاصية webEngine.getLoadWorker().stateProperty() لمراقبة تقدّم تحميل الصفحة. ألقِ نظرةً على شيفرة الصنف BrowserWindow.java لترى مثالًا على ذلك. ذكرنا بالأعلى أن كائن الصنف WebView (مع كائن الصنف WebEngine الخاص به) قادرٌ على تشغيل شيفرة JavaScript المُضمَّنة بصفحات الإنترنت؛ ولكن هذا ليس دقيقًا تمامًا، إذ تحتوي JavaScript على بعض البرامج الفرعية subroutines المسؤولة عن إظهار صناديق نوافذ بسيطة؛ فعلى سبيل المثال، يَعرِض صندوق النافذة من النوع "alert" رسالةً نصيةً للمُستخدِم؛ بينما يطرح صندوق النافذة من النوع "prompt" سؤالًا على المُستخدِم ويتلقى ردَّه النصي عليها؛ أما صندوق النافذة من النوع "confirm"، فيَعرِض رسالةً للمُستخدِم مع زر "OK" و زر "Cancel"، ويتلقى قيمةً مُعادةً من النوع boolean تشير إلى ما إذا كان المُستخدِم قد أغلق الصندوق بالنقر على زر "OK". في الواقع، يتجاهل الصنف WebEngine طلبات JavaScript لعرض تلك الصناديق افتراضيًا، ولكن يُمكِننا إضافة مستمعي أحداث للاستجابة إلى تلك الطلبات، إذ يَستخدِم الصنف BrowserWindow صناديق نافذة من تلك المُعرَّفة بالصنف SimpleDialogs للرد على تلك الأحداث؛ فعندما تحاول JavaScript أن تَعرِض صندوق نافذة تنبيهي مثلًا، فسيُولِّد كائن الصنف WebEngine حدثًا من النوع AlertEvent، يتضمَّن الرسالة التي تريد JavaScript أن تَعرِضها، وسيستجيب الصنف BrowserWindow باستدعاء SimpleDialogs.message() لكي يَعرِض الرسالة للمُستخدِم. ألقِ نظرةً على الشيفرة التالية: webEngine.setOnAlert( evt -> SimpleDialogs.message(evt.getData(), "Alert from web page") ); تختلف معالجة صناديق النافذة من النوعين prompt و confirm بعض الشيء؛ لكونهما يعيدان قيمةً، وتبين الشيفرة التالية الطريقة التي اتبعها البرنامج لمعالجتهما: webEngine.setPromptHandler( promptData -> SimpleDialogs.prompt( promptData.getMessage(), "Query from web page", promptData.getDefaultValue() ) ); webEngine.setConfirmHandler( str -> SimpleDialogs.confirm(str, "Confirmation Needed").equals("yes") ); لم نناقش بعد شريط القوائم الذي يدعمه الصنف BrowserWindow؛ إذ يحتوي ذلك الشريط على قائمةٍ واحدةٍ اسمها "Window"، وتحتوي بدورها على مجموعةٍ من الأوامر لأغراضٍ معينة، مثل فتح نافذة متصفح جديدة أو غلْق النافذة الحالية، كما تحتوي على قائمةٍ مكوّنة من نوافذ المتصفح المفتوحة حاليًا، ويستطيع المُستخدِم أن يختار أيًا منها ليُحضره إلى مقدمة الشاشة؛ ولكي تفهم طريقة تنفيذ ذلك، عليك أولًا أن تفهم طريقة استخدام الصنف BrowserWindow ضمن برنامجٍ متُعدّد النوافذ. إدارة عدة نوافذ لا يُعدّ الصنف BrowserWindow تطبيقًا، أي لا يُمكِن تشغيله على أنه برنامجٌ بحد ذاته، وإنما يُمثِّل نافذةً واحدةً ضمن برنامجٍ متعدد النوافذ. ستجِد النسخة القابلة للتشغيل من هذا الصنف بالملف WebBrowser.java. يمتد الصنف WebBrowser من الصنف Application مثل أي برنامجٍ مُصمَّم بمكتبة JavaFX، كما يعتمد على الصنفين BrowserWindow.java و SimpleDialogs.java؛ ولذلك ستحتاج إلى الأصناف الثلاثة لكي تتمكَّن من تشغيل البرنامج. يحتوي أي صنفٍ ينتمي للنوع Application على التابع start() الذي يستدعيه النظام عند بدء تشغيل التطبيق، إذ يستقبل هذا التابع معاملًا من النوع Stage يُخصِّص النافذة الرئيسية للبرنامج، ولكن ليس من الضروري للبرنامج أن يَستخدِم تلك النافذة فعليًا؛ إذ يتجاهل التابع start() المُعرَّف بالصنف WebBrowser النافذة الرئيسية المُخصَّصة، ويُنشِئ بدلًا منها نافذةً من النوع BrowserWindow، لتكون هي أول ما يَظهَر عند تشغيل البرنامج، وقد ضُبطَت تلك النافذة لتُحمِّل الصفحة الرئيسية لصفحة الإنترنت التي تحتوي على النسخة المُتاحة عبر الإنترنت من هذا الكتاب. يُمثِّل ما سبق كل ما ينبغي أن يفعله الصنف WebBrowser.java لتنفيذ البرنامج باستثناء القائمة "Window" التي تحتوي على قائمةٍ بكل النوافذ المفتوحة. ونظرًا لعدم كون تلك القائمة جزءًا من بيانات أي نافذة على حدة، فقد كان من الضروري الاحتفاظ بها بمكانٍ آخر، مثل كائن الصنف WebBrowser، ويمكن بدلًا من ذلك تخزين قائمة النوافذ مثل متغير عضو ساكن static بالصنف BrowserWindow، مما يَعنِي تشاركُه بين جميع النسخ المنشأة من ذلك الصنف؛ إذ يُعرِّف الصنف WebBrowser تابعه newBrowserWindow() بغرض فتح نافذةٍ جديدة؛ بينما يحتوي الصنف BrowserWindow على متغير النسخة owner للإشارة إلى كائن الصنف WebBrowser الذي فتح النافذة. وبالتالي إذا أراد البرنامج فتح نافذة جديدة، فإنه يفعل ذلك باستدعاء التابع owner.newBrowserWindow(url)، إذ يشير المعامل url إلى مُحدِّد الموارد الموحد الخاص بصفحة الإنترنت المطلوب تحميلها بالنافذة الجديدة. وفي المقابل، قد يحتوي المعامل على القيمة الفارغة null بهدف فتح نافذة متصفحٍ فارغة. يتحدََد حجم أي نافذة بمكتبة JavaFX وفقًا لحجم المشهد -من النوع Scene- المعروض داخلها افتراضيًا، كما تظهر النافذة بمنتصف الشاشة افتراضيًا؛ ويُمكِننا بدلًا من ذلك ضبط حجم النافذة ومكانها قبل فتحها. بالنسبة للبرامج متعددة النوافذ، لا يُحبَّذ عرض جميع النوافذ بنفس المكان بالضبط، كما يبدو أن الحجم الافتراضي لكائنات الصنف BrowserWindow صغيرٌ جدًا بمعظم شاشات الحاسوب؛ ولذلك يَضبُط التطبيق WebBrowser موضع جميع النوافذ التي يفتحها ليَبعُد مكان كل نافذةٍ مسافةً قصيرةً عن مكان النافذة التي فتحها التطبيق بالمرة السابقة؛ كما يضبُط التطبيق حجم النافذة بما يتناسب مع حجم الشاشة. يحتوي الصنف Screen المُعرَّف بحزمة javafx.stage على التابع الساكن Screen.getPrimary() الذي يعيد كائنًا يتضمَّن معلوماتٍ عن الشاشة الرئيسية للحاسوب؛ كما ويحتوي ذلك الكائن بدوره على كل من التابعين Screen.getPrimary().getVisualBounds()، الذي يُعيد كائنًا من النوع Rectangle2D ويُمثِّل المساحة القابلة للاستخدام من الشاشة الرئيسية. يَستدعِي تابع البرنامج start() ذلك التابع لكي يَحسِب كُلًا من حجم أول نافذة ومكانها على النحو التالي: public void start(Stage stage) { // (stage is not used) openWindows = new ArrayList<BrowserWindow>(); // List of open windows. screenRect = Screen.getPrimary().getVisualBounds(); // 1 locationX = screenRect.getMinX() + 30; locationY = screenRect.getMinY() + 20; // 2 windowHeight = screenRect.getHeight() - 160; windowWidth = screenRect.getWidth() - 130; if (windowWidth > windowHeight*1.6) windowWidth = windowHeight*1.6; // 3 newBrowserWindow("http://math.hws.edu/javanotes/index.html"); } // end start() حيث أن: [1]: يشير (locationX,locationY) إلى مكان الركن الأيسر العلوي للنافذة التي ستُفتَح بالمرة القادمة، وتتحرك النافذة الأولى إلى الأسفل قليلًا من الركن الأيسر العلوي للجوانب المرئية للشاشة الرئيسية. [2]: تعني أن حجم النافذة يعتمد على طول وعرض جوانب الشاشة المرئية بما يَسمَح ببعض المسافات الإضافية حتى يكون من الممكن وضع النوافذ فوق بعضها، مع إزاحة كل واحدة عن سابقتها قليلًا. قيِّد العرض ليكون على الأكثر 1.6 مرة من الطول لأسباب جمالية. [3]: تعني افتح النافذة الأولى لتَعرِض الصفحة الأمامية للكتاب. عندما يَفتح التابع newBrowserWindow() نافذةً جديدةً، سيعتمد حجمها ومكانها على قيم المتغيرات windowWidth و windowHeight و locationX و locationY؛ ولهذا ينبغي أن نُعدِّل قيم المتغيرين locationX و locationY لكي تَظهَر النافذة التالية بمكانٍ مختلف؛ وعلينا أيضًا أن نضيف النافذة الجديدة إلى قائمة النوافذ المفتوحة؛ كما ينبغي أن نتأكَّد من حذف النافذة من القائمة عند غلقها. لحسن الحظ، تُولِّد أي نافذة حدثًا عند غلقها، وبالتالي يُمكِننا أن نُضيف مستمعًا إلى ذلك الحدث، ليَحذِف معالج الحدث النافذة من قائمة النوافذ المفتوحة. ألقِ نظرةً على شيفرة التابع newBrowserWindow(): void newBrowserWindow(String url) { BrowserWindow window = new BrowserWindow(this,url); openWindows.add(window); // Add new window to open window list. window.setOnHidden( e -> { // 1 openWindows.remove( window ); System.out.println("Number of open windows is " + openWindows.size()); if (openWindows.size() == 0) { // 2 System.out.println("Program ends because all windows are closed"); } }); if (url == null) { window.setTitle("Untitled " + getNextUntitledCount()); } window.setX(locationX); // set location and size of the window window.setY(locationY); window.setWidth(windowWidth); window.setHeight(windowHeight); window.show(); locationX += 30; // set up location for NEXT window locationY += 20; if (locationX + windowWidth + 10 > screenRect.getMaxX()) { // 3 locationX = screenRect.getMinX() + 30; } if (locationY + windowHeight + 10 > screenRect.getMaxY()) { // 4 locationY = screenRect.getMinY() + 20; } } وتعني كل من: [1]: يُستدعَى عند غلق النافذة، وذلك ليحذف النافذة من قائمة النوافذ المفتوحة. [2]: ينتهي البرنامج أوتوماتيكيًا عند غلق جميع النوافذ. [3]: إذا كانت النافذة ستمتد إلى ما بعد طرف الشاشة الأيمن، فأعِد ضبط المتغير locationX إلى قيمته الأصلية. [4]: إذا كانت النافذة ستمتد إلى ما بعد طرف الشاشة السفلي، فأعِد ضبط المتغير locationY إلى قيمته الأصلية. يتضمَّن الصنف WebBrowser التابع getOpenWindowList()، والذي يعيد قائمةً بكل النوافذ المفتوحة؛ إذ يستدعِي كائن الصنف BrowserWindow ذلك التابع أثناء إنشائه للقائمة "Window". وفي الواقع، لا يحدث ذلك بأفضل كفاءةٍ ممكنة، ويُعاد بناء القائمة بكل مرة تُعرَض خلالها؛ إذ تُولِّد القائمة حدثًا عندما ينقُر المُستخدِم على اسم القائمة، وأيضًا قبل ظهورها مباشرةً. يُسجِّل الصنف BrowserWindow مستمعًا إلى ذلك الحدث، ويسترجِع معالج هذا الحدث قائمة النوافذ المفتوحة باستدعاء التابع owner.getOpenWindowList()، ويَستخدِمها لإعادة بناء القائمة قبل أن يُظهِرها على الشاشة. ألقِ نظرةً على شيفرة التابع المُعرَّف بالصنف BrowserWindow: private void populateWindowMenu() { ArrayList<BrowserWindow> windows = owner.getOpenWindowList(); while (windowMenu.getItems().size() > 4) { // 1 windowMenu.getItems().remove(windowMenu.getItems().size() - 1); } if (windows.size() > 1) { // 2 MenuItem item = new MenuItem("Close All and Exit"); item.setOnAction( e -> Platform.exit() ); windowMenu.getItems().add(item); windowMenu.getItems().add( new SeparatorMenuItem() ); } for (BrowserWindow window : windows) { String title = window.getTitle(); // Menu item text is the window title. if (title.length() > 60) { // 3 title = title.substring(0,57) + ". . ."; } MenuItem item = new MenuItem(title); final BrowserWindow win = window; // (for use in a lambda expression) // 4 item.setOnAction( e -> win.requestFocus() ); windowMenu.getItems().add(item); if (window == this) { // 5 item.setDisable(true); } } } حيث تعني كل من: [1]: تتكون القائمة من 4 عناصر دائمة. اِحذِف العناصر الأخرى المقابلة للنوافذ المفتوحة التي تُركت من آخر مرة عُرِضَت خلالها القائمة. [2]: أضف الأمر "Close All" فقط إذا لم تكن تلك هي النافذة الوحيدة. [3]: لا تَستخدِم نصوصًا طويلةً جدًا لعناصر القائمة. [4]: سيُحضِر معالج الحدث لعنصر القائمة ذاك النافذة المقابلة إلى المقدمة باستدعاء التابع requestFocus() الخاص بها. [5]: نظرًا لأن النافذة موجودة بالمقدمة فعليًا، فعطِّل العنصر المقابل لتلك النافذة. كما ترى، ليس من الصعب إدارة تطبيق متعدد النوافذ، كما أنه من السهل كتابة متصفح إنترنت بوظائف معقولة نوعًا ما باستخدام مكتبة JavaFX. كان هذا مثالًا جيدًا على استخدام أصناف موجودة مثل قاعدة نبني عليها أصنافًا أخرى. رأينا أيضًا أمثلةً جديدةً جيدةً للتعامل مع الأحداث، وبذلك نكون قد وصلنا تقريبًا إلى نهاية هذه السلسلة. سنناقش في المقال الأخير بعض الأشياء المتعلقة ببرمجة واجهات المُستخدِم الرسومية. ترجمة -بتصرّف- للقسم Section 4: Mostly Windows and Dialogs من فصل Chapter 13: GUI Programming Continued من كتاب Introduction to Programming Using Java. اقرأ أيضًا المقال السابق: مكونات الواجهة المركبة ونمط MVC في جافا واجهة المستخدم الحديثة في جافا مدخل إلى التعامل مع الملفات في جافا1 نقطة