فبراير 07، 2009

سلسلة البرمجة الرسومية بواسطة باي كيوت PyQt4 للمبتدئين 03

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

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

أولا يجب علينا أن نتصور الطريقة التي يمكن من خلالها التعبير على عدد ما، مثل 125.
إحدى هذه الطرق هي (1x100) + (2x10) + (5x1)
 أو
القيمة السابقة مضروبة في 10 + العدد الجديد. مثال: X=(X*10) + y 
او
أن نعتبر أن كل رقم بمثابة نص يتم إضافته في أخر السطر للقيمة السابقة، مثال: "1"+"2" النتيجة ستكون هي "21"

سأعتمد على الطريقة الأخيرة لبساطتها.

إذن كلما ضغطنا على زر رقم معين سيكتب الرقم الذي يعبر عنه في أخر السطر للقيمة الحالية.
حتى يتمكن كل زر من القيام بذلك يجب أن نحدد ماذا عليه أن يفعل عندما سيتم الضغط عليه. و هذا ما يسمى بالأحداث Events.


الآن سأحاول أن أوضح لكم كيف سيعمل البرنامج بلغة نفهمها نحن ثم بعدها نحولها إلى لغة بايثون:
1. ستظهر الواجهة الرسومية البرنامج.
2. بعدها سيقوم المستخدم بكتابة العدد 935.
3. في كل مرة يتم الضغط على زر ما، يجب عليه أن يقرر في أي حقل (الأول أم الثاني) سيقوم بإضافة العدد الذي عبر عنه إلى العدد المكتوب مسبقا. مثلا إضافة "5" إلى "93"
4. بعد ذلك سيقوم المستخدم بعملية الجمع +
5. تتكرر نفس العملية بالنسبة للعدد الثاني تماما كما في الخطوة الثالثة.
6. عندما يضغط المستخدم على زر = يجب إجراء العملية الحسابية المطلوبة بحيث سيستخدم البرنامج القيمة المتواجدة في الحقل الأول و الثاني ثم يقرر أي نوع من العمليات الحسابية سيقوم بها، ثم يظهر النتيجة في الحقل الثالث.


قبل أن أكتب الشفرة المصدرية سأدكر بأن الأزرار التي تعبر عن 0 حتى 9 إسمها c0 إلى c9
أزرار العمليات الرياضية:
+ opplus
- opminus
* opmulti
/ opdiv
زر = إسمه opequal
زر الفاصلة . opdot
زر المسح opclear
الحقل الأول إسمه value1، الحقل الثاني value2، حقل النتيجة valresult

الشفرة المصدرية للبرنامج هي:




import sys

from PyQt4 import QtGui, QtCore, uic

from functools import partial



class CalcApp(QtGui.QDialog):



    bSwitch = False

    iMathOp = 0



    def __init__(self, *args):

        QtGui.QWidget.__init__(self, *args)

        uic.loadUi("PyCalc.ui", self)



        self.connect(self.c0, QtCore.SIGNAL("clicked()"), partial(self.fAddNbr, '0'))

        self.connect(self.c1, QtCore.SIGNAL("clicked()"), partial(self.fAddNbr, '1'))

        self.connect(self.c2, QtCore.SIGNAL("clicked()"), partial(self.fAddNbr, '2'))

        self.connect(self.c3, QtCore.SIGNAL("clicked()"), partial(self.fAddNbr, '3'))

        self.connect(self.c4, QtCore.SIGNAL("clicked()"), partial(self.fAddNbr, '4'))

        self.connect(self.c5, QtCore.SIGNAL("clicked()"), partial(self.fAddNbr, '5'))

        self.connect(self.c6, QtCore.SIGNAL("clicked()"), partial(self.fAddNbr, '6'))

        self.connect(self.c7, QtCore.SIGNAL("clicked()"), partial(self.fAddNbr, '7'))

        self.connect(self.c8, QtCore.SIGNAL("clicked()"), partial(self.fAddNbr, '8'))

        self.connect(self.c9, QtCore.SIGNAL("clicked()"), partial(self.fAddNbr, '9'))



        self.connect(self.opplus, QtCore.SIGNAL("clicked()"), partial(self.fEntrySwitch, True, 1))

        self.connect(self.opminus, QtCore.SIGNAL("clicked()"), partial(self.fEntrySwitch, True, 2))

        self.connect(self.opmulti, QtCore.SIGNAL("clicked()"), partial(self.fEntrySwitch, True, 3))

        self.connect(self.opdiv, QtCore.SIGNAL("clicked()"), partial(self.fEntrySwitch, True, 4))



        self.connect(self.opequal, QtCore.SIGNAL("clicked()"), self.fCalculate)



        self.connect(self.opdot, QtCore.SIGNAL("clicked()"), self.fDecimal)



        self.connect(self.opclear, QtCore.SIGNAL("clicked()"), self.fClear)





    def fAddNbr(self, x):

        if self.bSwitch == False:

            self.value1.setText(self.value1.text() + x)

        else:

            self.value2.setText(self.value2.text() + x)





    def fEntrySwitch(self, bSwitch,  iMathOp):

        self.bSwitch = bSwitch

        self.iMathOp = iMathOp





    def fDecimal(self):

        if self.bSwitch == False:

            x = str(self.value1.text())

            v = self.value1

        else:

            x = str(self.value2.text())

            v = self.value2



        if x.find('.') == -1:

            v.setText(v.text() + '.')





    def fCalculate(self):

        if self.iMathOp == 1 :

            rCalc = float(self.value1.text()) + float(self.value2.text())

        elif self.iMathOp == 2:

            rCalc = float(self.value1.text()) - float(self.value2.text())

        elif self.iMathOp == 3:

            rCalc = float(self.value1.text()) * float(self.value2.text())

        elif self.iMathOp == 4:

            rCalc = float(self.value1.text()) / float(self.value2.text())

        else:

            return None



        self.valresult.setText(str(rCalc))





    def fClear(self):

        self.bSwitch = False

        self.iMathOp = 0

        self.value1.setText('')

        self.value2.setText('')

        self.valresult.setText('')





if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)



    widget = CalcApp()

    widget.show()



    sys.exit(app.exec_())





بالنسبة للتغيرات التي حصلت فهي كالتالي:
1. تم إستيراد دالة partial من قسم functools. سيتم الإستعانة بهذه الدالة لربط بين الدوال التي سنضيفها نحن و بين الأحداث (الضغط على الأزرار)
2. تم الإستعانة بمتغيرتين bSwitch التي سنستخدمها لتحديد الحقل الذي سيكتب فيه العدد، و iMathOp لتحديد نوع العملية الحسابية المطلوبة.
3. تم إضافة دالة fAddNbr لتقوم بمهمة إضافة العدد الذي ضغط عليه المستخدم إلى العدد الموجود سابقا، و هذه الدالة قادرة على معرفة أي حقل ستستخدم إعتمادا على متغيرة bSwitch
4. تم إضافة دالة fEntrySwitch التي ستقوم بتغير قيمة المتغيرة bSwitch إلى قيمة صحيح أو خطأ و  بتغير قيمة iMathOp إلى قيمة عددية من 1 إلى 4
5. دالة fCalculate التي ستقوم بمعرفة العملية الحسابية المطلوبة إعتمادا على قيمة المتغيرة iMathOp ثم تقوم بإجرائها على قيمة الحقل الأول و الثاني، ثم تضع النتيجة في الحقل الثالث.
6. دالة fClear التي تقوم بمسح محتوى الحقول و تغير قيمة المتغيرتين إلى القيمة المفترضة.
7. دالة fDecimal وظيفتها هو إضافة الفاصلة إلى العدد المتواجد في الحقل الأول أو الثاني، ثم التأكد من عدم إسناد أكثر من فاصلة واحدة إلى نفس العدد.
8. عودة إلى الدالة __init__ و التي سيتم تعديلها حتى تقوم:
8.1. بربط كل زر من أزرار الأعداد بدالة fAddNbr و بالتالي نحصل على وظيفة كتابة الأعداد في الحقلين الأول و الثاني.
8.2. بربط كل زر من أزرار العمليات الحسابية بدالة fEntrySwitch و بالتالي يستشعر البرنامج تغير قيمة المتغيرتين bSwitch و iMathOp
8.3. ربط زر يساوي = بدالة fCalculate التي ستتكلف بحساب النتيجة و وضعها في الحقل الثالث.
8.4. ربط زر الفاصلة بدالة fDecimal التي خصصناها إلى التحكم في الفاصلة.
8.5. ربط زر المسح C بدالة fClear لمسح و إعادة القيم في الحقول و المتغيرتين إلى القيم المفترضة

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

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

تحديث 1 (14-04-2009): يمكن تحميل الشفرة المصدرية من هنا.

هناك 5 تعليقات:

  1. جميل جدا والله
    لكن عن نفسى كنت هفكر فى اتجاه تانى
    مثلا استخدام sender فى ال slots والربط بloop للأزرار بدل التكرار طالما اللوجيك واحد وممكن استفادة من eval

    صحيح مش لازم تضيف f او i او اى سابقة للأسماءفى رأيى بتقلل وضوح الكود يمكن مفيدة مع البرمجة ل Win32 مثلا لكن مش مفيدة فى الحالة دى
    وبردو iMathOp انت ممكن تستخدم enumeration
    http://programming-fr34ks.net/smf/articles-12/enums-in-python/

    حاجة لفتت نظرى
    class CalcApp(QtGui.QDialog):

    انت اشتقيت من QDialog فإزاى يتسمى ال CalcApp ؟


    فى انتظار الجزء القادم ^_^

    ردحذف
  2. شكرا على هذه الملاحظات. فعلا كنت سأعتمد على بعض منها في التدوينة الجديدة.

    لكن لم أفهم بالتحديد ما قصدته ب إشتقاق Qdialog و CalcApp

    ردحذف
  3. اللى قصدته ان فى Qt يوجد نوع واحد من التطبيق QApplication واى حد هيقرا اسم CalcApp هيجى فى باله انك ورثت ال QApplication بعكس CalcDialog -لأنك ورثت من QtGui.QDialog- او CalcWindow

    ردحذف
  4. شكرا لك على هذه الملاحظة القيمة. هل تعرف/تملك كتاب/مصدر يتناول مثل هذه الملاحظات/نصائح؟

    ردحذف
  5. كتاب C++ GUI Programming with Qt 4
    و كتاب Rapid GUI Programming with Python and Qt
    والأمثلة اللى بتبقة مع Qt/PyQt وشرحها اللى فى ال Assistant مليئة بالأفكار وازاى تبرمج بطريقة تتكامل مع Qt

    اتمنى لك التوفيق

    ردحذف