اذهب إلى المحتوى

تحدثنا في المقال السابق عن حاوية العنوان label و صناديق المدخلات Entry والتي هي جزء من عناصر الواجهة الرسومية Widgets في مكتبة TKinter، وسنواصل الحديث في هذا المقال عن مجموعة العناصر التي يمكننا إضافتها للنافذة الرئيسية في واجهات المستخدم الرسومية وهي الأزرار Buttons ومربعات الرسائل messagebox بقليل من التفصيل.

هذه المقالة جزء من سلسلة مقالات تشرح أساسيات مكتبة TKinter لبناء واجهات رسومية في بايثون، وإليك فهرس السلسلة كاملة:

الأزرار Buttons

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

إنشاء الأزرار داخل النافذة

نختار اسم المتغير المطلوب ليمثل الأزرار ويُستخدم الصنف Button من مكتبة Tkinter لذلك، ويعطى أولًا المكان الذي نريد وضع الزرار فيه وهو النافذة الرئيسية، ومن ثم النص المراد عرضه داخله ليوضح الهدف منه أو العمل الذي سيقوم بتنفيذه عند النقر عليه باستخدام الخاصية text على النحو التالي:

First_button=Button(main_window,text="النص")

تنشئ الشيفرة التالية زرارًا يعرض كلمة "موافق" ومن ثم تضعه على النافذة الرئيسية باستخدام الدالة pack:

from tkinter import *
main_window = Tk()
main_window.title(" الأزرار ")

First_button=Button(main_window,text='موافق')
First_button.pack()
main_window.mainloop()

نحصل بتشغيل الشيفرة على الشكل التالي:

001_GUI_button.jpg

نافذة بزر

خصائص الأزرار

يمكننا تغيير الكثير من خصائص الأزرار كتغيير لون النص fg، ولون الخلفية bg، ونوع الخط font، ولون الأزرار أثناء النقر عليها activebackground، ولون النص أثناء النقر عليه activeforeground، وسمك الحدود bd، بالإضافة إلى ذلك تسمح الخاصية relief بتعديل نوع الحدود حيث يمكن إعطائها قيم مختلفة مثل :

raised, flat, ridge, groove and sunken.

كذلك يمكننا التحكم بحالة الزر باستخدام الخاصية state حيث يكون فعالًا normal افتراضيًا ويمكننا تغييره إلى غير فعال disable، ويمكننا عمل ذلك إما أثناء إنشاء الزر أو في خطوة لاحقة مستقلة باستخدام الدالة config على النحو التالي:

First_button.config(state='disable')

كما يمكننا التعديل على الخصائص باستخدام مبدأ القواميس dictionary حيث نقوم بمعاملة العنصر كقاموس ونستخدم اسم الخاصية المراد وضع قيمة لها كمفتاح للقاموس ونضع القيمة المرغوبة على النحو التالي:

First_button['state']='disable'

سنقوم برسم زر بالخصائص التالية لون النص fg أبيض، ولون الخلفية bg أخضر، وبخط من نوع Verdana بحجم 14 ويكون النص "موافق" تحته خط، بحدود ثخينة bd بمقدار 5 ونوع حدود relief بقيمة groove ونحيطه بمساحة حدود داخلية وخارجية على النحو التالي:

from tkinter import *

main_window = Tk()
main_window.title(" الأزرار ")

First_button=Button(main_window,text='موافق',
                    font = "Verdana 14 underline",
                    fg="white", bg="green",
                    bd=5 ,relief ='groove')
First_button.pack(padx=10,pady=30,ipadx=20,ipady=20)
main_window.mainloop()

عند تشغيل الشفرة سنحصل على الشكل التالي:

002_GUI_colored_button.jpg

نافذة بزر ملون

استجابة الأزرار

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

First_button=Button(main_window,text="النص" , command=function_name)

حيث تمثل function_name اسم الدالة التي سنكتبها، وسنعيد كتابة الشفرة السابقة ونضع استجابة للزر بحيث تظهر لنا رسالة ترحيبية عند النقر عليه في حاوية العنوان، لذا سنقوم بتعريف دالة نسميها CallBack ونضع فيها شفرة إظهار رسالة التعريف على النحو التالي:

from tkinter import *

main_window = Tk()
main_window.title(" الأزرار ")

def callback():
     mylabeltext.set(" مرحباً باستجابة الأزرار ")

First_button=Button(main_window,text='موافق',
                    font = "Verdana 14 underline",
                    fg="white", bg="green",
                    bd=5 ,relief ='groove',
                    command=callback)

First_button.pack(padx=10,pady=30,ipadx=20,ipady=20)

mylabeltext= StringVar()
result_Label = Label(main_window, textvariable=mylabeltext )
result_Label.pack()

main_window.mainloop()

وسنحصل على النتيجة التالية عند تشغيل الشفرة:

003_GUI_functional_button.jpg

نافذة بزر يستجيب للنقر

إدراج صورة داخل زر

بإمكاننا استبدال النص الذي يُعرض بالزرار بصورة قد تكون أكثر تعبيرًا من الكلمات لفعل ذلك نحتاج إلى تعريف كائن من الصنف PhotoImage وسنقوم بتسميته buttonphoto، باستخدام الخاصية file إذ سنعطيه مسار الصورة (مكان تواجدها في الجهاز) على النحو التالي:

buttonphoto = PhotoImage(file='Photo\path')

ومن ثم نستخدم الخاصية image أثناء تعريف الزر ونسند لها قيمة الكائن الذي أنشأناه ستكون الشفرة كاملة على النحو التالي:

from tkinter import *

main_window = Tk()
main_window.title(" الأزرار ")

def callback():
     mylabeltext.set(" مرحباً باستجابة الأزرار ")

buttonphoto = PhotoImage(file='Arrow.png')
First_button=Button(main_window,image=buttonphoto,
                     bd=5,command=callback)
First_button.pack(padx=10,pady=30,ipadx=20,ipady=20)

mylabeltext= StringVar()
result_Label = Label(main_window, textvariable=mylabeltext )
result_Label.pack()

main_window.mainloop()

وسنحصل على النتيجة التالية:

004_GUI_img_button.jpg

نافذة بزرار على شكل صورة

استخدمنا في المثال السابق الصورة المسماة Arrow.png وكانت موجودة في نفس مسار وجود ملف الشفرة، لذلك وضعنا اسمها مباشرة، ولو كانت في مكان مختلف لقمنا بتحديد مكان وجودها، للأسف الكائن PhotoImage يدعم فقط صيغ محدودة وهي:

GIF, PGM, PPM, PNG

ولو أردنا أن نعرض نصًا وصورة في نفس الزرار يجب علينا أن نستعين بالخاصية compound والتي تحدد مكان ظهور الصورة بالنسبة للنص وتأخذ قيمة من الاحتمالات التالية:

'top','bottom', 'left', 'right', 'center','none'

يعرض السطر التالي الصورة فوق النص:

buttonphoto = PhotoImage(file='Arrow.png')
First_button=Button(main_window,image=buttonphoto ,text='موافق',
        bd=5 ,command=callback ,compound='top')

وتكون النافذة بالشكل التالي:

005_GUI_img_txt_button.jpg

نافذة بزرار صورة ونص

مربع الرسائل messagebox

يحتاج البرنامج عند تطوير تطبيقات واجهات المستخدم الرسومية بالغالب لإعلام المستخدم بسير الأحداث، وإخباره برسائل الخطأ أو الرسائل التحذيرية ونحوها، على سبيل المثال عند نقر المستخدم على زر الحفظ، من الجيد إظهار رسالة تبين حالة العملية من نجاح أو فشل. أو عند حدوث خطأ ما وعدم تمكن البرنامج من إتمام مهمة معينة يُفترض إظهار رسالة تبين الخطأ ونوعه للمستخدم، لنتمكن من عمل هذا سنستعين بالوحدة messagebox من مكتبة Tkinter.

استدعاء مربع الرسائل

تأتي الوحدة messagebox مدمجةً مع مكتبة Tkinter لذلك لا حاجة لأي تثبيت إضافي له، فقط يضاف أمر استدعاء في بداية الشفرة لنتمكن من استخدامها على النحو التالي:

from tkinter import messagebox

مربعات الرسائل الخبرية

النوع الأول من أنواع مربعات الرسائل هي الخبرية والتي تظهر رسالة للمستخدم مع زر واحد فقط تغلق النافذة بالضغط عليه. وسنتعرف إلى الدوال التالية التي تظهر رسالة خبرية للمستخدم:

  • showinfo: تظهر مربع رسالة على شكل معلومة خبرية، لإخبار المستخدم بانتهاء تنفيذ أمر معين مثلًا
  • showerror: تظهر مربع رسالة على شكل رسالة خطأ، لإخبار المستخدم بحدوث خطأ أثناء تنفيذ أمر معين مثلًا.
  • showwarrning: تظهر مربع رسالة على شكل رسالة تحذيرية، لإخبار المستخدم بانتهاء تنفيذ أمر معين لكن بوجود أمر لم يتم بالشكل المطلوب مثلًا.

تأخذ جميع هذه الأنواع من المستخدم أمرين: أولًا عنوان مربع الرسالة وثانيًا نص الرسالة

showinfo(title, message)
showerror(title, message)
showwarrning(title, message)

عندما نرغب باستخدام واحدة من مربعات الرسائل السابقة نقوم باستدعائها مستخدمين اسم الوحدة على النحو التالي:

messagebox.showerror( title='Error', message='This is an error message.'))

يمكننا للكتابة بطريقة مختصرة تعديل سطر الاستدعاء منذ البداية ليكون على النحو التالي:

from tkinter.messagebox import *

ثم يمكننا استدعاء مربع الرسالة المطلوب باستخدام اسمه مباشرة دون الحاجة لوضع اسم الوحدة قبله.

سنقوم بعمل نافذة تحتوي ثلاث أزرار يقوم كل واحد منها بإظهار نوع من مربعات الرسائل عند النقر عليه، من خلال الشفرة التالية:

from tkinter import *
from tkinter.messagebox import *

main_window = Tk()
main_window.title(" الأزرار ")

def error_message():
     showerror( title='خطأ',message='هذه رسالة خطأ')

def info_message():
     showinfo(title='معلومة',message='هذه رسالة معلومات')

def warning_message():
     showwarning(title='تحذير',message='هذه رسالة تحذيرية')

Error_button=Button(main_window,bd=3,text='إظهار مربع رسالة خطأ  ',command =error_message)
Error_button.pack(ipadx=20,ipady=20)

Info_button=Button(main_window,bd=3,text='إظهار مربع رسالة إخبارية',command =info_message)
Info_button.pack(ipadx=20,ipady=20)

warning_button=Button(main_window,bd=3, text='إظهار مربع رسالة تحذيرية',command=warning_message)
warning_button.pack(ipadx=20,ipady=20)

main_window.mainloop()

وسنحصل على النافذة التالية:

006_GUI_window_withbuttons.jpg

نافذة بأزرار لعرض مربعات الرسائل

007_GUI_messagebox.jpg

مربعات الرسائل

مربعات الرسائل الحوارية

النوع الثاني من أنواع مربعات الرسائل هي الحوارية والتي تُظهر للمستخدم رسالة وتنتظر منه استجابة وتأتي على عدة أشكال:

  • askquestion: تظهر له خيار إما نعم أو لا Yes/No وترجع الدالة قيمة الزر الذي تم النقر عليه بصيغة Yes/No.
  • askyesno: تظهر له خيار إما نعم أو لا Yes/No وترجع الدالة قيمة الزرار الذي تم النقر عليه بصيغة True/False.
  • askyesnocancel: تظهر له خيار إما نعم أو لا أو إلغاء Yes/No/ Cancel وترجع الدالة قيمة الزر الذي تم النقر عليه بصيغة True/False/None على التوالي.
  • askokcancel: تظهر له خيار إما نعم أو إلغاء OK/ Cancel وترجع الدالة قيمة الزر الذي تم النقر عليه بصيغة True/False.
  • askretrycancel: تظهر له خيار إما إعادة المحاولة أو إلغاء retry/ Cancel وترجع الدالة قيمة الزر الذي تم النقر عليه بصيغة True/False.

تطبيق الآلة الحاسبة (الجزء الرابع)

بعد أن تعرفنا على عنصر الأزرار نستطيع الآن إكمال تطبيقنا ونضعها بداخل الإطار السفلي للآلة، نذكّر بالشكل التالي التخيلي للآلة الذي بدأنا منه فكرة ترتيب العناصر:

008_GUI_calculater.jpg

واجهة الآلة الحاسبة

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

009_GUI4_calculator3.jpg

واجهة الآلة الحاسبة الثانية

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

بدلًا من أن نعيد كتابة الخصائص وتكرارها لكل زرار نستطيع إنشاء قاموس يحتوي على كل خاصية وقيمتها المطلوبة للأزرار المتشابهه ونستخدمه وقت الإنشاء، فنضع قاموسًا لأزرار العمليات سنسميه operators وقاموسًا للأرقام سنسميه numbers على النحو التالي:

numbers={'fg':"white",'width':10,'height':3,'bd':2,'bg':"#007CFF",
'cursor':"hand2"}
operators={'fg': "black","width": 10,"height":3,"bd": 2, "bg":"#eee","cursor":"hand2"}

طبعًا لإنشاء الأزرار، سبق وذكرنا خلال الدرس أننا نحتاج لتكوين كائن من الصنف Button ونحدد خصائصه عند إنشائه، لذا سننشئ على سبيل المثال الزر رقم 7 فتكون الشفرة باستخدام القاموس على النحو التالي:

seven = Button(btns_frame, text = "7",**numbers)

وسنراعي عند وضع الأزرار على الشبكة ترتيب أماكنها، فالزر رقم سبعة هنا سيكون في الصف الثاني الذي يحمل الرقم 1 (لأن الترتيب يبدأ من صفر) والعمود الأول الذي يحمل الرقم 0، على النحو التالي:

seven.grid(row = 1, column = 0)

وهكذا نقوم بوضع جميع الأزرار على الشبكة، من خلال الشكل نرى الزر الذي يحمل الحرف (C) وهو لمحو شاشة الإدخال والزر الذي يحمل الرقم 0 مختلفين قليلًا في الأبعاد عن بقية الأزرار الأخرى، فالرمز (C) يحجز مساحة ثلاثة أزرار والرقم صفر يحجز مساحة زرين، فنحتاج أن نكتب خصائصهما بشكل كامل بشكل منفصل عن البقية لأن فيهما اختلافًا عنهم في خاصية العرض، على النحو التالي:

clear = Button(btns_frame, text = "C", fg = "black", width = 32, height = 3,
               bd = 0, bg = "#eee", cursor = "hand2"( 
zero = Button(btns_frame, text = "0", fg = "white",
              width = 21, height = 3, bd = 2, bg = "#007CFF", cursor = "hand2")

وعند وضعهم على الشبكة نحتاج لاستخدام خاصية columnspan والتي تحدد كم عدد الأعمدة التي سيشغلها هذا الزرار فتكون قيمتها عند الرمز (C) القيمة 3 وقيمتها عند الصفر 2 بالشكل التالي:

clear.grid(row = 0, column = 0, columnspan = 3)
zero.grid(row = 4, column = 0, columnspan = 2)

يتبقى لنا الآن كتابة شفرات الدوال التي تتعامل مع الأزرار عند النقر عليها، فنحتاج لثلاثة دوال الأولى لتمسح محتوى صندوق الإدخال عند النقر على الرمز (C) وسنعطيها اسم bt_clear والثانية لتضيف محتوى الأزرار (الرقم أو العملية) على مربع الإدخال سنسميها btn_click، ونربط كل زرار بالدالة المناسبة باستخدام الأمر command وسنأخذ مثالًا على الزر رقم 4:

four = Button(btns_frame, text = "4",**numbers,command = lambda: btn_click(4))

ونكمل على هذا المنوال مع بقية الأزرار.

أما الدالة الثالثة مرتبطة بالنقر على علامة المساواة = سنسميها bt_equal وهي تقوم بتنفيذ العملية الحسابية التي أدخلها المستخدم وتعرض النتيجة في مربع الإدخال، وسنضيف لها جزءًا بسيطًا في حال كانت العملية المدخلة خاطئة، فنستخدم مربعات الرسائل لنُظهر رسالة خطأ عند عدم القدرة على تنفيذ العملية المدخلة:

showerror( title='خطأ',message='العملية الحسابية غير صحيحة')

الكود بالكامل سيكون على النحو التالي:

from tkinter import *
from tkinter.messagebox import *


from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)


mycalculator = Tk()
mycalculator.title("آلة حاسبة بسيطة")
mycalculator.geometry("575x740")  
mycalculator.resizable(0, 0) 


input_frame = LabelFrame(mycalculator,text="المدخلات ",bd=3,width=550)
input_frame.pack(side=TOP)

btns_frame = LabelFrame(mycalculator,text=" لوحة الأرقام والعمليات ",
                        bg="white",width=550,bd=3)
btns_frame.pack()


expression = ""
input_text = StringVar()

def bt_clear(): 
    global expression 
    expression = "" 
    input_text.set("")


def btn_click(item):
    global expression
    expression = expression + str(item)
    input_text.set(expression)


def bt_equal():
    try:
          global expression
          result = str(eval(expression))
          input_text.set(result)
          expression = ""
    except Exception :
           showerror( title='خطأ',message='العملية الحسابية غير صحيحة')



input_label=Label(input_frame,text="أدخل المعادلة الحسابية ",bg="#007CFF",fg="white")
input_label.grid(row=0, column=1,ipady=10)
input_field = Entry(input_frame, width=18,
                    font=('arial', 12, 'bold'),textvariable=input_text)
input_field.grid(row=0, column=0,ipady=10)

numbers = {'fg':"white",'width':10,'height':3,'bd':2,'bg':"#007CFF",'cursor':"hand2"}
operators={'fg': "black","width": 10,"height":3,"bd": 2, "bg":"#eee","cursor":"hand2"}


#السطر الأول

clear = Button(btns_frame, text = "C",fg = "black", width = 32, height = 3,
               bd = 2, bg = "#eee", cursor = "hand2",command =bt_clear)
clear.grid(row = 0, column = 0,columnspan = 3)
divide = Button(btns_frame, text = " / ", **operators,command = lambda: btn_click("/"))
divide.grid(row = 0, column = 3)

###السطر الثاني

seven = Button(btns_frame, text = "7",**numbers,command = lambda: btn_click(7))
seven.grid(row = 1, column = 0)

eight = Button(btns_frame, text = "8",**numbers,command = lambda: btn_click(8))
eight.grid(row = 1, column = 1)

nine = Button(btns_frame, text = "9",**numbers,command = lambda: btn_click(9))
nine.grid(row = 1, column = 2,)

multiply = Button(btns_frame, text = " * ", **operators,command = lambda: btn_click("*"))
multiply.grid(row = 1, column = 3)

#السطر الثالث

four = Button(btns_frame, text = "4",**numbers,command = lambda: btn_click(4))
four.grid(row = 2, column = 0)

five = Button(btns_frame, text = "5",**numbers,command = lambda: btn_click(5))
five.grid(row = 2, column = 1)

six = Button(btns_frame, text = "6",**numbers,command = lambda: btn_click(6))
six.grid(row = 2, column = 2)

minus = Button(btns_frame, text = "-",**operators,command = lambda: btn_click("-"))
minus.grid(row = 2, column = 3)

#السطر الرابع
one = Button(btns_frame, text = "1",**numbers, command = lambda: btn_click(1))
one.grid(row = 3, column = 0)

two = Button(btns_frame, text = "2", **numbers ,command = lambda: btn_click(2))
two.grid(row = 3, column = 1)

three = Button(btns_frame, text = "3",  **numbers , command = lambda: btn_click(3))
three.grid(row = 3, column = 2)

plus = Button(btns_frame, text = "+",**operators,command = lambda: btn_click("+"))
plus.grid(row = 3, column = 3)

#السطر الخامس
zero = Button(btns_frame, text = "0", fg = "white",
              width = 21, height = 3, bd = 2, bg = "#007CFF", cursor = "hand2",
              command = lambda: btn_click(0))
zero.grid(row = 4, column = 0, columnspan = 2)

point = Button(btns_frame, text = ".",**numbers,command = lambda: btn_click("."))
point.grid(row = 4, column = 2)

equals = Button(btns_frame, text = "=", **operators,command = lambda: bt_equal())
equals.grid(row = 4, column = 3)

mycalculator.mainloop()

وبهذا نكون شبه أكملنا تطبيق الآلة، وسنضيف لها قائمة رئيسية لعرض معلومات عنها، وهذا ما سنتعرف عليه في الدرس القادم من هذه السلسلة.

المصادر

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...