كيف قمت بإنشاء تطبيق الآلة الحاسبة باستخدام Django ج2

في هذه السلسلة التعليمية، سنتعلم كيفية إضافة آلة حاسبة إلى تطبيق Django.

  • في الجزء الأول قمنا بتصميم تخطيط الآلة الحاسبة باستخدام HTML وCSS.
  • أما في الجزء الثاني، سنقوم بإيقاظ تطبيق الويب من سباته الطويل من خلال جعله تفاعليًا باستخدام JavaScript.

هنا جميع الأجزاء الثلاثة:

نحن لا نبني تطبيق آلة حاسبة لأننا لا نملك واحدة في المنزل أو لأن تطبيقنا سيكون متطورًا. الغرض من إنشاء هذا التطبيق البسيط هو تحسين مهاراتنا في البرمجة. أصبحت أهميتها أكثر وضوحًا في JavaScript، وهي لغة سهلة التعلم ولكن من الصعب إتقانها.

في هذا المشروع، سوف نتعلم كيفية التعامل مع DOM باستخدام JavaScript. تأكد من إكمال الجزء الأول قبل متابعة هذا الدرس. قم بإنشاء اسم ملف آخر script.js في دليلك الحالي، ودعنا نبدأ.

إجراء بعض التعديلات

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

حسنًا، يمكننا الآن أن نبدأ.

تعريف المتغيرات الثابتة

نحن بحاجة إلى تحديد بعض المتغيرات الثابتة لأزرار الآلة الحاسبة وعملياتها.

const keys = document.querySelector('.calculator-keys'),
         display = document.querySelector(.calculator-screen’),
         
         calculator = {
             displayValue: '0',
             firstOperand: null,
             waitingForSecondOperand: false,
             operator: null,
         };

يمثل متغير المفاتيح جميع مفاتيح الآلة الحاسبة الموجودة داخل عنصر div مع اسم class كـ calculator-keys. متغير display هو عنصر الإدخال الذي يعمل بمثابة شاشة الآلة الحاسبة.

يتكون التعبير الرياضي (20 + 10) من ثلاثة أجزاء رئيسية: المعامل الأول (20)، المعامل (+)، المعامل الثاني (10). في كائن الآلة الحاسبة، نحدد هذه المتغيرات وغيرها لتتبع كل ما هو ضروري لإنشاء تطبيق الآلة الحاسبة.

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

لاحظ أننا لم نحدد المعامل الثاني. هذا لأنه ليس محدودا. يمكن أن يستمر. كل ما يتم إدخاله على الشاشة بمجرد تحول القيمة المنطقية إلى صحيح يشكل المعامل الثاني.

إستخدام switch case

تتكون الآلة الحاسبة من أربعة مكونات وهي:

  • العوامل الرياضية لأداء العمليات الأساسي.
  • شاشة عرض لعرض العمليات الحسابية.
  • زر مسح الشاشة لمسح جميع المدخلات.
  • زر عشري للتعامل مع الأرقام العشرية.

سنقوم بإنشاء دالة لكل هذه العناصر ونستخدم switch case لتنفيذها

switch (value) {
    case '+':
    case '-':
    case '*':
    case '/':
    case '=':
        handleOperator(value);
      break;
    case '.':
      addDecimal(value);
      break;
    case 'all-clear':
      calculatorReset();
      break;
    default:
      addDigit(value)
}

شاشة الآلة الحاسبة

قمنا بإزالة القيمة الصفرية في ملف العلامات. دعونا نضيفه مرة أخرى عن طريق إنشاء دالة له. سيتم أيضًا استخدام دالة updateScreen() لتحديث الشاشة بناءً على كل ما هو موجود في DisplayValue عند إجراء عملية ما.

اكتب الكود التالي تحت المتغيرات الثابتة.

updateScreen = () => {
  display.value = calculator.displayValue;
};
updateScreen();

بمجرد تحديث المتصفح، سوف ترى الصفر معروضًا.

الاستماع للنقرات

نريد الاستماع للنقرات على جميع مفاتيح الآلة الحاسبة. اعتمادًا على نوع المفتاح الذي تم النقر عليه، سنستخدم بعد ذلك switch case لتنفيذها. لاحظ أننا لم نحدد متغيرات منفصلة لمجموعات المفاتيح الأربع.

نظرًا لأن جميع المفاتيح هي عناصر فرعية للعنصر الأصلي (div)، فإننا نختار العنصر الأصلي فقط لأننا نعلم أن العناصر الفرعية سيرثون حدث النقر. الكود الموالي يكون بعد دالة updateScreen().

keys.addEventListener('click', event => {
  const  target = event.target;
  const  value  = target.value;
  if (!target.matches('button')) {
    return;
  }
switch (value) {
    case '+':
    case '-':
    case '*':
    case '/':
    case '=':
        handleOperator(value);
      break;
    case '.':
      addDecimal(value);
      break;
    case 'all-clear':
      calculatorReset();
      break;
    default:
      addDigit(value)
}

updateScreen();
});

نحدد دالة مجهولة في مستمع الحدث والتي سيتم تنفيذها عند النقر فوق المفتاح. يمثل المتغير target العنصر الذي تم النقر عليه، وسيتم تسجيله في وحدة التحكم مع قيمته. إذا لم يكن العنصر زرًا، فسيتم إنهاء الدالة.

نضيف الآن switch case داخل دالة رد الاتصال. لذلك، اعتمادًا على نوع المفتاح الذي تم النقر عليه، ستقوم علبة المفتاح بتنفيذ الدالة المعنية. بمجرد تنفيذه، سوف يخرج من كتلة التبديل، وسيتم تحديث الشاشة.

الاستجابة للنقرات

(i) The Digits

لنبدأ بالقيمة الافتراضية. سيتم تنفيذ addDigit() في حالة عدم وجود تطابق لحالة الأحرف. ستجعل الوظيفة أزرارنا تنبض بالحياة وتعرض على الشاشة أي مفتاح يتم النقر عليه.

addDigit = digit => {
    const displayValue = calculator.displayValue,
      calculator.displayValue = displayValue === '0' ? digit : displayValue + digit;
    }
}

داخل دالة addDigit()، نستخدم عامل تشغيل ثلاثي للتحقق مما إذا كانت القيمة المعروضة على الشاشة صفرًا. إذا كان الأمر كذلك، فإننا نستبدل كل ما تم تخزينه في DisplayValue بالرقم الذي تم النقر عليه. وبخلاف ذلك، فهو ليس رقمًا صفرًا. في هذه الحالة، نقوم بربط الرقم بالقيمة الموجودة في متغير DisplayValue.

جربه من خلال التعليق على الدوال الثلاث الأولى داخل الدالة المجهولة.

يمكننا أن نرى الأزرار تستجيب للنقرات. كما هو الحال دائمًا، اضغط على Ctrl + Shift + I لعرض أدوات المطورين لأغراض تصحيح الأخطاء.

العلامة العشرية

عند هذه النقطة، الأرقام فقط هي التي تعمل. فلنقم بتنشيط العلامة العشرية عن طريق إنشاء دالة addDecimal().

addDecimal = decimal => {
    if (!calculator.displayValue.includes(decimal)) {
      calculator.displayValue += decimal;
    }
  }

إذا لم تكن displayValue تحتوي بالفعل على العلامة العشرية، فإننا نقوم بتضمينها ليتم عرضها على الشاشة. تتأكد عبارة if أيضًا من إضافة العلامة العشرية مرة واحدة فقط. قم بإلغاء التعليق على دالة addDecimal() لاختبارها.

مفاتيح العوامل

مفاتيح العوامل الأربعة وأزرار المساواة لا تعمل. فلنوقظهم من سباتهم الطويل يمكنك الآن إلغاء التعليق على دالة HandleOperator().

handleOperator = nextOperator => {
  const firstOperand = calculator.firstOperand,
        displayValue = calculator.displayValue,
        operator  = calculator.operator;

  const inputValue = parseFloat(displayValue);

  if (firstOperand == null && !isNaN(inputValue)) {
    calculator.firstOperand = inputValue;

  calculator.waitingForSecondOperand = true;
  calculator.operator = nextOperator;
 
}

تحصل الدالة على الإدخال من الشاشة وتحوله إلى رقم عشري. هذا لأنه تم تعريفه كسلسلة. كان بإمكاننا تحويله إلى رقم باستخدام دالة Number. لكننا لا نعرف ما يمكن لمستخدمينا إدخاله.

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

هل يمكنك أن ترى كيف يتغير كائن الآلة الحاسبة ديناميكيًا؟

لاحظ أنه يمكنك دائمًا تسجيل كائن الآلة الحاسبة داخل السطر الأخير من كل دالة لمراقبة ما يحدث في وحدة التحكم.

تحديث دالة addDigit()

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

قم بتحديث دالة addDigit() من خلال الكود التالي:

addDigit = digit => {
    const displayValue = calculator.displayValue,
        waitingForSecondOperand = calculator.waitingForSecondOperand; 
    if (waitingForSecondOperand === true) { 
      calculator.displayValue =  digit;
      calculator.waitingForSecondOperand = false;
    } else {
      calculator.displayValue = displayValue === '0' ? digit : displayValue + digit;
  }

نحن نحدد ما يجب أن يحدث عندما تقوم الدالة HandleOperator() بتحويل القيمة المنطقية إلى true. يصبح الرقم الذي تم إدخاله هو القيمة الجديدة ل displayValue. الآن بدلًا من الإلحاق، تتم الكتابة فوق displayValue بأي رقم يتم إدخاله.

تحديث دالة HandleOperator()

لم ننته بعد من دالة HandleOperator(). بالنظر إلى كتلة switch case، فإن هدفنا هو أن تقوم الدالة بإجراء حسابات رياضية على أي عامل يتم إدخاله.

العودة إلى دالةHandleOperator(). حيث تستمر في إرسال الإشارات للمشغل التالي، ولكن ماذا تفعل بالمشغلات التي استقبلتها سابقًا؟ ومن المتوقع أن يقوم ببعض العمليات. لم نحدد ذلك. لذلك قم بتحديث الدالة بما يلي:

handleOperator = nextOperator => {
  const firstOperand = calculator.firstOperand,
        displayValue = calculator.displayValue,
        operator  = calculator.operator;

  const inputValue = parseFloat(displayValue);

if (firstOperand == null && !isNaN(inputValue)) {
    calculator.firstOperand = inputValue;
  } else if (operator) {                                                                  //add from here…
    const result = calculate(firstOperand, inputValue, operator);
    calculator.displayValue = String(result);
    calculator.firstOperand = result;                                             //…to here
  }

  calculator.waitingForSecondOperand = true;
  calculator.operator = nextOperator;
 
}

نحن نحدد ذلك باستخدام عبارة else if. إذا قام المستخدم بإدخال عامل التشغيل، فسيتم استدعاء دالة calculate(). لاحظ ما تم تمريره كمعلمات إلى الدالة لأننا سنستخدمها لترميز دالة calculate().

يتم بعد ذلك تحويل النتيجة مرة أخرى إلى سلسلة وعرضها للمستخدم من خلال خاصية DisplayValue. هل تتذكر أننا حددنا ذلك في دالة updateScreen()؟

دالة calculate()

دالة calculate() لا تحتاج إلى شرح. تأخذ المعاملين الأول والثاني بالإضافة إلى العامل المحدد، و تقوم بإجراء العملية الحسابية.

calculate = (firstOperand, secondOperand, operator) => {
  if (operator === '+'){
    return firstOperand + secondOperand;
  } else if (operator === '-') {
    return firstOperand - secondOperand;
  } else if (operator === '*'){
    return firstOperand * secondOperand;
  } else if (operator === '/'){
    return firstOperand / secondOperand;
  }
  return secondOperand;
}

يقوم تطبيق الآلة الحاسبة الآن بإجراء العمليات باستخدام عوامل التشغيل الأربعة. لقد أدخلت 15*5 ويمكنك رؤية النتيجة في لقطة الشاشة أعلاه. تعرض وحدة التحكم أيضًا العمليات وكيف تم تغيير كائن الآلة الحاسبة ديناميكيًا.

بمجرد أن نضغط على المفتاح =، تصبح النتيجة المعروضة هي المعامل الأول للعملية التالية وتستمر دوال HandleOperator() في انتظار العامل التالي.

تحديث دالة HandleOperator() للمرة الأخيرة

ماذا لو أن العامل الأول الذي تم إدخاله ليس هو ما يدور في ذهننا وقررنا إدخال عامل آخر، فكيف سيتعامل تطبيق الآلة الحاسبة معه؟ و هل سوف يؤدي إلى نتائج غير متوقعة. دعونا نعدل HandleOperator() ليعطينا النتيجة المرجوة.

handleOperator = nextOperator => {
  const firstOperand = calculator.firstOperand,
        displayValue = calculator.displayValue,
        operator  = calculator.operator;

  const inputValue = parseFloat(displayValue);


  if (operator && calculator.waitingForSecondOperand)  {          //add from these …

    calculator.operator = nextOperator;

   console.log(calculator)

    return;

  }                                                                                               // to these …

  if (firstOperand == null && !isNaN(inputValue)) {
    calculator.firstOperand = inputValue;
  } else if (operator) {
    const result = calculate(firstOperand, inputValue, operator);
    calculator.displayValue = String(result);
    calculator.firstOperand = result;
  }

  calculator.waitingForSecondOperand = true;
  calculator.operator = nextOperator;
 console.log(calculator);
}

نظرًا لأنه تم ضبط القيمة المنطقية على القيمة true أثناء انتظار المعامل الثاني، فإننا نستخدم عبارة if للتأكد من أنه يجب استلام العامل التالي قبل إجراء عملية حسابية.

اختبرها. يجب أن يعمل التطبيق كما هو متوقع. من لقطة الشاشة، قمت بالنقر فوق العديد من العوامل ولكنها لم تنفذ أي عملية. وظل ينتظر المعامل الثاني.

إعادة ضبط الآلة الحاسبة

جميع الأزرار نشطة باستثناء زر واحد. زر AC لا يزال في سبات طويل. لا يمكننا أن نتركه ينام إلى الأبد. و لتنشيطه، سنقوم الآن بإلغاء تعليق دالة CalculatorReset()‎ وتعيين كافة خصائص كائن الآلة الحاسبة إلى حالتها الافتراضية.

calculatorReset = () => {
  calculator.displayValue = '0';
  calculator.firstOperand = null;
  calculator.waitingForSecondOperand = false;
  calculator.operator = null;

}

إنه مشابه لكائن الآلة الحاسبة. اختبره مرة أخرى للتأكد من أن الزر يعمل الآن.

تحديث دالة addDecimal()

لا يزال هناك خطأ في تطبيق الآلة الحاسبة. انظر ماذا يحدث عندما تقوم بإدخال علامة عشرية بعد إدخال علامة المشغل. يتم إلحاقه بالمعامل الأول. من المفترض أن يكون جزءًا من المعامل الثاني. دعونا نصلح الأمر!

addDecimal = decimal => {
if (calculator.waitingForSecondOperand === true) {
    calculator.displayValue = '0.'
      calculator.waitingForSecondOperand = false;
      return;
    }
    if (!calculator.displayValue.includes(decimal)) {
      calculator.displayValue += decimal;
    }
  }

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

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

تقليل النقاط العشرية

لا نريد أن يكون لدينا هذا النوع من العرض (انظر لقطة الشاشة أدناه) في تطبيقنا عندما نقوم بإجراء عمليات عشرية.

إذا كان الأمر على ما يرام معك، يمكنك تجاهله وتخطي هذه الخطوة. بخلاف ذلك، استخدم طريقة to.fixed() لتقييد عدد الأرقام العشرية بأي عدد من اختيارك. سيتم ذلك في دالة HandleOperator().

استبدل المقتطف التالي:

    calculator.displayValue = String(result);

بالتالي:

calculator.displayValue = '${parseFloat(result.toFixed(7))}';

اختبر التطبيق وسترى أن رقم الفاصلة العائمة قد انخفض.

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


اكتشاف المزيد من بايثون العربي

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

Scroll to Top

اكتشاف المزيد من بايثون العربي

اشترك الآن للاستمرار في القراءة والحصول على حق الوصول إلى الأرشيف الكامل.

Continue reading