لوحة المتصدرين
المحتوى الأكثر حصولًا على سمعة جيدة
المحتوى الأعلى تقييمًا في 11/15/22 في كل الموقع
-
مرحبا، كيف يمكنني حذف نص متكرر في txt بالبايثون ؟ مثال : hello12 hasob academy hello12 اريد حذف hello12 المكرره ع سبيل المثال وشكرا2 نقاط
-
Dic = {'Name':'اسم'} #هذا هو القاموس الذي اقصده كيف اجعل المستخدم يستطيع التعديل على قاموس ويتم حفظه بعد التعديل؟ ملحوظه: اللغه المستخدمه لغة بايثون1 نقطة
-
أنا أسف أنا أقصد بعد تشغيل الكود اريد ان اجعل المستخدم يستطيع التعديل على القاموس كيفما يريد ويبقى التعديل موجودا حتي بعد إيقاف البرنامج واعادة تشغيله1 نقطة
-
لازال السؤال غير واضح. إذا كنت تقصد تغيير قيمة من قيم القاموس باستخدام المفتاح فيمكنك عمل: Dic["Name"] = "القيمة الجديد" أما اذا كنت تقصد شيئا آخر فعليك بالتوضيح أكثر. يمكنك التعرف أكثر على القواميس في بايثون من خلال مقالات موجودة في أكاديمية حسوب. أو من خلال زيارة موسوعة حسوب. بالتوفيق1 نقطة
-
1 نقطة
-
في هذا الكود : //حفظ الملاحظة const saveNoteHandler = async () => { const type = "ايداع"; const body ={ id : new Date(), amount : amount, notice : notice, data : data, type : type } const allDa = [...notes , body] try{ const da = JSON.stringify(allDa) await AsyncStorage.setItem('note',da) setNotes(allDa) // //getData(); //console.log(allDa); setAmount('') setNotice('') setData('') }catch(e){ console.log(e); } } لاأريد أن أعطيك الحل بشكل مباشر , أريدك أن تفكر في الحل أن تقوم بحفظ ملاحظة جديدة الى الملاحظات الجديدة , لايوجد في مشكلة في ذلك ولكن المشكلة مصفوفة الملاحظات فارغة انظر : const [notes, setNotes] = useState([]); يجب عليك إضافة الملاحظات الى المصفوفة القديمة أنت في هذه الحالة تقوم بإضافة الملاحظات الى المصفوفة الجديدة واستبدالها بالمصفوفة القديمة ماهو الحل ؟ الحل بشكل بسيط , يجب عليك أستيراد المصفوفة كما تستوردها في شاشة العرض ثم تضعها في notes اي تستورد المصفوفة القديمة في كل مرة وتضعها داخل notes وفي هذه الحالة ستقوم بإضافة الملاحظات الجديدة بجانب القديمة1 نقطة
-
حسنا هاذول واحد من شاشات الادخال وشاشه العرض InsertInputMo.js Selected.js1 نقطة
-
1 نقطة
-
نعم , هكذا ستعيد كافة الملاحظات القديمة وأيضا الملاحظة الجديدة دون حذف أي شيء1 نقطة
-
هل قصد هاكذا const allDa = [...notes , body] await AsyncStorage.setItem('note',allDa)1 نقطة
-
السلام عليكم اريد تسجيل دخول شخصين ب ارقام سرية مختلفه if isset post هناك خلل وهو عندما اريد ادخال الاسم الثاني ورقمه السري, يربط الرقم السري للمستخدم الاول تحت محاولتي if(isset($_POST["ex5"])) { $user = $_POST["user"]; $password = $_POST["password"]; if ($user == "Steve" || $password == "student"){ echo "You logged IN"; } elseif ($password != "student" ){ echo "you password is INcorrect!, Try again"; } elseif ($user == "Steve"){ echo "you username is INcorrect!, Try again"; } elseif ($user == "Jane" || $password == "doe"){ echo "You logged IN"; } elseif ($user != "Jane"){ echo "you username is INcorrect!, Try again"; }elseif($password != "doe"){ echo "you password is INcorrect!, Try again"; }else { echo "FAILED"; } }1 نقطة
-
نعم لقد فهمت لك لاكن اذا تفظلت اذا يوجد مثال لذالك لكي اعرف اكثر وشكرا جزيلا لك1 نقطة
-
1 نقطة
-
سؤالك غير واضح , سأحاول إعطائك بعض الحالات التي قد تحدث معك: إذا كنت تقصد أنك عند إضافة معلومات جديدة لاتبقى القديمة في المصفوفة , فالجواب هنا: صحيح لأنك عند إضافة معلومات جديدة فأنت فعليا تقوم باسبتدال المعلومات السابقة أما إذا كنت تقصد أنه يقوم بمسح البيانات القديمة بوحده فربما لتشغيله عن طريق expo أما إن كنت تقصد غير شيء فأخبرني1 نقطة
-
أرى انك تتطور في البرنامج الخاص بك وهذا جيد جدا لأنك تحاول بنفسك,أما عن هذه المشكلة , فأنك تقوم بحفظ المدخلات بنفس الاسم 'note' حاول تغيير الاسم لواحدة من شاشات العرض وقم بعرضهم في الشاشة الثالثة أو إذا كنت تستطيع تصوير الكود فسيكون أفضل1 نقطة
-
في React Native أو React عموما فعند إستعمال دالة ال map فيجب عليك استعمال key لكي يتم تعريف كل مكون يعود من الmap برقم مثلا: const listItems = numbers.map((number, index) => <div key={index}> <li>{number}</li> </div> ); أولا أقوم بإضافة index داخل دالة ال map ثم إعطائك الأب نفس القيمة كما في المثال السابق نقوم بهذه العملية عند استعمال map1 نقطة
-
1 نقطة
-
فالبداية دعنا نعرف ال SQL بما أنك انتهت من أساسيات SQL في هذا يعني أنك تعرف كفية عما الآتي: أنواع أوامر SQL جملة إنشاء قاعدة البيانات جملة إنشاء جدول جملة تعديل الجدول كيفية إضافة وحذف وتعديل العمود إنشاء جداول وتعديلها هذه فقط بعض الأساسيات التي يجب تعلمها لكي تقول أنك تعلت أساسيات لغة SQL. ولكي تحترف لغة SQL هناك العديد من المصادر أرشح لك سلسلة مقالات حسوب التي بعنوان سلسلة SQL للمحترفين. ومن مقالات هذه السلسة المتقدمة هي مقالة إدارة الصلاحيات، واستخدام ملفات XML في الاستعلامات وأكثر. ولكي تصبح أكثر احترافا اقترح عليك أن تتابع مقالات حسوب التي تتحدث عن مفاهم متقدمة جدا في لغة SQL مقالات حسوب عن لغة SQL. وبالتأكيد لن ننسى موسوعة حسوب التي قامت بترجمة توثيق SQL كامل لن أبالغ إذا قلت أن هذا المصدر من أفضل المصادر العربية في احترف لغة SQL (إذا لم يكن الأفضل) ولكن لكي تسطيع الدراسة من هذا المصدر يجب أن تكون متمكناً من أساسيات لغة SQL كما ذكرنا في السابق. ومن أفضل الطرق لكي تحترف أي لغة برمجية ليس فقط لغة SQL هي طريقة التطبيق علي مشاريع تجريبة او حتي مشاريع أنت تحتاجها في حياتك اليومية, يمكنك البدء في التطبيق من هذه المقالة تعلم لغة الاستعلام SQL بالأمثلة العملي. ويوجد كتب جيدة في هذ المجال أقترح عليك منها كتاب تصميم قواعد البيانات وهذه نبذة مختصرة عن الكتاب وإذا أردت التعمق في عالم قوعد البيانات يمكنك قراءة هذا الكتاب الذي يتكلم عن قواعد بيانات Postgres الدليل العملي إلى قواعد بيانات PostgreSQL, هذة نبذة مختصرة عن الكتاب وإذا واجهتك أي مشاكل في رحلة تعلمك لا تتردد في طرح الأسئلة.1 نقطة
-
1 نقطة
-
1 نقطة
-
سأشرح لك الفكرة العامة و من فضلك قم بمحاولة تطبيقها و مشاركة النتائج هنا في حال حدوث خطأ لمساعدتك، إعطاءك الكود مباشرة كما سبق و قلت لك لن يفيدك بشيء. إن تعليمة switch يمكنك معرفة كيف تكتب ببحث صغير على غوغل أو بالرجوع لمقالات حسوب. قبل تعليمة switch علينا أخذ الرقم من المستخدم، ابحث عن تعليمة الإدخال في حال لم تكن تعرف كيفية كتابتها. بعد ذلك قم بإدخال حرف من المستخدم، بنفس الطريقة السابقة و لكن هنا حرف بدل رقم. يمكنك الآن كتابة تعليمة switch للحالات المطلوبة، يمكن اختبار فيما إذا كان عدد زوجي أو فردي باختبار باقي قسمة العدد على 2، فإذا كانت صفر يكون زوجي و إذا كانت 1 يكون فردي. الحالات البقية سهلة فهي مجرد عملية ضرب. يمكنك طباعة النتيجة باستعمال تعليمة الطباعة، يمكنك البحث عن كيفية كتابتها أيضاً كما سبق و ذكرت. قم بمحاولة تطبيق ما شرحته لك و شارك الكود معنا في حال حدوث أخطاء حتى نساعدك في حلها.1 نقطة
-
1 نقطة
-
سأشرح لك الطريقة الرياضية و بما أنك استطعت رسم الدائرة الكبيرة ستستطيعين رسم الدوائر الصغيرة بعد الشرح. سأشرح على دائرة صغيرة واحدة حالياً و من ثم سأشرح كيفية جعل الأمر على كل الدوائر. كما نلاحظ فإن القطر الخاص بالدائرة الصغيرة هو نصف القطر الخاص بالدائرة الكبيرة، و بالتالي منتصف نصف القطر هو مركز الدائرة الصغيرة، و نصف قطرها هو نصف نصف قطر الدائرة الكبيرة. بوجود المعلومات الخاصة بالمركز و نصف القطر يمكننا رسم الدائرة الصغيرة. لقد شرحت الخطوات من أجل دائرة واحدة، كيفية القيام بذلك من أجل الكثير من الدوائر يمكن القيام به عن طريق اختيار زاوية، أي الزاوية مثلاً بين نصف القطر المختار و المحور y أو x لا فرق، و من أجل كل زاوية سيكون لدينا نصف قطر و بالتالي دائرة صغيرة.1 نقطة
-
انا حاولت كثيرا بس ما قدرت احلها فياريت تساعدني على حلها حاولت كثيرا بس ما قدرت احلها فياريت تساعدني على حلها1 نقطة
-
من فضلك قم بمحاولة حل التمرين لوحدك، في حال حدوث أخطاء معك يمكننا مساعدتك، و لكن إعطاءك الحل مباشرة لن يساعدك على تطوير مهاراتك البرمجية و مهارات حل المشكلات لديك.1 نقطة
-
في حال كان لا يهمك ترتيب الأسطر في هذا الملف، فهناك طريقة سريعة جداً، و هي أسرع ما يمكن القيام به. نقوم في البداية بجلب كافة الأسطر و وضعها في list و نقوم بترتيب ال list. ثم يمكننا المرور على list و مقارنة كل عنصر بالذي يليه، في حال كان لا يساويه نقوم بإضافته إلى list جديدة ستكون هي الإجابة النهائية، ثم يمكن كتابة هذه ال list الجديدة في الملف. هذه الطريقة هي الأفضل في حال كان لديك عدد أسطر كبير، و لكن في حال كان عدد الأسطر صغير لا فرق بين الطرق بشكل عام.1 نقطة
-
قمنا بعمل بعض الأخطاء في الشيفرة السابقة يجب علينا إصلاحها. في ملف القالب قمنا بإضافة: {% if form.errors %} <div class="alert alert-danger"> {{ form.errors }} </div> {% endif %} والخطأ يكمن في اسم المتغير form والذي يجب أن يكون form_hospital {% if form_hospital.errors %} <div class="alert alert-danger"> {{ form_hospital.errors }} </div> {% endif %} أما سبب ظهور الأخطاء في صفحة الأدمن فهذا راجع لشمولية عمل الرسائل في دجانغو فعند إضافتنا ل messages.error(request, ('Error saving form.')) دجانغو سوف يقوم بحفظ هذه الرسالة واظهارها للعميل عند توفر شيفرة العرض الخاصة بالرسائل وبطبيعة الحال لم نقم بإضافة هذه الشيفرة في القالب الخاص بنا، لإضافتها نقوم ب: {% if messages %} <ul class="messages"> {% for message in messages %} <li {% if message.tags %} class=" {{ message.tags }} " {% endif %}> {{ message }} </li> {% endfor %} </ul> {% endif %} شيفرة القالب تصبح على الشكل: ##html <div class="container"> {% if messages %} <ul class="messages"> {% for message in messages %} <li {% if message.tags %} class=" {{ message.tags }} " {% endif %}> {{ message }} </li> {% endfor %} </ul> {% endif %} <!-- في حالة الخطأ سوف تظهر الأخطاء --> {% if form_hospital.errors %} <div class="alert alert-danger"> {{ form_hospital.errors }} </div> {% endif %} <form method="post" enctype="multipart/form-data"> {% csrf_token %} {{form_hospital}} <button class="btn btn-primary my-4" type="submit">Submit</button> </form> </div> بالتوفيق.1 نقطة
-
1 نقطة
-
أنا سعيد لأنك استطعت حل المشكلة السابقة قبل أن أجيبك على سؤالك في المرة الثانية الآن بالنسبة لعكس المكونات أو الترتيب العكسي تستطيع فعل التالي: myArray.slice(0).reverse().map(function(... هذا الكود سوف يعكس المصفوفة الجديدة , فتستطيع تطبيقها على مصفوفتك1 نقطة
-
إنك تقوم بتحديد الأحرف 14 و 15 و هما الفراغ و w، فلذلك لا يظهر لك شيء. لاحظ الفراغ يظهر في الطباعة، حيث أنه عند التدقيق فال w مزاحة بمقدار فراغ، قارن بينها و بين ال undefined التي طبعت فوقها و ستلاحظ ذلك، أيضاً وضوحاً أي حرفين لن يكونا متساويين في القيمة مع حرف واحد، أنت تقوم باختيار حرفين و تقارنهما مع حرف واحد فدائماً النتيجة false.1 نقطة
-
1 نقطة
-
تم تصميم HTML مع مراعاة القابلية للتوسعة للبيانات التي يجب أن ترتبط بعنصر معين ولكن لا يلزم أن يكون لها أي معنى محدد. تسمح لنا سمات data- * بتخزين معلومات إضافية حول عناصر HTML القياسية والدلالات دون الاختراقات الأخرى مثل السمات غير القياسية أو الخصائص الإضافية على DOM. بناء syntax بسيط. أي سمة على أي عنصر يبدأ اسم السمة ب data- هي سمة بيانات. لنفترض أن لديك مقالًا وتريد تخزين بعض المعلومات الإضافية التي لا تحتوي على أي تمثيل مرئي. فقط استخدم سمات البيانات لذلك: <article id="electric-cars" data-columns="3" data-index-number="12314" data-parent="cars"> … </article> الوصول من javascript قراءة قيم هذه السمات في JavaScript بسيطة للغاية أيضًا. يمكنك استخدام getAttribute () مع اسم HTML الكامل لقراءتها ، لكن المعيار يحدد طريقة أبسط: DOMStringMap يمكنك قراءته عبر خاصية مجموعة البيانات. للحصول على سمة بيانات من خلال كائن مجموعة البيانات ، احصل على الخاصية بجزء من اسم السمة بعد " data- " (لاحظ أنه يتم تحويل الشرطات إلى camelCase). const article = document.querySelector("#electric-cars"); article.dataset.columns; // "3" article.dataset.indexNumber; // "12314" article.dataset.parent; // "cars" تفحص أكثر https://wiki.hsoub.com/CSS/attr https://wiki.hsoub.com/HTML1 نقطة
-
سنتحدث في هذا المقال عن نظام إدارة المحتوى جوملا Joomla، وهو أحد أنظمة إدارة المحتوى أو Content Management System والمعروفة اختصارًا بـ CMS يستخدم لبناء مواقع الويب والتطبيقات عبر الإنترنت، فهو برنامج يساعد في تحرير ونشر وتعديل محتوى موقع الويب الخاص بك، مثل النصوص والصور والموسيقى والمستندات وما إلى ذلك. ماذا ستتعلم أُعِدَّ هذا الدليل التعليمي لأي شخص لديه معرفة أساسية بـ HTML و CSS ولديه رغبة في تطوير مواقع الويب، فسوف يعلمك هذا الدليل أساسيات نظام إدارة المحتوى جوملا Joomla 4 التي يمكنك من خلالها إنشاء مواقع الويب بسهولة، وبعد الانتهاء من هذا الدليل ستجد نفسك على مستوى معتدل من الخبرة في تطوير مواقع الويب باستخدام نظام جوملا Joomla. ما هو نظام جوملا Joomla Joomla أو جوملا هو نظام لإدارة المحتوى يتميز بأنه نظام مجاني مفتوح المصدر قابل للتمديد سهل الاستخدام سريع الاستجابة ومتعدد اللغات، فهو مقسم إلى قوالب الواجهة الأمامية front-end، والقوالب الخلفية (المسؤول) back-end، وطُوِّرت جوملا باستخدام لغة البرمجة PHP، والبرمجة كائنية التوجه OOP، وأنماط تصميم البرامج، ولتخزين البيانات تستخدم MySQL، وأُطلق الإصدار الأخير منه جوملا Joomla 4 في أغسطس 2021 بعد 16 عامًا من الإصدار الأول. فإذا كنت تواجه مشكلة في تصور كيفية عمل جوملا فقد يكون من المفيد لك التفكير في نظام جوملا كنظام تشغيل مثل نظام التشغيل Microsoft Windows، فأهم تشابه بينهما هو أن نظام التشغيل ويندوز يستخدم التطبيقات لتنفيذ بعض المهام المفيدة، وكذلك نظام جوملا يستخدم الإضافات لتنفيذ بعض المهام المفيدة. لذلك هناك الآلاف من إضافات جوملا المجانية والتجارية المدفوعة المتاحة من أطراف ثالثة لجميع أنواع الأغراض الخاصة، ومع ذلك تأتي جوملا مع إضافات أساسية كافية للسماح لك بإنشاء موقع ويب كامل الوظائف، لذلك من الأفضل أن تتعرف على إضافات جوملا الأساسية قبل البحث عن إضافات الطرف الثالث. مكونات نظام جوملا Joomla يتمتع نظام جوملا بمكونات خاصة مدمجة قوية، وهي على النحو التالي: مدير المستخدم: يسمح بإدارة معلومات المستخدم، مثل: الإذن بالتحرير أو الوصول أو النشر أو إنشاء أو حذف المستخدم وتغيير كلمة المرور واللغات، فالمصادقات هي الجزء الرئيسي من مهام مدير المستخدم. مدير المحتوى: يسمح بإدارة المحتوى باستخدام محرر WYSIWYG وهي اختصارًا لجملة "What You See Is What You Get" أو "ما تراه هو ما تحصل عليه"، وهي واجهة مستخدم تسمح للمستخدم بمعالجة تخطيط المستند مباشرةً دون الحاجة إلى أمر تخطيط. مدير اللافتات: يستخدم لإضافة أو تعديل اللافتات على موقع الويب. مدير القوالب: يدير التصاميم المستخدمة على الموقع، فيمكنك تنفيذ القوالب دون تغيير هيكل المحتوى في غضون ثوانٍ قليلة. مدير الوسائط: هي أداة لإدارة ملفات ومجلدات الوسائط إذ يمكنك بسهولة تحميل ملفات الوسائط وتنظيمها وإدارتها في أداة محرر المقالات الخاصة بك. مدير جهات الاتصال: يسمح بإضافة جهات اتصال وإدارة معلومات الاتصال لمستخدمين معينين. مدير روابط الويب: يُوفر مورد الارتباط لمستخدم الموقع ويمكن تصنيفها في فئات. البحث: يتيح للمستخدمين البحث عن المعلومات على موقع الويب، ويمكنك استخدام الفهرسة الذكية وخيارات البحث المتقدم واقتراح عمليات البحث تلقائيًا لجعل بحث جوملا أفضل. مدير القائمة: يسمح بإنشاء القوائم وعناصر القوائم ويمكن إدارتها لاحقًا، كما يمكنك وضع قائمة في أي نمط وفي أماكن متعددة. الخلاصات المبسطة: أو Really Simple Syndication والمعروفة اختصارًا بـ "RSS"، فهي تساعد محتويات موقعك على التحديث تلقائيًا. ماذا ستحتاج يجب تثبيت جوملا على خادم ويب، إما على بيئة استضافة محلية تكون على حاسوبك لأغراض الاختبار الخاصة، أو بيئة استضافة عامة من أحد مزودي خدمات الاستضافة لجعل المحتوى الخاص بك متاحًا لبقية العالم. بيئة الاستضافة المحلية (على جهازك الشخصي) سوف تحتاج إلى تثبيت حزمة WAMP أو XAMPP أو LAMP، نعم يبدو هذا الإجراء شاقًا ولكنه عادةً ما يكون سهلًا جدًا وقد يكون مثبتًا بالفعل مع نظام التشغيل الخاص بك. وسوف تحتاج أيضًا إلى تثبيت phpMyAdmin لإنشاء قاعدة البيانات ومستخدم قاعدة بيانات. بيئة الاستضافة العامة يوفر جميع مقدمي خدمات الاستضافة تقريبًا البيئة اللازمة لتشغيل نظام جوملا، ومع ذلك يمكنك اختيار الباقة الأرخص مع إعدادات "php.ini" شديدة التقييد، لذا من الأفضل لك التحقق من الإعدادات الموصى بها في هذا الدليل التعليمي في قسم "تثبيت جوملا Joomla" ومقارنته مع ما تقدمه خدمة الاستضافة المقترحة. نسخة مثبتة من نظام جوملا سوف تحتاج إلى تنزيل أحدث إصدار من نظام جوملا على جهاز الحاسوب المحلي الخاص بك من موقع Joomla. وبعد التنزيل انقله أو انسخه إلى جذر موقع الويب الخاص بك في مجلد جديد، ثم استخرج الملفات المضغوطة وامنح المجلد اسمًا قصيرًا، ثم ابدأ في تثبيت نظام جوملا. وتتيح لك عملية التثبيت باستخدام بيانات بسيطة وقليلة استكشاف موقع جوملا قبل الشروع في إنشاء موقع لمحتواك. تنويه: هذه الخطوات سوف نتناولها بمزيد من التفاصيل في هذا الدليل التعليمي تحت عنوان "إعداد الاستضافة المحلية" و "إعداد الاستضافة العامة" و "تثبيت جوملا Joomla" بالدروس القادمة. البدائل استخدم التثبيت التجريبي: يقدم العديد من مزودي خدمات الاستضافة أو مواقع مثبت البرامج النصية إصدارات "تجريبية" لتثبيت نظام جوملا، نعم ستتمكن على الأقل من إجراء تغييرات على النظام لكنها لن تكون دائمة، وذلك بسبب إعادة تعيين "عمليات التثبيت التجريبية" إلى حالة التثبيت "الجديدة" على فترات منتظمة. التثبيت بنقرة واحدة: يقدم العديد من مزودي خدمات الاستضافة تثبيتًا بنقرة واحدة لبرامج مواقع الويب الشائعة. الميزات الجديدة في نظام جوملا Joomla 4 مع نظام جوملا يمكنك إنشاء العديد من أنواع مواقع الويب، مثل: مواقع الويب القائمة على المجتمع أو التجارة الإلكترونية أو مواقع التعلم الإلكتروني، فيمكن لجوملا التكيف مع هيكل أو غرض موقع الويب الذي تفكر فيه، فنظام جوملا يمتلك مجموعة كبيرة من المميزات لمساعدتك في تحقيق نتائج مذهلة، ومن أهم هذه المميزات ما يلي: المرونة: نظام يسهل تخصيصه، يتوفر له أكثر من 6500 إضافة أُجريت لهم عملية تحقق، وقوالب عالية الجودة والعديد منها مجاني. جدولة المهام: هل لديك مهام تقوم بها مرارًا وتكرارًا؟ أو مهام مستقبلية لا تريد نسيان تنفيذها؟ يمكنك الآن جعلها تُنفذ تلقائيًا بسهولة من خلال استخدام الأداة الجديدة "جدولة المهام Task Scheduler" التي جرى تضمينها في الإصدار 4.1 من نظام جوملا . تحسين محركات البحث SEO: تمتع بالوصول إلى الصفحات الأولى من أي محرك بحث سريعًا باستخدام جوملا 4، فهو يعتني بالـ SEO حتى تتمكن من التركيز على جذب الانتباه إلى المحتوى الرائع الخاص بك، ويعني تحسين محركات البحث المدمج في الصفحة أنك تحصل على بنية الصفحة الصحيحة دون استخدام أي إضافات من طرف ثالث. التصميم: تسرع منطقة الإدارة المعاد تصميمها عملية إنشاء المحتوى، ويمنحك مدير الوسائط المحسن وتحديثات المحرر القدرة على تصميم وإنشاء مواقع ويب مذهلة، كما تساعدك قوالب المقالات أنت وزملائك في الحفاظ على تصميم الصفحة. البحث: اسمح للمستخدمين بالعثور على المحتوى الخاص بك بسرعة وكفاءة، فبحث نظام جوملا متقدم وقابل للتكوين، باستخدام المفهرس النشط فإنه يضيف المحتوى على الوجه الذي أُنشئ به، لذلك لا تحتاج إلى أكثر من التركيز على إنشاء المزيد من المحتوى المذهل. البريد الإلكتروني: تعمل قوالب البريد الإلكتروني القابلة للتخصيص على تسريع عملية نشر الموقع وتخصيصه، فقط عدِّل رسائل البريد الإلكتروني الخاصة بالموقع لتناسب علامتك التجارية، واستخدم النمط المميز لموقعك للتواصل مع المستخدمين عند إرسال رسائل البريد الإلكتروني للموقع. سير العمل: باستخدام مهام سير العمل يمكنك إنشاء أي عدد من العمليات لنقل إنشاء المحتوى الخاص بك من فكرة إلى منشور مصقول بطريقة محددة ومنضبطة، وتعمل المكونات الإضافية لسير العمل على إنشاء بيئة جديدة قوية تعمل على تسريع إنشاء المحتوى. السرعة: مع الإصدار الرابع من نظام جوملا استفد من زيادة الأداء، فالسرعة هي أحد أهم العوامل في تحويل النقرات إلى مشاهدات سواء كان موقعك تجارة إلكترونية أو تحتاج إلى زيادة اشتراكاتك، فالإصدار الرابع من نظام جوملا يجعل موقعك سريعًا. سهولة الوصول: يقدم الإصدار الرابع من نظام جوملا أفضل إمكانية وصول في فئتها مما يفيد جميع زوار موقعك، ويرجع ذلك إلى احتواء كلٍّ من التصميم والتباين والبنية التحتية على إمكانية وصول مدمجة. الحماية والأمان: يساعدك تشغيل موقع ويب به شيفرة برمجية حديثة ومكتوبة جيدًا على البقاء آمنًا، فالإصدار الرابع من نظام جوملا يحتوي على العديد من التغييرات المصممة لزيادة الأمان وإبعاد المتسللين. وإلى هنا نكون قد وصلنا إلى نهاية هذا الدرس من هذا الدليل، وفي الدرس القادم سوف نشرح خطوات إعداد بيئة الاستضافة المحلية. نتمنى أن يكون هذا الدليل قد أضاف لكم معلومات جديدة ومفيدة، وفي حالة وجود أي استفسارات لا تترددوا في ذكرها لنا في التعليقات. اقرأ أيضًا 10 معايير لاختيار نظام إدارة المحتوى CMS مميزات نظام إدارة المحتوى ووردبريس اعتماد نظام إدارة المحتوى أو القيام ببرمجة خاصة؟ مُساعدك لاتخاذ القرار1 نقطة
-
يمكنك إستعمال المواقع التي تقوم بإزالة الخلفية بشكل تلقائي عبر الذكاء الإصطناعي، هنا بعض هذه المواقع: remove.bg removal.ai pixlr remove background retoucher.online ويمكنك الوصول لمواقع أكثر من خلال البحث في جوجل على "Remove Background from Images".1 نقطة
-
تضيف الخيوط threads مستوًى جديدًا من التعقيد إلى البرمجة، ولكنها مهمةٌ وستصبح أساسيةً بالمستقبل، ولذلك لا بُدّ أن يَطلِّع كل مبرمجٍ على بعض أنماط التصميم design pattern الأساسية المُستخدَمة مع الخيوط، حيث سنفحص بهذا المقال بعض التقنيات البسيطة وسنبني عليها بالأقسام التالية. الخيوط والمؤقتات ومكتبة جافا إف إكس يُمكِننا استخدام الخيوط لتنفيذ مهمةٍ معينةٍ تنفيذًا دوريًا، وهو ما يُعدّ أمرًا بسيطًا لدرجة وجود أصنافٍ مُتخصِّصة لتنفيذ تلك المهمة، ولقد تعاملنا مع إحداها بالفعل، وهو الصنف AnimationTimer المُعرَّف بحزمة javafx.animation، التي درسناها بالقسم الفرعي الصنف AnimationTimer من المقال تعرف على أهم الأحداث والتعامل معها في مكتبة جافا إف إكس JavaFX حيث يستدعِي ذلك الصنف تابعه handle() دوريًا بمعدل 60 مرةٍ لكل ثانية. في الواقع، كان اِستخدَام الخيوط ضروريًا لتنفيذ العمليات المشابهة قبل أن تتوفَّر المؤقتات. لنفترض أننا نريد فعل شيءٍ مشابه باستخدام خيط، كأن نَستدعِي برنامجًا فرعيًا subroutine على فتراتٍ دورية، مثل 30 مرةٍ لكل ثانية. سيُنفِّذ تابع الخيط run() حلقة تكرار loop، سيتوقَّف خلالها الخيط لمدة "30 ميللي ثانية"، ثم سيستدعِي بعدها البرنامج الفرعي. تُنفِّذ الشيفرة التالية ذلك باستخدِام Thread.sleep() -الذي ناقشناه بقسم العمليات على الخيوط من المقال السابق مقدمة إلى الخيوط Threads في جافا- ضمن صنفٍ متداخل nested: private class Animator extends Thread { public void run() { while (true) { try { Thread.sleep(30); } catch (InterruptedException e) { } callSubroutine(); } } } سنُنشِئ الآن كائنًا ينتمي إلى ذلك الصنف، ونَستدعِي تابعه start()، مع الملاحظة بأنه لن يكون هناك أي طريقةٍ لإيقاف الخيط بعد تشغيله؛ وإنما يُمكِننا إيقاف حلقة التكرار عندما تساوي قيمة متغيرٍ متطايرٍ volatile منطقي معين وليكن اسمه هو terminate القيمة true كما ناقشنا بقسم المتغيرات المتطايرة من المقال السابق. إذا أردنا تشغيل التحريكة animation مرةً أخرى بعد إيقافها، فسنضطّر لإنشاء كائنٍ جديدٍ من النوع Thread؛ نظرًا لأنه من الممكن تنفيذ تلك الكائنات مرةً واحدةً فقط. سنناقش بالقسم التالي بعض التقنيات المُستخدَمة للتحكم بالخيوط. تختلف الخيوط عن المؤقتات جزئيًا فيما يتعلّق بالتحريكات؛ حيث لا يفعل الخيط الذي يَستخدِمه الصنف AniamtionTimer والمُعرَّف بمكتبة جافا إف إكس أكثر من مجرد استدعاء البرنامج handle() مرةً بعد أخرى، والذي يُنفَّذ ضمن خيط تطبيق جافا إف إكس المسؤول عن إعادة رسم مكوِّنات الواجهة والإستجابة لما يفعله المُستخدِم. يُعدّ ذلك أمرًا مهمًا لأن مكتبة جافا إف إكس ليست آمنةً خيطيًا thread-safe؛ بمعنى أنها لا تَستخدِم المزامنة synchronization لتجنُّب حالات التسابق race conditions الممكن حدوثها بين الخيوط التي تحاول الوصول إلى كلٍ من مكوِّنات واجهة المُستخدِم الرسومية GUI ومتغيرات الحالة الخاصة بها. لا يُمثِّل ذلك مشكلةً بشرط أن يحدث كل شيء ضمن خيط التطبيق. في المقابل، قد تَنشَأ مشكلةٌ إذا حاول خيطٌ آخر تعديل إحدى مُكوِّنات الواجهة أو إحدى المتغيرات المُستخدَمة بخيط واجهة المُستخدِم الرسومية، وعندها قد يكون اِستخدَام المزامنة حلًا مناسبًا مع أن اِستخدَام الصنف AnimationTimer -إن كان ذلك ممكنًا- عادةً ما يكون الحل الأمثل؛ ولكن يُمكِنك استخدام Platform.runLater()، إذا كنت مضطّرًا لاستخدام خيطٍ منفصل. تحتوي حزمة javafx.application على الصنف Platform الذي يتضمَّن التابع الساكن Platform.runLater(r)؛ حيث يَستقبِل هذا التابع كائنًا من النوع Runnable، أي نفس الواجهة interface المُستخدَمة لإنشاء الخيوط على أنه معاملٌ بإمكاننا اِستدعائه من أي خيط. تتلّخص مسؤولية ذلك التابع في تسليم r إلى خيط تطبيق جافا إف إكس لتنفيذه، ثم يعود مباشرةً دون أن ينتظر انتهاء تنفيذ r. بعد ذلك، يَستدعِي خيط التطبيق التابع r.run() بعد أجزاءٍ من الثانية أو حتى على الفور إذا لم يَكُن الحاسوب مُنشغِّلًا بتنفيذ شيءٍ آخر. تُنفَّذ الأصناف المُنفِّذة للواجهة Runnable بنفس ترتيب تَسلُمها، ونظرًا لأنها تُنفَّذ داخل خيط التطبيق، يُمكِنها معالجة واجهة المُستخدِم الرسومية معالجةً آمنةً بدون مزامنة. يُمرَّر معامل التابع Platform.runLater() عادةً مثل تعبير لامدا lambda expression من النوع Runnable. سنَستخدِم Platform.runLater() بعدة أمثلة خلال هذا المقال وما يليه. سنفحص الآن المثال التوضيحي RandomArtWithThreads.java الذي يَستخدِم خيطًا للتحكُّم بتحريكةٍ بسيطةٍ جدًا. لا يفعل الخيط بهذا المثال أكثر من مجرد استدعاء التابع redraw() كل ثانيتين، الذي يُعيد رسم محتويات الحاوية canvas؛ واستخدام التابع Platform.runLater() لتنفيذ redraw() ضمن خيط التطبيق. يستطيع المُستخدِم الضغط على زر لبدء التحريكة وإيقافها. يُنشَأ خيطٌ جديدٌ بكل مرة تبدأ خلالها التحريكة، ويُضبَط متغيرٌ منطقيٌ متطايرٌ اسمه running إلى القيمة false عندما يُوقِف المُستخدِم التحريكة إشارةً للخيط بأن عليه أن يتوقف، كما ناقشنا بالمقال المتغيرات المتطايرة من المقال السابق. يُعرِّف الخيط بواسطة الصنف التالي: private class Runner extends Thread { public void run() { while (running) { Platform.runLater( () -> redraw() ); try { Thread.sleep(2000); // انتظر ثانيتين قبل إعادة رسم الشاشة } catch (InterruptedException e) { } } } } التعاود داخل الخيوط إذا كان الخيط يُنفِّذ خوارزميةً تعاوديةً recursive (ناقشنا التعاود في مقال التعاود recursion في جافا)، وكنت تريد إعادة رسم الواجهة عدة مرات أثناء حدوث التعاود؛ فقد تضطّر لاستخدام خيطٍ منفصلٍ للتحكُّم بالتحريكة. من الصعب تقسيم خوارزمية تعاودية إلى سلسلةٍ من استدعاءات التوابع داخل مؤقت، فمن البديهي أكثر استدعاء تابعٍ تعاودي واحدٍ لإجراء التعاود، وهو أمرٌ سهل إنجازه ضمن خيط. سنفحص المثال التوضيحي QuicksortThreadDemo.java الذي يَرسِم تحريكةً تُوضِح طريقة عمل خوارزمية QuickSort التعاودية لترتيب المصفوفات. ستحتوي المصفوفة في هذا المثال على ألوان، وسيكون الهدف هو ترتيبها وفقًا لسلّم الألوان المعروف من الأحمر إلى البنفسجي. يَسمَح البرنامج للمُستخدِم أيضًا بالنقر على زر "Start" لبدء العملية، وعندها تُرتَّب الألوان ترتيبًا عشوائيًا، ثم تُستدعَى خوارزمية QuickSort لترتيبها وتُعرَض العملية بحركة بطيئة. يتبدَّل زر "Start" أثناء عملية الترتيب إلى "Finish" ليَسمَح للمُستخدِم بإيقاف عملية الترتيب قبل انتهائها. في الواقع، من الممتع مشاهدة خرج هذا البرنامج، ولربما يساعدك حتى على فهم طريقة عمل خوارزمية QuickSort على نحوٍ أفضل، لذلك عليك أن تُجرِّب تشغيله. ينبغي أن تتغيّر الصورة المعروضة بالحاوية في هذا البرنامج بكل مرةٍ تُجرِي خلالها الخوارزمية تعديلًا على المصفوفة. لاحِظ أن المصفوفة تتغيّر بخيط التحريكة بينما لا بُدّ من إجراء التغيير المقابل على الحاوية بخيط تطبيق جافا إف إكس باستخدام Platform.runLater() كما ناقشنا بالأعلى. بكل مرة يُستدعى خلالها Platform.runLater()، يتوقف الخيط لمدة "100 ميللي ثانية" ليَسمَح لخيط التطبيق بتنفيذ المعامل المُمرَّر من النوع Runnable وليتمكَّن المُستخدِم من مشاهدة التعديلات. هناك أيضًا توقُّفٌ أطول بما يصِل إلى ثانيةٍ كاملة بعد ترتيب عناصر المصفوفة عشوائيًا مباشرةً وقبل بدء عملية الترتيب الفعلي. يُعرِّف الصنف QuicksortThreadDemo التابع delay() الذي يجعل الخيط المُستدِعي له يتوقَّف لفترة معينة، نظرًا لأن الشيفرة تتوقَّف بأكثر من مكان. والآن، كيف نُنفِّذ شيفرة الزر "Finish" المسؤولة عن إيقاف عملية الترتيب وإنهاء الخيط؟ في الواقع، يؤدي النقر على هذا الزر إلى ضبط قيمة المتغير المنطقي المتطاير running إلى القيمة false إشارةً للخيط بأنه عليه الانتهاء. تَكْمُن المشكلة في إمكانية النقر عليه بأي لحظة، حتى لو كان البرنامج منهمكًا بتنفيذ الخوارزمية وبمستوًى منخفضٍ جدًا من التعاود. لا بُدّ أن تعود جميع استدعاءات التوابع التعاودية لنتمكّن من إنهاء الخيط، ويُعد التبليغ عن استثناء exception إحدى أبسط الطرق التي يُمكنِها تحقيق ذلك. يُعرِّف الصنف QuickSortThreadDemo صنف استثناءٍ جديد اسمه ThreadTerminationException لهذا الغرض، ويفحص التابع delay() قيمة المُتغيّر running؛ فإذا كانت مساويةً للقيمة false، سيُبلِّغ عن استثناءٍ تسبَّب بإنهاء الخوارزمية التعاودية، وبالتتابع خيط التحريكة ذاته. ألقِ نظرةً على تعريف التابع delay(): private void delay(int millis) { if (! running) throw new ThreadTerminationException(); try { Thread.sleep(millis); } catch (InterruptedException e) { } if (! running) // افحصها مرة أخرى فربما تكون قد تغيرت أثناء توقُّف الخيط throw new ThreadTerminationException(); } يَلتقِط تابع الخيط run() الاستثناء المنتمي للصنف ThreadTerminationException: // 1 private class Runner extends Thread { public void run() { for (int i = 0; i < hue.length; i++) { // املأ المصفوفة باستخدام الفهارس hue[i] = i; } for (int i = hue.length-1; i > 0; i--) { // رتّب المصفوفة عشوائيًا int r = (int)((i+1)*Math.random()); int temp = hue[r]; hue[r] = hue[i]; // 2 setHue(i,temp); } try { delay(1000); // انتظر ثانية قبل بدء عملية الترتيب quickSort(0,hue.length-1); // رتّب المصفوفة بالكامل } catch (ThreadTerminationException e) { // ألغى المُستخدِم عملية الترتيب // 3 Platform.runLater( () -> drawSorted() ); } finally { running = false; // 4 Platform.runLater( () -> startButton.setText("Start") ); } } } حيث أن: [1]: يُعرِّف هذا الصنف خيطًا يُنفِّذ خوارزمية QuickSort التعاودية؛ حيث يبدأ الخيط بخلط عناصر المصفوفة hue عشوائيًا، ثم يَستدعِي التابع quickSort() لترتيبها بالكامل. إذا توقَّف التابع quickSort نتيجة استثناء من النوع ThreadTerminationException -يحدُث إذا نقر المُستخدِم على زر "Finish"-، يُعيد الخيط المصفوفة إلى حالتها المُرتَّبة قبل أن ينتهي؛ وبالتالي سواءٌ ألغى المُستخدِم عملية الترتيب أم لا، تكون المصفوفة مرتبةً بنهاية الخيط. في جميع الحالات، يُضبَط نص الزر إلى "Start" بالنهاية. [2]: التعليمة الأخيرة التي ينبغي إنجازها ضمن الحلقة هي hue = temp. لن تتغير قيمة hue بعد ذلك، ولذلك تُنجز عملية الإسناد باستدعاء التابع setHue(i,temp) الذي سيُبدِّل القيمة الموجودة بالمصفوفة، كما أنه يَستِخدِم Platform.runLater() لتغيير لون الشريط رقم i بالحاوية. [3]: ضع الألوان بصورةٍ مرتبة. يرسم التابع drawSorted() ألوان جميع الشرائط بالترتيب. [4]: تأكَّد من أن running يُساوِي false. يكون ذلك ضروريًا فقط إذا انتهى الخيط طبيعيًا. يَستخدِم البرنامج المتغير runner من النوع Runner لتمثيل الخيط المسؤول عن عملية الترتيب. عندما ينقر المُستخدِم على الزر "Start"، تُنفَّذ الشيفرة التالية لإنشاء الخيط وتشغيله: startButton.setText("Finish"); runner = new Runner(); running = true; // اضبط قيمة الإشارة قبل تشغيل الخيط runner.start(); لا بُدّ من ضبط قيمة متغير الإشارة running إلى القيمة true قبل بدء الخيط؛ لأنه لو كان يحتوي على القيمة false بالفعل عند بدء الخيط، فلربما سيرى الخيط تلك القيمة بمجرد بدءه، ويُفسِّرها على كونها إشارةً للتوقُّف قبل أن يفعل أي شيء. تذكَّر أنه عند استدعاء runner.start()، يبدأ الخيط runner بالعمل على التوازي مع الخيط المُستدعِي له. عندما ينقر المُستخدِم على زر "Finish"، تُضبَط قيمة running إلى القيمة false إشارةً للخيط بأن عليه الانتهاء، ولكن ماذا لو كان الخيط نائمًا في تلك اللحظة؟ في تلك الحالة لا بُدّ أن يستيقظ الخيط أولًا حتى يتمكَّن من الإستجابة لتلك الإشارة؛ أما إذا أردنا أن نجعله يستجيب بصورةٍ أسرع، يُمكِننا استدعاء التابع runner.interrupt() لإيقاظ الخيط إذا كان نائمًا. لا يؤثر هذا على البرنامج من الناحية العملية، ولكنه يجعل استجابة البرنامج أسرع على نحوٍ ملحوظ بالأخص إذا نقر المُستخدِم على زر "Finish" بعد النقر على زر "Start" مباشرةً عندما ينام الخيط لمدة ثانيةٍ كاملة. استخدام الخيوط بالعمليات المنفذة بالخلفية إذا أردنا أن تكون استجابة برامج واجهة المُستخدِم الرسومية GUI سريعة، أي تستجيب للأحداث events بمجرد وقوعها تقريبًا، لا بُدّ أن تُنهِي توابع معالجة الأحداث الموجودة بالبرنامج عملها بسرعة. تُخزَّن الأحداث برتل queue أثناء وقوعها، ولا يستطيع الحاسوب الاستجابة لحدثٍ معين قبل أن تُنهِي توابع معالجة الأحداث السابقة له عملها. يَعنِي ذلك أنه ينبغي للأحداث الانتظار أثناء تنفيذ الحاسوب لمُعالِج حدثٍ معين؛ وإذا استغرق مُعالِج حدثٍ معين فترةً طويلة لتنفيذ عمله، ستَجْمُد freeze واجهة المُستخدِم خلال تلك الفترة، وهو ما يُضايق المُستخدِم بالأخص إذا استمر لأكثر من جزءٍ من الثانية. تستطيع الحواسيب العصرية لحسن الحظ إنجاز الكثير من العمليات الضخمة خلال جزءٍ من الثانية. ومع ذلك، هناك بعض العمليات الضخمة للغاية لدرجة لا يُمكِن تنفيذها بمعالجات الأحداث event handlers (أو بتمرير مُنفَّذات للواجهة Runnable إلى Platform.runlater()). ويكون من الأفضل في تلك الحالة تنفيذ تلك العمليات بخيطٍ آخر منفصل يَعمَل على التوازي مع خيط معالجة الأحداث، وهذا يَسمَح للحاسوب بالاستجابة إلى الأحداث الأخرى في نفس الوقت الذي يُنفَّذ خلاله تلك العملية، ويُقال أن العملية "تُنفَّذ بالخلفية background". يختلف تطبيق الخيوط في هذا المثال عن المثال السابق؛ فعندما يُستخدَم الخيط للتحكُّم بتحريكة، فإنه فعليًا لا يَفعَل سوى القليل، إذ عليه فقط أن يستيقظ كل عدة ثواني ليُجرِي قليلًا من العمليات المتعلّقة بتحديث متغيرات الحالة state variables لإطار التحريكة التالي ومن ثَمّ رَسْمه. يُوفِّر ذلك وقتًا كافيًا لخيط تطبيق جافا إف إكس، ويَسمَح له بإجراء أي إعادة رسمٍ ضرورية لمُكوِّنات واجهة المُستخدِم الرسومية، وكذلك معالجة أي أحداثٍ اخرى. عندما نُنفِّذ عمليةٌ معينة بالخلفية ضمن خيط، فإننا نريد إبقاء الحاسوب مُنشغِلًا بتنفيذ تلك العملية بأقصى ما يمكن، ولكن قد يتسابق هذا الخيط مع خيط التطبيق على زمن المعالجة، وعندها يبقى تعطُّل معالجة الأحداث بالأخص إعادة الرسم ممكنًا إذا لم ننتبه كفاية. يُمكِننا لحسن الحظ استخدام أولويات priorities للخيوط لنتجنَّب تلك المشكلة، حيث يُمكِننا ضبط الخيط المسؤول عن تنفيذ العملية بالخلفية بحيث يَعمَل بأولويةٍ أقل من أولوية خيط معالجة الأحداث، وسنضمَن بذلك معالجة الأحداث بأسرع ما يمكن، وسيَحظَى بنفس الوقت الخيط الآخر بأي زمن معالجةٍ إضافي. تستغرق معالجة الأحداث وقتًا قصيرًا للغاية عمومًا، وبالتالي سيُستغَل غالبية زمن المعالجة بتنفيذ العملية المُشغَّلة بالخلفية بنفس الوقت الذي ستستجيب فيه الواجهة بسرعة. ناقشنا أولوية الخيوط في قسم العمليات على الخيوط من المقال السابق. يُعدّ البرنامج BackgroundComputationDemo.java مثالًا توضيحيًا على معالجة العمليات بالخلفية. يُنشِئ هذا البرنامج صورةً يستغرِق تحديد ألوان بكسلاتها وقتًا طويلًا نوعًا ما. تمثّل تلك الصورة قطعةً من شكلٍ هندسيٍ معروف باسم "مجموعة ماندلبرو Mandelbrot set"، وسنستخدِم تلك الصورة بعدة أمثلة خلال هذا المقال. يتشابه البرنامج "BackgroundComputationDemo" مع برنامج "QuicksortThreadDemo" الذي ناقشناه بالأعلى، حيث تُجرَى العمليات ضمن خيطٍ مُعرَّفٍ بواسطة صنفٍ مُتداخِل اسمه Runner، وسنَستخدِم متغيرًا متاطيرًا اسمه running للتحكُّم بالخيط؛ فإذا كان المُتغيّر يساوي false، ينبغي أن ينتهي الخيط. يُوفِّر البرنامج زرًا لبدء العملية وإنهائها. بخلاف البرنامج السابق، سيَعمَل الخيط باستمرار دون أن ينام. بعد انتهاء الخيط من حساب كل صف من البسكلات، سيَستدعِي التابع Platform.runLater() لينسخ تلك البكسلات إلى الصورة المعروضة على الشاشة، وسيتمكَّن بذلك المُستخدِم من مشاهدة التحديثات الناتجة عن العمليات المُجرَاة، وسيشاهد الصورة أثناء تكوُّنها صفًا بعد آخر. عندما ينقر المُستخدِم على زر "Start"، يُنشَأ الخيط المسؤول عن عملية المعالجة، والذي لا بُدّ من ضبطه ليَعمَل بأولويةٍ أقل من أولوية خيط تطبيق جافا إف إكس. نظرًا لوقوع الشيفرة المسؤولة عن إنشاء ذلك الخيط ضمن خيط التطبيق، يُمكِننا ضبط أولوية الخيط المُنشَا بحيث تكون أقل بمقدار الواحد من أولوية الخيط المُشغَّل. تذكَّر أنه من الضروري ضبط تلك الأولوية داخل تعليمة try..catch؛ فإذا حدث خطأ أثناء ذلك، نَضمَن استمرار البرنامج، وإن لم يَكُن بنفس السلاسة التي كان سيَعمَل بها لو كانت الأولوية قد ضُبطَت صحيحًا. تُنشِئ الشيفرة التالية الخيط، وتُشغِّله: runner = new Runner(); try { runner.setPriority( Thread.currentThread().getPriority() - 1 ); } catch (Exception e) { System.out.println("Error: Can't set thread priority: " + e); } running = true; // اضبط قيمة الإشارة قبل تشغيل الخيط runner.start(); على الرغم من عمل البرنامج BackgroundComputationDemo جيدًا، إلا أن هناك مشكلةً واحدةً وهي أن هدفنا هو إتمام العملية بأسرع وقتٍ ممكن من خلال استغلال ما هو مُتوفّرٌ من زمن المعالجة. سيتمكَّن البرنامج من إنجاز ذلك الهدف إذا كان مُشغَّلًا على حاسوبٍ بمعالج واحد؛ ولكن إذا كان الحاسوب يحتوي على عدة معالجات، فإننا في الواقع نَستخدِم معالجًا واحدًا فقط لإنجاز العملية، وفي تلك الحالة، لن يكون لأولوية الخيط أي أهمية؛ فمن الممكن تشغيل كُلٍ من خيط التطبيق وخيط التحريكة على التوازي باستخدام معالجين مختلفين. سيكون من الأفضل لو تمكَّنا من استخدام جميع تلك المعالجات لإتمام العملية، وهو ما يتطلَّب معالجةً على التوازي parallel processing من خلال عدة خيوط. سننتقل إلى تلك المشكلة فيما يلي. استخدام الخيوط في المعالجة المتعددة سنفحص الآن البرنامج MultiprocessingDemo1.java، الذي يختلف قليلًا عن البرنامج "BackgroundComputationDemo"؛ فبدلًا من إجراء المعالجة بخيطٍ واحد فقط، سيُقسِّم البرنامج "MultiprocessingDemo1" المعالجة على عدة خيوط. وسيَسمَح البرنامج للمُستخدِم بتخصيص عدد الخيوط المطلوب تشغيلها، وسيتولى كل خيطٍ منها المعالجة المطلوبة لجزءٍ معين من الصورة. ينبغي أن تنجز الخيوط عملها على التوازي؛ فإذا استخدمنا خيطين على سبيل المثال، فسيَحسِب الأول النصف الأعلى من الصورة؛ بينما سيَحسِب الثاني النصف السفلي. تعرض الصورة التوضيحية التالية شاشة البرنامج مع اقتراب نهاية المعالجة عند استخدام ثلاثة خيوط، حيث تُشير المساحات الرمادية إلى أجزاء الصورة غير المُعالجة بعد. عليك أن تُجرِّب البرنامج؛ فعند استخدام عدة خيوطٍ بحاسوبٍ متعدّد المعالجات، ستكتمل المعالجة على نحوٍ أسرع بالموازنة مع استخدام خيطٍ واحد. لا تُعدّ الطريقة المُستخدَمة لتقسيم المشكلة على مجموعة الخيوط بهذا المثال الطريقة الأمثل، وسنتناول بالقسم التالي إمكانية تحسين تلك الطريقة، ومع ذلك ما يزال البرنامج "MultiprocessingDemo1" مثالًا جيدًا على المعالجة المُتعدّدة. عندما ينقر المُستخدِم على زر "Start"، سيُنشِئ البرنامج عدد الخيوط المُخصَّصة ويُشغِّلها، كما سيُسنِد لكُلٍ منها جزءًا من الصورة. ألقِ نظرةً على الشيفرة التالية: workers = new Runner[threadCount]; // يحمل خيوط المعالجة int rowsPerThread; // عدد الخيوط التي ينبغي أن يحسبها كل خيط rowsPerThread = height / threadCount; // (height = vertical size of image) running = true; // اضبط قيمة الإشارة قبل تشغيل الخيوط threadsCompleted = 0; // Records how many of the threads have terminated. for (int i = 0; i < threadCount; i++) { int startRow; // الصف الأول الذي يحسبه الخيط رقم i int endRow; // 1 startRow = rowsPerThread*i; if (i == threadCount-1) endRow = height-1; else endRow = rowsPerThread*(i+1) - 1; workers[i] = new Runner(startRow, endRow); try { workers[i].setPriority( Thread.currentThread().getPriority() - 1 ); } catch (Exception e) { } workers[i].start(); } [1] الصف الأخير الذي يحسبه الخيط رقم i. انشِئ خيطًا وشغَِله لحساب صفوف الصورة من startRow إلى endRow. لا بُدّ أن تكون قيمة endRow للخيط الأخير مساويةً لرقم الصف الأخير بالصورة. أجرينا عددًا قليلًا من التعديلات لتفعيل المعالجة المتعددة إلى جانب إنشاء عدة خيوط بدلًا من خيط واحد. كما هو الحال في المثال السابق: عندما ينتهي أي خيطٍ من حساب ألوان صفٍ من البسكلات، فإنه يَستدعِي التابع Platform.runLater() ليَنسَخ الصف إلى الصورة. هناك شيءٌ واحدٌ جديد، وهو: عندما تنتهي جميع الخيوط من عملها، سيتبدّل اسم الزر من "Abort" إلى "Start Again"، وسيُعاد تفعيل القائمة التي كانت قد عُطّلَت بينما الخيوط مُشغَّلة. والآن، كيف سنعرف أن جميع الخيوط قد انتهت؟ ربما تتساءل لما لا نَستخدِم join() لننتظر انتهاء الخيوط كما فعلنا بمثالٍ سابق في قسم العمليات على الخيوط من المقال السابق؟ حيث لا يُمكِننا بالتأكيد فعل ذلك بخيط تطبيق جافا إف إكس على الأقل. سنَستخدِم في هذا المثال متغير نسخة instance variable، اسمه threadsRunning لتمثيل عدد خيوط المعالجة قيد التنفيذ؛ وعندما ينتهي كل خيطٍ من عمله، عليه استدعاء تابعٍ لإنقاص قيمة ذلك المتغير بمقدار الواحد، حيث سيُستدعَى ذلك التابع ضمن عبارة finally بتعليمة try لنتأكَّد تمامًا من تنفيذها عند انتهاء الخيط. عندما يُصبِح عدد الخيوط المُشغَّلة صفرًا، سيُحدِّث التابع حالة البرنامج على النحو المطلوب. تعرض الشيفرة التالية التابع الذي ستستدعيه الخيوط قبل انتهائها: synchronized private void threadFinished() { threadsRunning--; if (threadsRunning == 0) { // انتهت جميع الخيوط Platform.runLater( () -> { // تأكَّد من صحة حالة واجهة المُستخدِم الرسومية عندما تنتهي الخيوط startButton.setText("Start Again"); startButton.setDisable(false); threadCountSelect.setDisable(false); }); running = false; workers = null; } } لاحِظ أن التابع المُعرَّف بالأعلى متزامن synchronized؛ لضمان تجنُّب حالة التسابق race condition التي يُمكِنها أن تقع عند إنقاص قيمة المُتغيّر threadsRunning. قد يَستدعِي خيطان ذلك التابع بنفس اللحظة إذا لم نَستخدِم المزامنة، وإذا كان التوقيت دقيقًا، فربما يقرأ كلاهما نفس قيمة المتغير threadsRunning، ويَحسِبا نفس الإجابة بعد إنقاصه. ستقل في تلك الحالة قيمة المتغير threadsRunning بمقدار واحدٍ فقط وليس اثنين؛ أي أننا لم نَعُدّ خيطًا على النحو الصحيح، وعليه لن يَصِل المتغير threadsRunning إلى الصفر نهائيًا، وسيَعمَل البرنامج باستمرار بطريقةٍ مشابهة للقفل الميت deadlock. في الواقع، نادرًا ما تَحدث تلك المشكلة لأنها تعتمد على توقيتٍ بعينه، ولكن بالبرامج الأكبر حجمًا، تصبح تلك المشاكل خطيرةً للغاية كما أن تنقيحها debug ليس سهلًا. في المقابل، تمنع المزامنة حدوث ذلك الخطأ تمامًا. ترجمة -بتصرّف- للقسم Section 2: Programming with Threads من فصل Chapter 12: Threads and Multiprocessing من كتاب Introduction to Programming Using Java. اقرأ أيضًا المقال السابق: مقدمة إلى الخيوط Threads في جافا كيفية إنشاء عدة خيوط وفهم التزامن في جافا كتابة أصناف وتوابع معممة في جافا1 نقطة
-
تحدث المشكلات دوما، لكن وباستخدامك لتقنيات خدمة الزبائن الواردة في هذا الدرس يمكنك استعادة ثقة العملاء مجدّدًا. روى لي صديقي جيمس موقفًا واجهه في أحد الفنادق، عندما كان في مهمة عمل أوائل العام الحالي. حيث اضطر لتمدّيد إقامته آخر أيام الزيارة، ودفع -في الحقيقة الشركة هي من دفعت- مبلغ إضافيّ لتأخير تسليم الغرفة، وذلك كي يتسنّى له البقاء وإجراء مكالمة حول صفقة مهمة. وبما أنه سيحتاج للخصوصية والهدوء، علّق جيمس لوحة "عدم المقاطعة" على باب غرفته قبل موعد المكالمة، لكن وما جرى أنه بعد مضي 20 دقيقة من المحادثة بدأ أحدهم بطرق باب الغرفة بإلحاح، ورغم ذهوله من الأمر تجاهل جيمس الطرقات العنيفة وتابع مكالمته، آملًا أن يتوقف الطارق أيًا كان ويتركه وشأنه، لكن الطرق استمر بعلوّ. اضطر جيمس للاعتذار من محدّثه لدقيقتين كي يرى مَن الطارق -الأمر الذي سيزعج رئيسه في العمل حتمًا- ، إلا أنّه وفي الوقت نفسه وجد عاملة خدمة الغرف أمامه وجهًا لوجه بعد أن استخدمت بطاقتها لفتح باب الغرفة. حالما رأت العاملة جيمس مع سماعاتٍ على أذنيه اعتذرت منه وغادرت، لكن بعد فوات الأوان. لقد كان من المحتمل أن تكلّفه هذه المقاطعة صفقة مهمّة -لحسن الحظ لم يحصل ذلك- إلا أنّ الأمر أفسد تجربته في الفندق بكل الأحوال. قبل أن يسلم جيمس الغرفة ويغادر، توجّه لمقابلة مدير الفندق، وأقل ما يمكن قوله عما حدث في ذاك اللقاء أنه مدهشٌ تمامًا. لقد أصغى مدير الفندق إلى القصّة كاملة. هل كان بحاجة لمعرفة التفاصيل وإلى أي مدى كانت مكالمة جيمس على قدرٍ من الأهمية؟ بالتأكيد لا، كان جيمس مستاءً وقد تفهّم المدير ذلك، فهم أيضًا كم من المهم أن يشعر جيمس بأنه يُصغى إليه ، لذا تركه يصرخ ويعبر عن غضبه حتى أفرغ كل ما في جعبته. تاليًا: اعتذر المدير بشدّة، وتحمل مسؤولية الخطأ الحاصل، منوّهًا إلى أنه كان سيستاء بذات القدر لو واجه المشكلة عينها، مرجحًا إلى كون العاملة لا تزال في فترة التدريب، وأنه سيتأكد بنفسه من عدم تكرار الموقف مرة أخرى. بعد هذا كلّه، بدأ جيمس بالهدوء قليلًا، لكن المدير فاجأه بما هو أكثر، إذ قدم له بطاقة إقامة لليلة مجانية يمكنه استخدامها في أيّ من سلسلة فنادقه. خلال دقائق فحسب، تحوّل جيمس من عميل غاضب إلى عميل راض، بل وفيّ، ومع نهاية مقابلته للمدير، أخبره بأن المشكلة تلاشت بالكامل، حتى أنه أشاد بأداء عاملي خدمة الغرف طيلة الأيام الماضية وطلب من المدير ألا يتخذ ضد العاملة التي قاطعته أي إجراء صارم بسبب ما حصل، ووفقًا لجيمس فهي لا تزال تعمل في ذات الفندق حتى الآن. حدث هذا في سياتل، والعجيب أن جيمس لا زال يتردد على نفس الفندق بخياره الشخصي بسبب الطريقة التي حلّ فيها المدير الخطأ. يًسمي أساتذة التسويق مايكل مكولوف وساندرا بارادواج هذه التجربة بـ: مفارقة استعادة الخدمة service recovery paradox. ومفارقة استعادة الخدمة تأتي كنتيجة لإصلاح الأخطاء ومحاولة استعادة العميل بشكلٍ إيجابي للغاية، الأمر الذي يتسبب في رفع مستوى رضا/ولاء العميل بشكل أكبر حتى مما كان عليه قبل المشكلة الحاصلة في الخدمة. بتعبيرٍ بسيط: الأخطاء تحدث دومًا ، وستستمر بالحدوث كذلك. لا تعني خدمة العملاء الجيدة القضاء التام على الأخطاء -فهذه مهمة أشبه بالمستحيلة-، لكنها تعني الاستفادة من الأخطاء بتحويلها إلى فرصة لبناء علاقة أقوى مع عميلك. خمس خطوات لخدمة استعادة عملاء ممتازةمن المعروف عن شركة والت ديزني براعتها في إدارة الأمور بكل النواحي، بدءًا من الخدمات اللوجستية انتهاءً بالإدارة والتسويق، تبدو ديزني اليوم كنموذج تسعى الشركات التجارية الأخرى للتعلم منه ومحاكاته. في الواقع، تدفع الشركات آلاف الدولارات لإرسال موظفيها إلى معهد ديزني ليتعلموا رؤاها الخاصة، ومع ما يزيد عن 135 مليون شخص يرتاد منتجعات وحدائق ديزني سنويًا، أصبحت هذه الشركة بارعة في فنّ "استعادة خدمة العميل" لتجعل عملاءها سعداء ومخلصين. يتكوّن منهج ديزني في استعادة خدمة العملاء من خمس خطوات يمكنك تذكرها بسهولة مع اختصارها إلى H.E.A.R.D: Hear, Empathize, Apologize, Resolve, Diagnose اسمع، تعاطف، اعتذر، أصلح، شخّص 1- اسمعتمامًا كما فعل مدير الفندق في قصة جيمس، دع العميل يخبرك بقصته كاملة دون مقاطعة، حتى لو كان يصرخ بغضب، فما نحتاجه هو شخص منصتٌ فحسب. 2- تعاطفلعل التعاطف أحد أكثر مهارات خدمة دعم العملاء حساسيةً، وتعني قدرتك على الفهم العميق لأفكار الزبون ومشاعره، وإيصال الإحساس بهذا التفهّم لعميلك أيضًا. يمكنك استخدام عبارات من قبيل "كنت سأستاء مثلك تمامًا"، أو "أفهم تمامًا سبب شعورك بالإحباط". 3- اعتذرطالما أن العميل محقّ في شكواه، لا يمكنك أن تعتذر بما فيه الكفاية. في دراسة أعدتها مدرسة كاري التجارية في جامعة ولاية أريزونا، تبيّن أن 37% من العملاء يرضون عن استعادة الخدمة عندما يكون ثمة تعويض مادي لقاء ما عانوه -مثل استرداد مبلغ أو شحن حساب-، لكن عندما تجعل الشركة الاعتذار على رأس أولويات التعويض، فإن نسبة الزبائن الذين يشعرون بالرضا تتضاعف لتصبح 74%. 4- حل المشكلةحل المشكلة بسرعة، وهذه الخطوة ممكنة فقط في حال كان لموظفيك سلطة تمكّنهم من تعويض الزبون المستاء بسرعة، لذا يجب عليك الحرص على إعطاء فريقك الصلاحيات التي تتيح لهم التصرف في هذه المواقف. إذا لم تستطع تحديد نوع التعويض الملائم لحلّ المشكلة، يمكنك سؤال العميل: "ما الذي يمكنني فعله لأصحح الأمور؟" عبر إظهار لهفتك لجعل الأمور أفضل بالنسبة له، ستبدأ بسد الفجوة بين العميل غير الراضي أمامك، وبين ما تطمح أن يكون عليه زبونك من رضا. 5- شخص المشكلةحالما استطعت إرضاء العميل، ابدأ بتحليل الأمور بعمق ومعرفة أسباب حدوث المشكلة، دون إلقاء اللوم على أحد. خدمة العملاء المستائين هي فرصة وليست عقوبةمن الأسهل لك أن تترك العميل الغاضب يخرج من الباب بعد المشكلة، وحقيقة فإنه قد يحدث ذلك رغم محاولات إصلاح الخلل، إلا أنّ رجل الأعمال الناجح يعلم أن استعادة الخدمة هي واحدة من أهم عناصر المحافظة على العملاء، باتباع بضعة خطوات بسيطة يمكنك تحويل العملاء الغاضبين إلى آخرين مخلصين وسعداء. هل كنت يومًا بجهة تلقي شكوى عميل واستعادة خدمته؟ يسرني سماع تجاربكم في التعليقات. تُرجم بتصرف عن مقال You Screwed Up, and You Have an Angry Customer. Now Wha لكاتبه Len Markidan. حقوق الصورة البارزة: Designed by Freepik.1 نقطة