مكتبة wx لإنشاء الكائنات الرسومية

مكتبة wx
حياكم الله زوار موقعنا

في هذه الصفحة تُتابِعون بعض الشروحات والنماذج في إنشاء الكائنات الرسومية في لغة البرمجة بايثون باستخدام المكتبة wx.
يُمكنُكم التنقل بين الدروس بالرقم 1 والتنقل بين النماذج بالرقم 2.
وقبل أن نبدأ تذَكَروا دائما الأستاذ صلاح البرعصي من صالِحِ دُعائِكُم.
نسأل الله العظيم رب العرش الكريم أن يبارِكَ له في أولادِهِ وأهلِهِ وماله ويجعلَهُ من عبادِهِ المُخلَصين.

تثبيت المكتبة وإنشاء الكلاس:


بسم الله الرحمن الرحيم.
سنتحدث في هذه الأَسطُر عن مكتبة wx لإنشاء الكائنات الرسومية.

في البداية نقوم بتثبيت مكتبة wx عن طريق كتابة هذا الأمر بعد فتح سطر الأوامر cmd
pip install wxPython

#الآن نتعلم إنشاء أول شيء في أي برنامج وهو الإطار، والإطار هذا هو الشيء الذي يحتوي على الكائنات التي نستخدمها من أزرار ومربعات تحرير وقوائم وما إلى ذلك.
#أول شيء نكتبه استيراد المكتبة هكذا:
import wx

#إنشاء الكلاس
class test(wx.Frame):
#test هذا اسم الكلاس الجديد الذي يرث من كلاس wx.Frame ويمكن تسميته أي شيء.
#بعد ذلك نكتب دالة الإنشاء التي تعمل مباشرةً بعد تشغيل البرنامج
#وطبعا نسبقها بتثليم مناسِب.
	def __init__(self):
		super(test,self).__init__(None,-1, title= 'إطار فارغ')
#None وتعني الكائن الحاوي وهنا لا يوجد شيء نضع عليه الإطار لأنه أول شيء، المعامل الثاني -1 وهو رقم الهوية يمكن كتابة أي رقم بشرط عدم تكراره مع أي كائن آخر والأفضل -1 لعدم الوقوع في الخطأ أو النسيان، 
#title وهو عنوان الإطار نكتب فيه اسم البرنامج.
		self.Show()
#أخيرا هذا كود إظهار الإطار.


app= wx.App()
#كود إنشاء تطبيق wx
test()
#تشغيل الإطار.
app.MainLoop()
#الحلقة الرئيسية لبقاء البرنامج يعمل دائما بلا توقف حتى يغلقه المستخدِم.

ونلتقي قريبا بمشيئةِ الرحمن.
لا تنسوا ذِكرَ الله.

نموذج إطار فارغ


import wx

class test(wx.Frame):
	def __init__(self):
		super(test,self).__init__(None,-1, title= 'إطار فارغ')
		self.Show()

app= wx.App()
test()
app.MainLoop()

الأزرار wx.Button


في هذا الدرس سنتعلم كيفية إنشاء زر وربطه بِحدَث، والمقصود بالحدَث هو الشيء الذي يحدث عند الضغط على هذا الزر.
سنعمل على نفس الإطار الذي أنشأناه سابِقا.
import wx

class test(wx.Frame):
def __init__(self):
super(test,self).__init__(None,-1, title= 'إطار فارغ')
الآن سنُنشِئ لائحة وهي كائن آخر يوضع على الإطار ليحتوي على الكائنات الصغيرة.
p = wx.Panel(self,-1)
p: وهو اسم المتغير الذي يُشير إلى اللائحة لكي نستخدمه لاحقا في وضع الأزرار عليه.
self وهو ما يرمز للكِلاس هذا أي نضعها على الإطار وناقص 1 طبعا هو رقم الهوية.
الآن ننشِئ الزر هكذا:
self.b1 = wx.Button(p,-1,'ok')
تم إنشاء الزر،	self.b1: المتغير الذي يُشير إلى الزر.
p وهو اللائحة التي وضعنا عليها الزر -1 رقم الهوية،
أما ok فهو اسم الزر الذي سيظهر للمُستخدِم
ويمكننا تغييره لاحقا إذا كان زرا واحدا يقوم بأكثر من شيء مثل الإيقاف والاستماع أو إرسال شيء ونسخ نتيجته.
ويكون تغييره هكذا:
self.b1.SetLabel("الاسم الجديد")
أي أنهُ SetLabel تغير اسم الزر
أما GetLabel() نحصل من خلالِها على اسم الزر وطبعا نسبقها باسم المتغير الخاص بالزر الذي نريد معرفة اسمه.
بعد ذلك نربط الزر بدالة الحدَث هكذا:
self.b1.Bind(wx.EVT_BUTTON, self.Exit)
Exit هنا هو اسم الدالة التي سنكتبُها لاحقا يمكننا كتابة أي اسم طبعا بشرط أن نتذكره عند كتابة الدالة ونكتبه كما هو بأحرف كبيرة أو صغيرة يعني كما كتبناه هنا.
self.Show()
كود إظهار الإطار

الآن نكتب دالة الحدث الخاصة بالزر
def Exit (self, event):
self.Close()
واضحة بها أمر واحد فقط وهو إغلاق الإطار.
وإلى لقاءٍ آخر إن شاء الله.
لا تنسَوا ذِكْرَ الله.

نموذج به زر واحد


import wx

class test(wx.Frame):
	def __init__(self):
		super(test,self).__init__(None,-1, title= 'إطار به زر واحد')
		p = wx.Panel(self,-1)
		self.b1 = wx.Button(p,-1,'ok')
		self.b1.Bind(wx.EVT_BUTTON, self.Exit)
		self.Show()

	def Exit (self, event):
		self.Close()

app= wx.App()
test()
app.MainLoop()

نموذج آخر على الأزرار، plus and minus buttons

import wx

#نموذج على الأزرار بإنشاء زرين أحدهما يزيد عنوان الإطار 1 والثاني ينقصه 1.
class frame(wx.Frame):
	def __init__(self, parent):
		super(frame,self).__init__(parent,-1, title= 'test1')
		self.counter= 1
		p = wx.Panel(self,-1)
		self.b1 = wx.Button(p,-1, u'plus')
		self.b1.Bind(wx.EVT_BUTTON, self.onPlus)
		self.b2= wx.Button(p,-1, u'minus')
		self.b2.Bind(wx.EVT_BUTTON,self.onMinus)
		self.Show()

	def onPlus(self, event):
		self.counter=self.counter+1
		self.SetTitle('test%d'% self.counter)

	def onMinus(self, event):
		self.counter= self.counter-1
		self.SetTitle('test%d' %self.counter)

app= wx.App()
frame(None)
app.MainLoop()

مربعات التحرير wx.TextCtrl


مرحبا بِكم من جديد.
سنتحدث في هذا الدرس عن مربعات التحرير وسنعمل على إطارُنا المعهود.

import wx

class test(wx.Frame):
	def __init__(self):
		super(test,self).__init__(None,-1, title= 'إطار به مربعات تحرير وأزرار')
		p = wx.Panel(self,-1)
		self.tc1 = wx.TextCtrl(p, -1)
		self.tc2 = wx.TextCtrl(p, -1)
#أنشأنا مربعين للكتابة.
		self.b1 = wx.Button(p,1,'beep')
#الآن يمكننا جعل هذا الزر يعمل افتراضيا بالضغط على أي مكان في الإطار يعني حتى إذا لم يكن التركيز عليه وكان على أحد مربعات الكتابة مثلا فإنه يعمل ويمكننا فعل ذلك بأحد الطريقتين:
#إما أن نجعل رقم الهوية الخاص به 1 وليس ناقص 1.
#وفي هذه الحالة سنكتب رقم الid في معاملات كود الربط.
#والطريقة الثانية أن نكتب اسم المتغير الخاص بالزر ونلحقه ب SetDefault() يعني هكذا
		self.b1.SetDefault()
		self.Bind(wx.EVT_BUTTON, self.Beep, id=1)
وأنشأنا أيضا هذا الزر وربطناه بِحدَث البيب وفكرة هذا النموذج هي كتابة قيم البيب في المربعات وربطها بالزر لإصدار الصوت كما سنرى لاحقا.
		self.Show()

	def Beep (self, event):
هنا اسم دالة الزر.
الآن نريد أن نحصل على القيم التي سيكتبها المستخدم، فسنضع متغير للقيمة الأولى وآخر للثانية.
	hz = self.tc1.GetValue()
قيمة المربع الأول.
	length = self.tc2.GetValue()
قيمة المربع الثاني.
الآن نريد تحويل هاتين القيمتين إلى أعداد لأنهما حاليا في نوع السلاسل النصية.
	hz = int(hz)
	length = int (length)
الآن إلى السطر الخاص بإصدار الصوت ولا ننسى في البداية قبل كل هذا أن نستورد دالة البيب من مكتبة winsound هكذا:
from winsound import Beep
Beep(hz, length)

وكما نعلم فهذه الدالة تأخذ معاملين وهم أعداد صحيحة الأول مسؤول عن حدة الصوت والثاني عن مدته أي طول زمن الصوت.
مثال:
Beep(1000, 500)

أي أن هذا السطر الأخير من الدالة يأخذ الأرقام التي سيكتبها المستخدِم ويتعامل معها.
Beep(hz, length)

app= wx.App()
test()
app.MainLoop()
مرحبا بِكم من جديد.
سنتحدث في هذا الدرس عن مربعات التحرير وسنعمل على إطارُنا المعهود.

import wx

class test(wx.Frame):
	def __init__(self):
		super(test,self).__init__(None,-1, title= 'إطار به مربعات تحرير وأزرار')
		p = wx.Panel(self,-1)
		self.tc1 = wx.TextCtrl(p, -1)
		self.tc2 = wx.TextCtrl(p, -1)
#أنشأنا مربعين للكتابة.
		self.b1 = wx.Button(p,1,'beep')
#الآن يمكننا جعل هذا الزر يعمل افتراضيا بالضغط على أي مكان في الإطار يعني حتى إذا لم يكن التركيز عليه وكان على أحد مربعات الكتابة مثلا فإنه يعمل ويمكننا فعل ذلك بأحد الطريقتين:
#إما أن نجعل رقم الهوية الخاص به 1 وليس ناقص 1.
#وفي هذه الحالة سنكتب رقم الid في مععاملات كود الربط.
#والطريقة الثانية أن نكتب اسم المتغير الخاص بالزر ونلحقه ب SetDefault() يعني هكذا
		self.b1.SetDefault()
		self.Bind(wx.EVT_BUTTON, self.Beep, id=1)
#وأنشأنا أيضا هذا الزر وربطناه بِحدَث البيب وفكرة هذا النموذج هي كتابة قيم البيب في المربعات وربطها بالزر لإصدار الصوت كما سنرى لاحقا.
		self.Show()

	def Beep (self, event):
#هنا اسم دالة الزر.
#الآن نريد أن نحصل على القيم التي سيكتبها المستخدم، فسنضع متغير للقيمة الأولى وآخر للثانية.
		hz = self.tc1.GetValue()
#قيمة المربع الأول.
		length = self.tc2.GetValue()
#قيمة المربع الثاني.
#الآن نريد تحويل هاتين القيمتين إلى أعداد لأنهما حاليا في نوع السلاسل النصية.
		hz = int(hz)
		length = int (length)
#الآن إلى السطر الخاص بإصدار الصوت ولا ننسى في البداية قبل كل هذا أن نستورد دالة البيب من مكتبة winsound هكذا:
from winsound import Beep
		Beep(hz, length)

#وكما نعلم فهذه الدالة تأخذ معاملين وهم أعداد صحيحة الأول مسؤول عن حدة الصوت والثاني عن مدته أي طول زمن الصوت.
مثا#ل:
#Beep(1000, 500)

#أي أن هذا السطر الأخير من الدالة يأخذ الأرقام التي سيكتبها المستخدِم ويتعامل معها.
#Beep(hz, length)

app= wx.App()
test()
app.MainLoop()

نموذج مربعات التحرير


import wx
from winsound import Beep

class test(wx.Frame):
	def __init__(self):
		super(test,self).__init__(None,-1, title= 'beep')
		p = wx.Panel(self,-1)
		self.tc1 = wx.TextCtrl(p, -1)
		self.tc2 = wx.TextCtrl(p, -1)

		self.b1 = wx.Button(p,1,'beep')
		self.Bind(wx.EVT_BUTTON, self.Beep, id=1)
		self.Show()

	def Beep (self, event):
		hz = self.tc1.GetValue()
		length = self.tc2.GetValue()
		hz = int(hz)
		length = int (length)
		Beep(hz, length)

app= wx.App()
test()
app.MainLoop()

بعض الأُمور المتعلِقة بمربعات التحرير


السلام عليكم ورحمة الله وبركاته.
طابت أوقاتُكُم بذِكْرِ الله.

في هذا الدرس سنتناول بعض الأوامر المتعلِقة بمربعات التحرير.
كما رأينا في النموذج السابق كانت المربعات بدون عنوان أو اسم يُعرِفُها ولعمل ذلك نستخدم شيء اسمه النص الثابت ونضعه فوق مربع التحرير في دالة البِناء، ويكون هكذا:
self.st1 = wx.StaticText(p, -1, 'اكُْتُب الرقم الأول')
وبالإمكان عدم إسناده إلى متغير إذا كنا لا نحتاج إلى إخفائه أو تغييره لاحقا.
لإخفائه نكتب:
self.st1.Hide()
ويمكننا فعل ذلك مع أي كائن سواء زر أو مربع تحرير أو أي كائن آخر لم نتحدث عنه بعد.
لجعل التركيز ينتقل لمربع التحرير أو أي كائن نستخدِم
SetFocus()
طبعا اسم المتغير الخاص بالكائن في البداية وبعده نقطة.
لتحويل مربع التحرير إلى مربع للقراءة فقط
SetEditable(False)
ولإرجاعه للوضع الطبيعي:
SetEditable(True)

وكما أنشأنا زر وربطناه بما يتم كتابته في مربعات التحرير فإنهُ بإمكاننا عدم فعل ذلك والضغط إنتر فقط لإتمام ما نريده باستخدام دالة الحدَث التالية:
self.tc1 = wx.TextCtrl(p, style=wx.TE_PROCESS_ENTER)
self.tc1.Bind(wx.EVT_TEXT_ENTER, self.Anser, id = self.tc1.GetId())
ننتبه هنا إلى نوع نمط المربع وهو style=wx.TE_PROCESS_ENTER 
وهذا المعامل الثالث نكتبه بعد رقم الهوية.

أيضا فإنه يوجد حدَث آخر للربط يمكننا من عمل دالة تراقب الذي يتم كتابته مباشرةً بمجرد كتابته دون الضغط على أي شيء وتكون هكذا:
self.tc1.Bind(wx.EVT_TEXT, self.Sound, id = self.tc1.GetId())
ما بعد self. هو اسم الدالة ونكتب فيها مثلا مسار الملف الصوتي وتشغيله.
وهذه نستخدمها لإصدار صوت مثلا أثناء الكتابة أو مراقبة إجابة سؤال ما.

يمكننا أيضا جعل المربع للقراءة فقط بطريقة أخرى وهو جعل النمط هكذا 
style=16
وهذا يكون سطر واحد
أما إذا أردنا جعله متعدد الأسطُر نجعله هكذا:
style=176
ولجعل نص ما يكون داخل المربع نستخدم الوظيفة التالية:
SetValue("النص")

وإلى لقاءٍ آخر.
إلى ذلك الوقت لا تنسوا ذِكْرَ الله.
اللهم علِمنا ما ينفعنا وانفعنا بما علَمْتَنا.

نموذج آخر على مربعات التحرير والأزرار


الكاتب سُلَيْمان القسيمي.
# مثال عملي على واجهة رسومية بمربعين للكتابة وزر للتنفيذ في بايثون

import wx
#استيراد مكتبة wx

class form(wx.Frame):
#عمل كلاس للنافذة وقد سميناه form , وهو يأخذ وظائف وخصائصه من كلاس wx.Frame 
	def __init__(self):
# تعريف دالة __init__ للكلاس وهي الدالة التي نقوم فيها بإعداد النافذة من ناحية تعيين الاسم والحجم وإضافة العناصر إليها, ويتم تنفيذ دالة __init__ تلقائيًا بمجرد تنفيذ الكلاس
		wx.Frame.__init__(self,parent=None,title="نافذة تجريبية")
# استخدام ال __init__ function للكلاس wx.Frame الذي قمنا بتضمينه داخل كلاسنا, ونحن نقوم بذلك لكي نسمح لكلاسنا أن يستعمل جميع الإعدادات الموجودة داخل كلاس wx.Frame , ففي البداية كتبنا داخل الدالة __init__ كلمة self لبيان أن هذه الدالة مندرجة تحت الكلاس , والإعدادا الثاني هو ال parent , ويعني النافذة الحاوية لنافذتنا
# وبما أن نافذتنا هذه ليس لها حاوي بحكم أنها هي النافذة الرئيسية والوحيدة في البرنامج فقد جعلنا قيمة parent تساوي None أي لا شيء 
# أما الإعداد الثالث فهو title , وهو يساوي العنوان الذي نريده للنافذة
		panel = wx.Panel(self)
# اللائحة أو ال panel في نوافذ wx هي حاويات تكون موضوعة داخل النافذة الرئيسية ونقوم فيها بإضافة جميع العناصر التي نريد
# إذًا فنحن قد أنشأنا متغيرًا أسميناه panel وجعلنا قيمته لائحة جديدة مندرجة تحت النافذة الرئيسية
# ال method Panel من المكتبة self هي المسؤولة عن إنشاء اللائحات , وهنا تستقبل ال method معامل واحد وهو ال parent , وقد قمنا بالتعويض عنه بكلمة self أي النافذة الرئيسية
# وفي أي مكان نستخدم فيه كلمة self داخل هذا الكلاس يعني ذلك أننا نشير إلى هذا الكلاس أو بمعنى أدق هذه النافذة
		wx.StaticText(panel,-1,"الاسم الأول: ")
# هذا نص ثابت قمنا بوضعه قبل ال edit box الأول من أجل أن يقرأ لنا الناطق الغرض من ال edit box عند الوقوف عليه أو المرور عليه بزر التاب
# نرى أن لدينا 3 معاملات للكلاس StaticText , الأول هو الحاوي لهذا العنصر
# يجب الاتفاق إلى أن جميع العناصر في wx يجب أن يكون لها حاوي , ويعوض عنه عادةً في البراميتر الأول
# إن الحاوي لجميع عناصرنا لهذه النافذة ستكون اللائحة , فإذًا عوضنا عنها باسم المتغير الخاص بها وهو panel
# المعامل الثاني هو ال id , وهو ضروري جدًا ليتم تمييز العناصر عن بعضها , ولا تحتاج أنت كمبرمج إلى هذا ال معرف, لذا فعوض عنه بالرقم -1 لكي يقوم wx تلقائيًا باختيار ال id للعنصر
		self.firstName = wx.TextCtrl(panel,-1,value="")
#نأتي إلى أول خانة للكتابة, سنلاحظ أننا كتبنا كلمة self. قبل اسم المتغير, وهذا ضروري لكي يكون المتغير عام ويمكن استخدامه حتى خارج دالة init
# كالعادة نجد أن الخانة الأولى من العنصر قد تم التعويض عنها بالحاوي وهو اللائحة التي أسميناها panel 
# أما الخانة الثانية فهي كذلك ثابتة كالعادة وهي المعرف id وقد عوضنا عنها بالرقم -1
# الإعداد الثالث هو ال value , أي محتوى المربع النصي , ويمكننا ترك قيمة نصية فارغة
		wx.StaticText(panel,-1,"الاسم الأخير: ")
		self.lastName = wx.TextCtrl(panel,-1,value="")
# السطرين السابقين قمنا فيهما بنفس فكرة إنشاء النص الثابت وال edit box الأول , مع تغيير النص الثابت إلى "الاسم الأخير" وإنشاء متغير جديد لل lastName وهو كذلك يمكن استدعاؤه خارج دالة init لوجود الكلمة self قبله
		submit = wx.Button(panel,-1,"إرسال")
# هذا هو الزر الذي سيقوم بعرض الرسالة المحتوية على رسالة ترحيبية للاسم المكتوب
# في البداية نوضح بعض الأمور
# النقطة الأولى نلاحظ أن المتغير الذي أنشأنا فيه الزر لا يحتوي على كلمة self , وسبب ذلك هو أننا لا نحتاج إلى إجراء أي تغيير أو استدعاء للزر خارج دالة __init__ لذا فإن المتغير سيبقى محليًا فقط داخل دالة	__init__
# الأمر الآخر هو أننا لم ننشئ نصًا ثابتًا أو StaticText قبل إنشاء الزر بسبب أن الزر يحتوي من نفسه على إعداد لتعيين الاسم
# في البداية وكعادة جميع عناصر wx الخانة الأولى تمثل الحاوي واتفقنا أن الحاوي هو اللائحة المدعية ب panel
# الأمر ذاته ينطبق على المعامل الثاني الذي يشير إلى المعرف id وهو ثابت أيضًا
# أما الخانة الثالثة فهي ال label أو المسمى المظهري لهذا الزر , وقد اخترنا كلمة إرسال لتمثل اسم لهذا الزر 
		submit.Bind(wx.EVT_BUTTON,self.onSubmit)
# في هذا السطر سنقوم بربط زر الإرسال بدالة يتم تنفيذها بمجرد الضغط على الزر
# استخدمنا لهذا الغرض وظيفة Bind التي تستقبل معاملين
# المعامل الأول هو نوع الحدث , سنعوض فيه ب wx.EVT_BUTTON , أي حدث النقر على الزر
# أما الخانة الثانية فنكتب فيها اسم الدالة المراد تنفيذها حال الضغط على الزر
# لقد أنشأنا أسفل هذا الكود بقليل وداخل الكلاس دالة باسم onSubmit ووضعنا فيها الأوامر المطلوب تنفيذها
# لذا فسنكتب في هذه الخانة اسم هذه الدالة
# لكن قبل ذلك علينا أن نعلم أن هذه الدالة موجودة داخل هذا الكلاس , مما يعني أنه يجب كتابة كلمة self. قبل كتابة اسم الدالة
# أي نكتبها هكذا self.onSubmit
		self.Show()
# وظيفة السطر السابق ببساطة هو إظهار النافذة بعد إعدادها , فكما قلنا سابقًا كلمة self تشير دائمًا إلى النافذة نفسها

	def onSubmit(self,event):
# هذه هي دالة onSubmit التي أخبرنا الزر أن يقوم بتنفيذها حال الضغط عليه
# لقد وضعنا معاملين للزر, الأول هو self الذي يجب وضعه دائمًا طالما أن الدالة هذه موجودة داخل كلاس
# أما المعامل الثالث فقد أسميناه event وهو ضروري أيضًا لكي ينتظر حدث الضغط على الزر المرتبط بالدالة لتتنفذ
# داخل أكواد الدالة لن نتحدث عن هذين المعاملين ولن نستخدمهما 
		firstName = self.firstName.GetValue()
# هنا قمنا بإنشاء متغير محلي داخل الدالة أسميناه firstName , وهو يختلف عن المتغير self.firstName الذي كان يمثل ال edit box الخاص بالاسم الأول
# فالمتغير self.firstName يمكن استدعاؤه داخل الكلاس كاملًأ أما المتغير firstName فهو خاص بالدالة هذه فقط ويتم حذفه بعد تنفيذها
# لقد قمنا في هذا السطر باستدعاء النص الذي كتبه المستخدم داخل المربع النصي المخزن في المتغير self.firstName , وقد استخدمنا لفعل ذلك الوظيفة GetValue
# القيمة التي ترجع بها الوظيفة GetValue يتم تخزينها في المتغير المحلي firstName
		lastName = self.lastName.GetValue()
# نفس الطريقة مع خانة الاسم الأخير
		fullName = firstName+" "+lastName
# هنا أنشأنا متغيرًا أسميناه fullName أي الاسم الكامل , وهو يساوي الاسم الأول زائد مسافة فاصلة زائد الاسم الأخير
		wx.MessageBox("أهلًا بك عزيزي "+fullName+" في هذا البرنامج","ترحيب")
# أخيرًا قمنا بإنشاء message box قمنا فيها بالترحيب بصاحب الاسم بعد جمع اسمه واسم عائلته 
# يحتوي كود ال message box على معاملين اثنين رئيسيَين, الأول هو متن الرسالة , والثاني عنوان الرسالة.

app = wx.App()
# نحن الآن خارج الكلاس , وقد أنشأنا كائن app ليتم تشغيل جميع الأكواد السابقة , فبدون هذا الكائن لا يمكن لنوافذ wx أن تعمل
form()
# هنا قمنا بتنفيذ كلاسنا الذي أنشأناه وأنشأنا وظائفه في أعلى الكود
# يجدر القول هنا أن السطر السابق سيقوم بتنفيذ دالة __init__ التي قمنا بتضمين الأوامر المبدئية لإنشاء النافذة والعناصر وربط العناصر بالأحداث.
app.MainLoop()
# وهذا السطر الأخير مسؤول عن جعل النافذة تعمل دون توقف إلى أن يقوم المستخدم بإغلاقها

رسائل المحاورة wx.MessageDialog


السلام عليكم ورحمة الله وبركاته.
طابت أوقاتُكُم بذِكْرِ الله.
في هذا الدرس سنتحدث عن محاورة wx.GetTextFromUser ويبدو لنا من اسمِها أنها تأخذ نص من المستخدِم
إنها كذلك بالفعل، محاورة صغيرة بها مربع كتابة وزرين أحدهما للموافقة يتم من خلاله إجراء أي عملية والآخر هو زر الإلغاء، وزر الموافقة يكون افتراضي مُفَعل يعني إذا ضغطنا إنتر ونحن في مكان الكتابة سيتم تفعيل زر الموافقة.
وهذه لا تحتاج إلى إنشاء إطار لكن تحتاج إلى إنشاء تطبيق wx هيا لنرى كيف نكتبها:
import wx
app=wx.App()
وهذا السطر يمكننا كتابته في بداية الكود أو آخره لا فرق.
name= wx.GetTextFromUser(هنا بين القوسين نحتاج لكتابة معاملين الأول وهو النص الذي يقول للمستخدِم ماذا يفعل كأن نقول له اكتُب اسمك أو أدخِل القيمة التي تريد تحويلها إذا كان تطبيق لتحويل العملة مثلا، المعامِل الثاني اسم المحاورة)
وننتبِه إلى الأحرُف الكبيرة من بداية كل كلمة.

أما الآن فهيَا بنا نتعرف على الرسائل الحوارية wx.MessageDialog
هذه الرسائل تُشبِه الmessagebox ولكن بها عدة أشكال كرسالة الخطأ أو رسالة السؤال ويمكن أن تتضمن زرًا واحدا أو 2 أو 3.
نتعرف أولا على wx.MessageBox
wx.MessageBox("تمت العملية بنجاح", "معلومات")
كما نلاحظ تحتوي على معاملين نص الرسالة وعنوانها.
رسالة السؤال:
q= wx.MessageDialog(self, طبعا self هذه إذا كنا نعمل على إطار فهذا هو الأب أي الكائن الحاوي أما إن كان العمل بدون إطار نكتب None، "هل تريد اللعب مرةً أخرى؟", كما نرى نص السؤال، " ", هذا من المُفترض نكتب فيه عنوان المحاورة وبالإمكان تركها فارغة، wx.YES_NO هنا حددنا الأزرار التي ستظهر | wx.ICON_QUESTION) هذا نوع المحاورة أي سؤال
وليس من الضروري أن تكون أسماء الأزرار yes و no بإمكاننا تغييرها حسب الحاجة، يعني مثلا إذا كان السؤال "اختَر النمط الذي تَوَد لعبه"، نغير أسماء الأزرار هكذا:
q.SetYesNoLabels("المستوى السهل", "المستوى الصعب")
الآن نعرف البرنامج إذا ضغط المستخدم الزر الأول ماذا يفعل أو الثاني ماذا يفعل
if q.ShowModal() == wx.ID_YES:
أي إذا ضغط على الزر المُسَمى yes حتى إن كان اسمه متغير ف yes هنا تُشير إليه أيضا،
نضع الشيء الذي نريده أن يحدُث كوضع دالة اللعب مثلا أو إغلاق البرنامج أو أي شيء.
في الأسطُر التالية نتعرف على أنواع الأزرار التي يمكن أن نضعها وشكل المحاورات.
wx.OK: هذه تعطينا زر واحد فقط وبوِسعنا تغييره كما وضحنا ذلك سابقا.
wx.OK_CANCEL: زرين وهما الموافقة والإلغاء.
وبالإمكان أيضا إضافة الزر Cancel إلى yes و no.
أنواع المحاورات:
محاورة المعلومات: wx.ICON_INFORMATION
محاورة الخطأ: wx.ICON_HAND
محاورة التحذير: wx.ICON_WARNING
ونفصل بين الأزرار التي نريدها ونوع المحاورة بعلامة |
ولإظهار الرسالة نكتب: اسم المتغير الخاص بالرسالة نلحقه بShowModal()

عزيزي القارئ: تابِع النماذج المُلحقة وسيتضح الأمر لديك أكثر.
وإلى لقاءٍ آخر.
لا تنسوا ذِكْرَ الله،
ولا تنسوا الأستاذ صلاح من دُعائكم.

نموذج على رسائل المحاورة


import wx

def Number():
	num = wx.GetTextFromUser(u"اكتب رقما صحيحا ثُم اضغط مفتاح الإدخال لنرى معاً أكان هذا الرقم فرديا أَم زوجيّاً", " ")
	try:
		num =int(num)
	except:
		error = wx.MessageDialog(None, u"من فضلِك اكتب رقما صحيحا ولا تكتُب حروف أو أي شيءٍ آخر", u'خطأ، حاوِل مرةً أخرى', wx.OK | wx.ICON_HAND)
		error.ShowModal()
		return

	if num%2==0:
		r="لقد أدخَلتَ رقما زوجيّاً"
	else:
		r="لقد أدخَلْتَ رقما فردياً"
	result= wx.MessageDialog(None, u"%s" %r, u'النتيجة', wx.OK | wx.ICON_INFORMATION)
	result.ShowModal()



app = wx.App()
Number()

نموذج آخر


import wx
import webbrowser

def Link():
	link = wx.GetTextFromUser(u"قم بلَصق الرابط الذي تَوَد فتحه ثُمَّ اضغط مفتاح الإدخال", "")
	l = webbrowser.open(link)

app = wx.App()
Link()

القوائم wx.ListBox


# اليوم سنتناول بالدراسة كائن رسومي جديد نضيفه إلى ما درسناه سابقاً 
ألا وهو القائمة (list box), طبعاً الدراسة ستتم في ضوء مكتبة wx والتي يشكل كلاس listbox كائن مهم من كائناتها الرسومية,
عليك أن تعرف أنك حين تكتب القائمة لن تحتاج إلى تعلم الكثير حول هذا الأمر فبكل بساطة ستكتب ما تعودّتَ على كتابته في اللائحة والأزرار.

import wx
إلى الإطار:
class test(wx.Frame):
def __init__(self):
super(test,self).__init__(None,-1, title= 'نموذج على القوائم')
هنا نضيف كائن القائمة هكذا:
self.lb = wx.ListBox(p,-1)
كالعادة وضعناها على اللائحة وأعطيناها رقم هوية، ولكن هي الآن فارغة لا تحتوي أية عناصر ويمكننا إضافة العناصر بإحدى الطريقتين التاليتين:
إما أن نكتب ضمن معاملات إنشاء القائمة أي بعد رقم الهوية، نكتب فاصلة ثم
choices=[]
طبعا بين الأقواس المربعة نكتب العناصر التي نريدها ككتابة أي قائمة.
الطريقة الثانية: أن نسند قائمة عادية إلى متغير ثم نستخدم هذه الوظيفة
self.lb.Set()
بين القوسين نكتب اسم المتغير.
وهذه الوظيفة تُفيدُنا كثيرا إذا ما كان برنامجنا يحتوي على أكثر من قائمة فإنهُ عند الضغط على أحد العناصر تفتح لنا قائمة جديدة هنا نقول عند الضغط على هذا العنصر غير عناصر القائمة واجعلها كذا وكذا.
كيف نربِط كل عنصر بحَدَث؟
علينا أن نعرف أولا كيف نعرف أي العناصر عليه التركيز، وذلك عن طريق الكود التالي:
lb.GetSelection()
وهذه الوظيفة ترجع لنا رقم العنصر المحدد
يعني العنصر الأول في القائمة يساوي 0 والثاني 1 وهكذا
وإذا لم يكن أي عنصر محدد فستعطينا -1
لنقل التركيز نستخدم
SetSelection()
بين القوسين نكتب رقم العنصر.
ولمعرفة السلسلة النصية للعنصر أي اسمه:
GetString(2)
على سبيل المثال إن كنا نريد معرفة السلسلة النصية للعنصر الثالث في القائمة
أو GetStringSelection()
لمعرفة اسم العنصر المحدد.
ولمعرفة السلاسل النصية لكل العناصر نستخدم:
GetStrings()
لإضافة عنصر جديد في آخر القائمة:
Append()
اسم العنصر في سلسلة نصية بين القوسين.
لإدراج عنصر في مكان محدد
Insert()
بين القوسين نكتب اسم العنصر ورقمه هكذا:
lb.Insert("الملِكة", 3)
أي أنه العنصر الرابع سيكون اسمه الملِكة.
لحذف عنصر نستخدِم:
Delete(1)
لمعرفة عدد العناصر:
GetCount()
لمعرفة حالة القائمة إذا كانت فارغة أو لا
lb.IsEmpty()
إذا كانت فارغة فستُرجِع True وإذا كانت غير ذلك فستُرجِع False.
لحذف جميع العناصر:
Clear()
للبحث عن عنصر معين لحذفه مثلا
i = lb.FindString('python')
lb.Delete(i)
ولإضافة مجموعة من العناصر	نسند مجموعة من العناصر إلى متغيِر ثم نقول:
lb.Append() اسم المتغير بين القوسين.

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

اللهم علِمنا ما ينفعنا وانفعنا بما علَمْتَنا.

نموذج بسيط على القائمة


import wx

class frame(wx.Frame):
	def __init__(self, parent, id , title):
		super(frame, self).__init__(parent, id , title)
		p= wx.Panel(self, -1)
		self.listbox= wx.ListBox(p, -1, choices=['apple', 'banana', 'potato', 'fish'])
		b= wx.Button(p, 1, 'press me')
		self.Bind(wx.EVT_BUTTON, self.onPress, id =1)
		self.Show()

	def onPress(self, event):
		food= self.listbox.GetStringSelection()
		print ("I like "+food+"very much.")
# هنا تم ربط الحدَث بالزر وليس بالعنصر، ولكن يعتمد على اسم العنصر فقط، في النموذج التالي سنتعرف على طريقة ربط البيانات بالعناصر.

app= wx.App()
frame(None, -1, 'a frame with a list box')
app.MainLoop()
#و عند تنفيذ هذا الملف تظهر نافذة و فيها listBox و زر "press me"
#ال listBox فيها العناصر 'apple', 'banana', 'potato', 'fish'
#إذا إخترنا من القائمة 'banana', ثم ضغطنا على الزر أو ضغطنا إنتر لأن الزر يعمل تلقائي من أي مكان،
#نتحرك بِ alt+tab إل python terminal أو بوابةْ بايثون فنجد مكتوبا عليها 'I like banana cery much.'
#فإذا إخترنا 'fish' من القائمة مرة أخرى, ثم ضغطنا الزر
#نتحرك إل البوابة فنجد مكتوبا عليها 'I like fish cery much.'
#و هكذا.

ربط عناصر القائمة بالبيانات والأحداث


مرحبا بكم من جديد، بعد أن تعرفنا على دوال القائمة ListBox 
الآن نتعرف على طريقة ربط العناصر بالبيانات والأحداث.
لكي نربط بين عنصر ما وبياناته نستخدم:
SetClientData()
بين الأقواس نكتب رقم العنصر ثم فاصلة ثم ما نريد ربطه.
ما الذي يُمكننا ربطه بالعناصر؟
من الممكن أن نربط كل عنصر برقم هاتف وعنوان مثلا،
أو ربط كل عنصر برابط موقع ما
أو ربط كل عنصر بمسار ملف صوتي أو نصي.
فإذا كنا نريد ربط العنصر بأكثر من شيء نضعهم في قائمة بعد كتابة رقم العنصر والفاصلة يعني هكذا:
lb.SetClientData(1, ['www.google.com', 'موقع جوجل'])
هنا ربطنا العنصر رقم 1 أي العنصر الثاني في القائمة برابط واسم الموقع.
الآن كيف نحصل على البيانات التي أضفناها؟
مثلا نريد بيانات العنصر الذي عليه التركيز، إذن نتبع الخطوات التالية:
s=lb.GetSelection()
ثم نبدأ بإضافة البيانات لكل عنصر كما ذكرنا.
الآن نريد أن نحصل على بيانات العنصر:
data=lb.GetClientData(s)
وإن كان للعنصر أكثر من معلومة ونريد واحدة فقط نقول:
data[0] أو data[1]
يعني في مثالنا 0 يُمثل رابط الجوجل و1 يمثل اسم الموقع.

ننتقل إلى ربط العناصر بالأحداث ويتم هذا بإحدى الطريقتين:
إما ربط الحدَث بالعنصر عند الضغط إنتر وهذا باستخدام كود الحدَث هذا:
self.Bind(wx.EVT_MENU, self.Start)
طبعا هنا	Start هو اسم الدالة التي سنكتب فيها الذي نريده أن يحدث عند الضغط إنتر على أي عنصر.
وهذا لاستخدام زر الفأرة في الضغط:
self.lb.Bind(wx.EVT_LISTBOX_DCLICK, self.Start)
وإما أن نربط الحدث بمجرد المرور على العنصر باستخدام هذا الحدَث:
self.lb.Bind(wx.EVT_LISTBOX, self.ok)

إذا كان برنامجنا يحتوي على أكثر من وضع للقائمة أي إنه عند البدء يكون في القائمة عناصر معينة ثم تتغير عند الانتقال إلى جزء آخر من البرنامج، يعني على سبيل المثال:
عندي عنصر اسمه قائمة الكُتب وعند الضغط عليه يفتح قائمة أخرى فيها كتب التاريخ، كتب اللغة العربية، كتب أحكام التِلاوة، وهكذا وكل عنصر منهم يحتوي على قائمة أخرى بها أسماء الكتب، في هذه الحالة نحتاج إلى ربط كل مجموعة عناصر بحدَث مختلف فماذا نفعل؟
عندما نكون في القائمة الأولى نسميها على سبيل المثال القائمة الرئيسية هكذا:
lb.SetName("القائمةُ الرئيسية")
وعندما ننتقل إلى قائمة أخرى نعطيها مثلا اسم العنصر الذي ضغطنا عليه هكذا:
l= self.lb.GetStringSelection()
self.lb.SetName(l)
وللحصول على اسم القائمة نستخدم:
GetName()
ثم نضع شرط إذا كان اسم القائمة كذا افعل كذا وإذا كان اسمها كذا فافعل كذا.
if self.lb.GetName()=="القائمةُ الرئيسية":
على سبيل المثال نبدأ نضع بيانات كل عنصر إذا كنا في دالة تنفيذ الحدث بمجرد المرور على العنصر وعند الضغط إنتر نحصل على البيانات كما سنرى بشكلٍ أوضح في النماذج التوضيحية.
وهنا ننتبه لكتابة اسم القائمة كما كتبناه بالضبط يعني إذا كان فيه حركات معينة أو أحرُف كبيرة وصغيرة نكتبه كما هو في كل مرة.
والآن إلى النماذج.

مع تحيات الأستاذ صلاح البرعصي.
اللهم اجعل له بكل حرف حسنات مُضاعفة، ولكل مَن ساهم ولو بحرف واحد في هذه الدورة.

لا تنسَ ذِكْرَ الله.

نموذج على ربط عناصر القائمة بالبيانات والأحداث


import wx
import webbrowser

class frame(wx.Frame):
	def __init__(self, parent, id, title):
		super(frame, self).__init__(parent, id, title)
		p= wx.Panel(self)
		self.lb= wx.ListBox(p, -1, choices=['Google', 'Youtube', 'facebook', 'AlaqsaVoice'])
# إنشاء القائمة وما تحتويه من عناصر.
		self.lb.SetSelection(0)
#وضع التركيز على العنصر الأول عند بدء البرنامج.
		self.lb.Bind(wx.EVT_LISTBOX, self.ok)
#كود ربط الحدَث بمجرد الوقوف على العنصر.
		self.Bind(wx.EVT_MENU, self.Start)
#ربط العناصر بالضغط على الإنتر.
# الآن لدينا دالتين علينا كتابتهما لاحقا.
		self.Show()

	def ok (self, event):
#هذه الدالة الأولى التي سنربطها بالتحرُك على العناصر.
		s = self.lb.GetSelection()
#المتغير الذي يعطينا رقم العنصر الذي عليه التركيز.
#الآن نضع بيانات كل عنصر.
		self.lb.SetClientData(0, 'www.google.com')
		self.lb.SetClientData(1, 'www.youtube.com')
		self.lb.SetClientData(2, 'm.facebook.com')
		self.lb.SetClientData(3, 'www.alaqsavoice.ps')
		global data
#المتغير الذي سنأخذ منه البيانات وجعلناه عام لكي نستخدمه في الدالة التالية.
		data=self.lb.GetClientData(s)

	def Start (self, event):
		webbrowser.open(data)
#يعني عند الضغط إنتر يفتح الموقع المحدد.

app= wx.App()
frame(None, -1, 'قائمة المواقع')
app.MainLoop()

قائمة السياق contextMenu


حياكم الله وبياكم.
سنتحدث اليوم عن كائن جديد وهو قائمة السياق التي كما نعرف نصل لها بمفتاح التطبيقات الأبلكيشن ويكون بها بعض العناصر للتحكم في عنصر ما من القائمة العادية على سبيل المثال،
ويمكن ربط قائمة السياق بالقائمة أي الlistBox أو بالإطار بشكلٍ عام،
فإذا تم ربطها بالقائمة فلم تظهرإلا إذا كان التركيز على القائمة وأما إذا ربطناها بالإطار فستظهر ونحن على أي عنصر آخر.
وهذا الكائن يوضع على الإطار مباشرةً أي لا نحتاج إلى وضعه على اللائحة.
الآن كيف نضيفه؟
بالنسبة لدالة البِناء لن نحتاج لإضافة المزيد فيها، يعني سنكتُب كود ربط الحدَث فقط ونختار اسم دالة كالمعتاد لنستخدمها لاحقا في إضافة العناصر كما سنرى في النموذج التوضيحي بعد قليل.
وكود الربط هو:
wx.EVT_CONTEXT_MENU
يعني سيكون هكذا:
self.lb.Bind(wx.EVT_CONTEXT_MENU, self.اسم الدالة التي سنكتب فيها العناصر)
وإذا أردنا	ربطها بالإطار فما علينا إلا إزالة اسم الListBox يعني جعلناها self.Bind.
ثم في دالة الحدَث نُنشِئ العناصر التي نريدُها ونربطها بأحداث أخرى نكتُب دوالها لاحقا.

لا تنسَوْا أخيكُم سُلَيْمان القسيمي من دُعائكم.
وإلى لقاءٍ آخر إن شاء الله.

نموذج على قائمة السياق contextMenu


import wx
import webbrowser
from pyperclip import copy

class frame(wx.Frame):
	def __init__(self, parent, id, title):
		super(frame, self).__init__(parent, id, title)
		p= wx.Panel(self)
		self.lb= wx.ListBox(p, -1, choices=['Google', 'Youtube', 'facebook', 'AlaqsaVoice'])
# إنشاء القائمة وما تحتويه من عناصر.
		self.lb.SetSelection(0)
#وضع التركيز على العنصر الأول عند بدء البرنامج.
		self.s=self.lb.GetSelection()
		self.lb.SetClientData(0, 'www.google.com')
		self.lb.SetClientData(1, 'www.youtube.com')
		self.lb.SetClientData(2, 'm.facebook.com')
		self.lb.SetClientData(3, 'www.alaqsavoice.ps')
		self.lb.Bind(wx.EVT_LISTBOX, self.ok)
		self.Bind(wx.EVT_MENU, self.Start)
#ربط العناصر بالضغط على الإنتر.
		self.lb.Bind(wx.EVT_CONTEXT_MENU, self.Menu)
#كود ربط قائمة السياق بالقائمة العادية بحيث لا تظهر قائمة السياق إلا عندما يكون التركيز على القائمة، وإذا أردنا أن تظهر قائمة السياق في أي مكان فما علينا إلا أن نحذف اسم القائمة ونربط بين قائمة السياق والإطار بشكلٍ عام أي self.Bind.
#أما العناصر التي سنضيفها في قائمة السياق والأحداث المتعلقة بها فسيتم إضافتها لاحقا في الدالة المسمى Menu.
		self.Show()

	def ok (self, event):
		global data
		data=self.lb.GetClientData(self.s)

	def Start (self, event):
		webbrowser.open(data)
#يعني عند الضغط إنتر يفتح الموقع المحدد.

	def Menu (self, event):
		contextMenu = wx.Menu()
#كود إنشاء قائمة السياق.
		CopyLink= contextMenu.Append(-1, "نسخ رابط الموقع")
#عنصر لنسخ رابط الموقع الذي عليه التركيز.
		AddLink= contextMenu.Append(-1, "إضافة موقع جديد")
#وهذا لإضافة موقع جديد والرابط الخاص به.
		DeleteLink= contextMenu.Append(-1, "حذف الموقع المحدد")
#أما هذا للحذف.
#وإذا أردنا إضافة قائمة فرعية بها عدة عناصر أخرى
		subMenu = wx.Menu()
		item1	= subMenu.Append(-1, "item 1")
		item2	= subMenu.Append(-1, "item 2")
#ثم نضيف العناصر إلى القائمة الأساسية هكذا:
		contextMenu.Append(-1, "sub menu", subMenu)
#الآن نُنشئ روابط تربط بين العناصر التي أنشأناها والأحداث الخاصة بها.
		self.Bind(wx.EVT_MENU, self.Copy, CopyLink)
		self.Bind(wx.EVT_MENU, self.Add, AddLink)
		self.Bind(wx.EVT_MENU, self.Delete, DeleteLink)
#الآن كود عرض القائمة.
		self.PopupMenu(contextMenu)

#الآن علينا كتابة ثلاث دوال لتلك الأحداث.
	def Copy (self, event):
		copy(data)

	def Delete (self, event):
		self.lb.Delete(self.s)

	def Add (self, event):
		Link=wx.GetTextFromUser("قم بِلَصْق الرابط الذي تَوَد إضافته", "الرابط")
		Name= wx.GetTextFromUser("قم بكتابة اسم الرابط", "اسم الرابط")
		self.lb.Append(Name, Link)


app= wx.App()
frame(None, -1, 'نموذج على قائمة السياق')
app.MainLoop()

صناديق الخيارات ومربعات التحديد wx.ComboBox and wx.CheckBox


طابت أوقاتُكُم بذِكْرِ الله.
سنتحدث في الأسطُر التالية عن صناديق الخيارات ومربعات التحديد.
لصناديق الخيارات نمطين أحدهما يكون للاختيار فقط والثاني يسمح بالكتابة فيه.
وصندوق الخيارات من الكائنات التي توضع على اللائحة.
يتم إنشاءه هكذا:
cb=wx.ComboBox(p, -1, style=16)
وهذا النمط للاختيار فقط.
أما النمط الذي يسمح بالكتابة فنجعل الستايل 0 أو نتركه افتراضي.
وهذا يُفيد إذا كانت الخيارات كثيرة جدا فبإمكان المستخدِم كتابة الاختيار الذي يريده ليصل إليه بسرعة.
ولوضع اسم أو عنوان لهذه الصناديق نستخدِم النص الثابت كما فعلنا مع مربعات التحرير.
الوظائف التي تُستخدم مع صناديق الخيارات تشبه ما نستخدمها مع القوائم
SetSelection() لنقل التركيز على عنصر ما.
GetSelection() للحصول على رقم العنصر الذي عليه التركيز وهكذا.
بعض روابط الأحداث:
EVT_TEXT_ENTER: حدَث الضغط على مفتاح الإدخال.
ويتوجب اختيار النمط هذا wx.TE_PROCESS_ENTER.
EVT_TEXT: يُستخدَم في وضع حدث ما عند تغيير النص في صندوق الخيارات.
وتوجد أنماط وأحداث أخرى لكن هذه أهمها.

ننتقل إلى مربعات التحديد checkBox:
هذا الكائن أيضا نضعه على اللائحة ووظيفته تخيير المستخدِم بين أحد الأمرين إذا كان محدد يحدث كذا وإذا ألغينا التحديد يحدُث العكس، على سبيل المثال إظهار بعض الكائنات وإخفاؤها، أو تكرار مقطع صوتي وهكذا.
ويتم إنشائه هكذا:
ck=wx.CheckBox(p, -1, 'تفعيل الإرشادات الصوتية')
ما بعد رقم الهوية طبعا هو اسم مربع التحديد الذي يظهر فإذا تم تحديده اشتغلت الإرشادات الصوتية وإذا تم الإلغاء فلا تعمل الإرشادات.
نحصل على قيمة مربع التحديد هكذا:
GetValue()
إذا كان محدد ترجع True وإذا كان غير محدد ترجع False.
توجد أيضا طريقة أخرى تتحقق إذا ما كان المربع محدد وهي:
IsChecked
if ck.IsChecked:
وهذه تفعل أمر معين لمرة واحدة يعني إذا حددته يحدث شيء ما وإذا ألغيت التحديد فلن يحدث شيء.
وكود ربط مربع التحديد بالحدَث هو:
wx.EVT_CHECKBOX

وإلى لقاءٍ آخر إن شاء الله على أملِ أن نلتقي ونرتقي في سماء العِلْم.
لا تنسوا ذِكرَ الله.

نموذج على صناديق الخيارات ومربعات التحديد


import wx

class test(wx.Frame):
	def __init__(self):
		super(test,self).__init__(None,-1, title= 'نموذج على صناديق الخيارات ومربعات التحديد')
		p=wx.Panel(self, -1)
#إنشاء الصندوق الأول:
		cb= wx.ComboBox(p, -1, choices= ['ok', 'good', 'bad', 'english'], style=16)
		cb.SetSelection(0)
#جعل التركيز على العنصر الأول.
#صندوق يقبل الكتابة
		cb2=wx.ComboBox(p, -1, choices=['1', '2', '3', '4', '5'])
#إنشاء مربع التحديد.
		self.ck =wx.CheckBox(p, -1, 'إخفاء الزر')
#وهذا جعلناه يتحكم في إظهار وإخفاء الزر.
#حدَث الربط.
		self.ck.Bind(wx.EVT_CHECKBOX, self.Box)
		self.b=wx.Button(p, -1, 'ok')
		self.Show()

	def Box (self, event):
		s=self.ck.GetValue()
#للحصول على حالة مربع التحديد.
		if s==True:
			self.b.Hide()
		else:
			self.b.Show()

app= wx.App()
test()
app.MainLoop()

شريط القوائم والاختصارات hotkey


الكاتب سُلَيْمان القسيمي
الإخوة الكرام السلام عليكم ورحمة الله وبركاته.
في رسالتي هذه سأشرح لكم بمشيءة الله تعالى طريقة ربط واستخدام اختصارات لوحة المفاتيح على برامجنا التي ننشؤها باستخدام لغة البرمجة بايثون وبالتحديد مع مكتبة wx.
هذا الموضوع يحتوي على شيء من التفصيل فدعونا ندخل مباشرةً في صلبه.
علينا أن نعلم أنه توجد أكثر من طريقة لربط برنامجنا باختصار معين, سنختار المناسب منها حسب الحاجة.
أما عن أسهل تلك الطرق فهي تلك التي نقوم بها لربط اختصار معين لأحد عناصر شريط القوائم, ذلكم الشريط الذي نصل إليه بالضغط على زر الآلت alt
باختصار شديد جدًا, من أجل أن تقوم بربط أحد عناصر شريط القوائم باختصار معين من اختصارات لوحة المفاتيح, فأنت فقط ستكتب الاختصار ضمن اسم العنصر
دعونا نأخذ في ذلك مثالًأ بسيطًا:
menuBar = wx.MenuBar()
fileMenu = wx.Menu()
menuBar.Append(fileMenu, "ملف")
openItem = fileMenu.Append(-1, "فتح...")
لاحظوا معي الأكواد السابقة.
هنا قمت بإنشاء شريط للقوائم ووضعت فيه قائمة واحدة هي ملف.
داخل قائمة ملف وضعت عنصرًأ واحدًا وهو عنصر لفتح ملف ما على البرنامج.
لو ركزنا على السطر الأخير فسنعلم أن هذا هو السطر المسؤول عن إضافة عنصر الفتح إلى قائمة ملف.
لاحظوا معي أن الدالة المختصة بإضافة العنصر إلى القائمة تستقبل معاملَين اثنَين.
الأول قمت بالتعويض عنه بالرقم -1, وهو يمثل ال id الخاص بالعنصر.
أما المكون الثاني فهو عبارة عن الاسم الظاهري للعنصر, وقد وضعت فيه قيمة نصية هي "فتح..."
المتعارف عليه دائمًأ أن عنصر القائمة فتح دائمًا ما يتم ربطه بالاختصار control + o
ولفعل ذلك سنأتي قبل علامة إغلاق التنصيص الخاص بكتابة اسم العنصر.
سنأتي إلى ذلكم الموضع ونكتب \t التي تترجم بعلامة التاب
ثم بعدها مباشرة نكتب الاختصار الذي نريده, وهو في مثالنا ctrl+o
أي سيكون الكود بهذه الطريقة.
openItem = fileMenu.Append(-1, "فتح\tctrl+o")
وكفى.
هذا كل ما فالأمر.
دعونا ننتقل إلى جانب آخر وهو كيفية ربط زر معين موجود لدينا على الشاشة باختصار معين.
بمعنى أنه لو قام المستخدم بالضغط على هذا الاختصار في أي موضع من الشاشة فإنه سيتم تلقائيًأ بتنفيذ الدالة المرتبطة بالزر.
لنركز قليلًا.
هناك كلاس في مكتبة wx يسمى AcceleratorTable
من خلال هذا الكلاس؛ نستطيع إنشاء مخطط بالاختصارات, والعناصر المرتبطة بها, ثم بعد ذلك سنضيف هذا المخطط إلى نافذتنا
دعونا نكون نافذة بسيطة ونضع فيها بعض العناصر:
class MyWindow(wx.Frame):
def _init_(self):
wx.Frame._init_(self, parent=None, title="اختصارات wx")
self.Centre()
panel = wx.Panel(self)
btn1 = wx.Button(panel, -1, "إظهار الرسالة الأولى")
btn2 = wx.Button(panel, -1, "إظهار الرسالة الثانية")
btn1.Bind(wx.EVT_BUTTON, self.onMSG1)
btn2.Bind(wx.EVT_BUTTON, self.onMSG2)
self.Show()
def onMSG1(self, event):
wx.MessageBox("تم عرض الرسالة من خلال الزر الأول", "رسالة ")
def onMSG2(self, event):
wx.MessageBox("تم عرض الرسالة من خلال الزر الثاني", "رسالة ")
نريد الآن إضافة مخطط يحدد لنا الاختصارات المرتبطة بكل من الزر الأول والزر الثاني.
سنعطي الزر الأول على سبيل المثال الاختصار ctrl+1 والزر الثاني ctrl+2
نأتي فوق self.Show() ونكتب ما يلي
shortcuts = wx.AcceleratorTable([(wx.ACCEL_CTRL, ord("1"), btn1.GetId()), (wx.ACCEL_CTRL, ord("2"), btn2.GetId())])
self.SetAcceleratorTable(shortcuts)
ما هذا الكود؟ دعوني أخبركم.
في البداية عمدنا إلى تعريف متغير أسميناه shortcuts
قيمة هذا المتغير ستكون كائن من نوع AcceleratorTable 
كيف تم إنشاء ال acceleratorTable
إخوتي الكرام يتم التعبير عن مخططات المفاتيح من خلال القوائم.
بحيث أن كل اختصار توضع بياناته كعنصر من عناصر القائمة
نرى هنا أن عناصر القائمة هي بيانات من نوع tuple 
لاحظوا معي أن ال tuple هذا قد تم تقسيمه إلى 3 عناصر أو لنقل 3 خانات
الخانة الأولى لكتابة المتغير الذي يمثل ال modifier key الخاص بالاختصار.
يمكن أن نقول أن ال modifier keys هي بمثابة المفاتيح المرجعية لاختصارات لوحة المفاتيح, كالكنترول والشفت والآلت والويندوز.
هنا تم استخدام المتغير wx.ACCEL_CTRL ليعبر عن زر الكنترول,
يمكن التعويض عن هذا المتغير بالرقم 2
نأتي إلى الخانة الثانية من ال tuple 
والخانة الثانية هنا ستعبر عن الحرف المرتبط بال modifier key 
بمعنى أنه في مثالنا هذا سيكون الرقم 1 للزر الأول , والرقم 2 للزر الثاني
لكن مهلًا.
بايثون لا يعترف بالحرف هكذا دون إضافات
بايثون يريد مني الكود الخاص بهذا الحرف.
للإتيان بالكود الخاص بالحرف نستخدم دالة اسمها ord 
وهي تقبل مني قيمة نصية تتكون من حرف واحد.
هنا قمت بوضع الرقم 1 للتعويض عن الكود الخاص به.
وهنا يجب الملاحظة إلى أنه إذا أردنا ربط الاختصار بحرف من الحروف الإنجليزية فيجب التعويض عنه كبيرًأ capital وليس صغيرًأ small
أما الخانة الأخيرة فهي فسنعوض فيها بال المعرف id الخاص بالعنصر الذي نريد ربطه.
يمكننا معرفة ال id المقترن بأي عنصر من عناصر wx باستخدام الوظيفة GetId
بمعنى أنك ستكتب المتغير الخاص بالعنصر أولًأ ثم نقطة ثم GetId() 
انتهينا الآن من عملية ربط أول زر معنا بالاختصار الخاص به, إذًا سنقوم فورًأ بإغلاق القوس الهلالي لل tuple المحدد لبيانات الاختصار
بعد ذلك إن شئنا إضافة اختصار آخر فسنضع فاصلة ونفتح قوسًا آخرًأ ونكرر ما سبق مع تغيير البيانات.
وآخيرًأ نغلق قوس العنصر وقوس القائمة وقوس دالة AcceleratorTable طبعًأ.
بعد الانتهاء من تجهيز مخطط الإختصارات نقوم بإضافته إلى النافذة باستخدام وظيفة SetAcceleratorTable بهذه الطريقة
self.SetAcceleratorTable(shortcuts)
حيث أن shortcuts هو المتغير الذي تم فيه حفظ مخطط الاختصارات.
الإخوة الكرام, كل ما سبق ذكره من اختصارات كان يعمل على تنفيذ دالة مربوطة بعنصر معين, ولا يمكن تنفيذه على الاختصارات العامة المقرونة بالنافذة ككل.
أما لو كنت تريد إضافة اختصارات عامة للنافذة ككل فهنا سندخل في نطاق الأحداث events
وال events هي أحداث معينة في البرنامج يمكن أن يستغلها المبرمج لاتخاذ قرار معين 
هناك 4 أحداث في wx مرتبطة بالاختصارات
wx.EVT_CHAR_HOOK: ويستخدم لرصد الاختصارات الأحادية, أي ذات الزر الواحد.
wx.EVT_KEY_DOWN: يستخدم لرصد الاختصارات الأحادية والمركبة في آن واحد, ويتم تنفيذ الأمر بمجرد الضغط على المفاتيح المحددة.
wx.EVT_KEY_UP: نفس الحدث السابق, ولكنه لا يتنفذ إلا عند رفع أيدينا عن المفاتيح المحددة
wx.EVT_HOTKEY: نفس الحدثين السابقين, لكنه يقوم بتنفيذ الاختصارات المحددة حتى لو كانت نافذة البرنامج مخفية.
ما سنغطيه الآن هو الحدث الأول والثاني, كونهما الأكثر شيوعًا, أما الحدثَين الأخيرَين فسنتحدث عنهما فيما بعد بمشيءة الله.
أولًأ: wx.EVT_CHAR_HOOK:
هو حدث يتم إرساله إلى البرنامج في كل مرة يقوم المستخدم بالضغط على أحد أزرار لوحة المفاتيح.
عند تعيين دالة معينة لهذا الحدث؛ يمكن معرفة أسماء الأزرار التي تم ضغطها وتنفيذ أوامر مخصصة لكل زر.
دعونا نأتي بالنافذة التي قمنا ببنائها أعلاه ونرى كيف يمكن تطبيق الحدث عليها
class MyWindow(wx.Frame):
def _init_(self):
wx.Frame._init_(self, parent=None, title="اختصارات wx")
self.Centre()
panel = wx.Panel(self)
btn1 = wx.Button(panel, -1, "إظهار الرسالة الأولى")
btn2 = wx.Button(panel, -1, "إظهار الرسالة الثانية")
btn1.Bind(wx.EVT_BUTTON, self.onMSG1)
btn2.Bind(wx.EVT_BUTTON, self.onMSG2)
shortcuts = wx.AcceleratorTable([(wx.ACCEL_CTRL, ord("1"), btn1.GetId()), (wx.ACCEL_CTRL, ord("2"), btn2.GetId())])
self.SetAcceleratorTable(shortcuts)
self.Show()
def onMSG1(self, event):
wx.MessageBox("تم عرض الرسالة من خلال الزر الأول", "رسالة ")
def onMSG2(self, event):
wx.MessageBox("تم عرض الرسالة من خلال الزر الثاني", "رسالة ")
لاحظوا معي أنه عندما تم ربط الأزرار بحدث الضغط قمنا باستخدام وظيفة تسمى Bind
داخل هذه الوظيفة تم تمرير الحدث المطلوب رصده, وكذلك الدالة التي سيتم تنفيذها عند وقوع الحدث
سنقوم بنفس الأمر تمامًأ لربط النافذة بالحدث EVT_CHAR_HOOK
self.Bind(wx.EVT_CHAR_HOOK, self.onHook)
طيب نأتي الآن عند نهاية الكلاس ونقوم بتعريف دالة onHook التي قلنا أنها هي التي ستقوم بمعالجة الحدث EVT_CHAR_HOOK
def onHook(self, event):
if event.GetKeyCode() == wx.WXK_ESCAPE:
wx.Exit()
event.Skip()
ماذا حدث هنا؟
قلنا أن الأحداث يتم التعرف عليها من خلال دالة معينة
هنا تم تعريف الدالة onHook, وقد أخذت معاملَين
* self: كإشارة إلى أن الدالة معرفة داخل كلاس.
* event: وهنا يتم تمرير تفاصيل الحدث, من ناحية الزر الذي تم ضغطه إلخ...
داخل الدالة وضعنا أمرًا شرطيًا يسأل ال event عن الزر الذي تم ضغطه
الآن تفسير العبارة event.GetKeyCode() هو ماذا؟
هو تحقق من الزر المضغوط
فهنا نحن نقول له, إذا كان الزر الذي تم ضغطه, يساوي كود زر الهروب escape 
إذًا قم بتنفيذ دالة إغلاق البرنامج.
أما عن
event.Skip()
فهي جملة تطلب من البرنامج أن يستمر في العمل ولا يتوقف فقط عند عملية الرصد.
قد يسألني سائل.
كيف أعرف الكود الخاص بالزر الذي أريد رصده.
دعونا نتفق على أنه لو أردت رصد أحد الحروف الهجائية أو الأرقام فإنك ستستخدم دالة ord وتمرر لها الحرف أو الرقم بين علامتَي تنصيص
أما عن الأزرار العامة؛ فأكوادها موجودة على هيئة متغيرات في مكتبة wx
وغالب الأزرار العامة, إن لم تكن كلها, تبدأ أسماؤها ب WXK ثم شرطة سفلية ثم اسم الزر capital
أي بالشكل التالي:
الكنترول: wx.WXK_CONTROL
الشفت: wx.WXK_SHIFT
المسافة: wx.WXK_SPACE
مفاتيح المهمة من f1 إلى f12: wx.WXK_F ثم الرقم
مفتاح الدخول enter: wx.WXK_RETURN
وقس على ذلك
الآن لو أردت معالجة أكثر من اختصار فستقوم فقط بإضافة جملة elif وجوابها
ثانيًا: الحدث wx.EVT_KEY_DOWN:
وهو حدث يتم إرساله إلى البرنامج طالما أن زر أو أكثر قيد الضغط.
برمجيًا: يتم ربط هذا الحدث ومعالجته بنفس طريقة EVT_CHAR_HOOK
مع اختلافين اثنَين:
الأول هو أنك تحتاج إلى ربط الحدث بعناصر الشاشة كلها ولا يمكن ربطها بالشاشة مرة واحدة.
أسهل طريقة لفعل ذلك هو استخدام الحلقة for للدوران على عناصر النافذة وربطها واحدًا تلو الآخر بالحدث
أي بهذه الطريقة:
for item in panel.GetChildren():
item.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
إن الوظيفة GetChildren هي وظيفة تستخدم لمعرفة العناصر المندرجة تحت حاوي معين كاللائحة أو النافذة ككل , ووضع تلك العناصر في قائمة.
الإختلاف الثاني هنا هو مسألة ال modifier keys التي رأيناها في ال AcceleratorTable 
لكن هنا ال modifier keys يضاف فقط إلى الاختصارات المركبة كون أن هذا الحدث يقبل أيضًأ إنشاء اختصارات ذات زر واحد بنفس طريقة ال evt_char_hook بالضبط
طيب لنبرمج دالة onKeyDown التي ستعالج لنا الحدث
def onKeyDown(self, event):
if event.controlDown and event.shiftDown and event.GetKeyCode() == wx.WXK_SPACE:
wx.MessageBox("لقد ضغطت على الاختصار control+shift+space", "رسالة")
event.Skip()
لاحظوا معي أن بنية الدالة تشبه كثيرًا تلك التي تم بناؤها لمعالجة الحدث EVT_CHAR_HOOK 
الفرق هنا كما ذكرنا وجود ال modifier key 
فكما ترون أني كتبت جملة شرطية تقول للبرنامج أنه لو تم الضغط على الكنترول والشفت والمسافة معًا فسيتم إظهار رسالة لذلك
هنا ال event.controlDown ستساوي True لو كان زر الكنترول قيد الضغط, والحال أيضًأ ينطبق على ال event.shiftDown الذي يطبق نفس الأمر على الشفت
أما الجزء الثالث من الشرط فهو نفس التعبير الذي تم استخدامه في evt_char_hook لمعرفة الزر المضغوط من الأزرار العامة.
في نهاية الدالة نجد نفس جملة التخطي التي تساعد على إبقاء سلوك لوحة المفاتيح على الوضع الافتراضي طالما أنه لم يتم اكتشاف أن إحدى الاختصارات المحددة قد تم الضغط عليها.
أخيرًا: دعونا نشاهد كود النافذة كاملًا ونتيح لمن أراد تجربة نتائجه أن يقوم بنسخه.
مع ملاحظة أني قمت بإضافة شريط القوائم وربطته عنصر الفتح بدالة خاصة به لكي يتسنى لكم قراءة النافذة بالكامل.
class MyWindow(wx.Frame):
def _init_(self):
wx.Frame._init_(self, parent=None, title="اختصارات wx")
self.Centre()
panel = wx.Panel(self)
btn1 = wx.Button(panel, -1, "إظهار الرسالة الأولى")
btn2 = wx.Button(panel, -1, "إظهار الرسالة الثانية")
btn1.Bind(wx.EVT_BUTTON, self.onMSG1)
btn2.Bind(wx.EVT_BUTTON, self.onMSG2)
# مخطط الاختصارات Accelerator Table
shortcuts = wx.AcceleratorTable([(wx.ACCEL_CTRL, ord("1"), btn1.GetId()), (wx.ACCEL_CTRL, ord("2"), btn2.GetId())])
self.SetAcceleratorTable(shortcuts)
menuBar = wx.MenuBar()
fileMenu = wx.Menu()
menuBar.Append(fileMenu, "ملف")
openItem = fileMenu.Append(-1, "فتح...\tctrl+o")
self.Bind(wx.EVT_MENU, self.onOpenItem, openItem)
self.SetMenuBar(menuBar)
# ربط النافذة بكل من الحدث EVT_CHAR_HOOK والحدث EVT_KEY_DOWN
self.Bind(wx.EVT_CHAR_HOOK, self.onHook)
for item in panel.GetChildren():
item.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
self.Show()
def onMSG1(self, event):
wx.MessageBox("تم عرض الرسالة من خلال الزر الأول", "رسالة ")
def onMSG2(self, event):
wx.MessageBox("تم عرض الرسالة من خلال الزر الثاني", "رسالة ")
def onOpenItem(self, event):
wx.FileSelector("اختر ملف")
# الدالة التي ستعالج دالة EVT_CHAR_HOOK
def onHook(self, event):
if event.GetKeyCode() == wx.WXK_ESCAPE:
wx.Exit()
event.Skip()
# الدالة التي ستعالج الحدث EVT_KEY_DOWN
def onKeyDown(self, event):
if event.controlDown and event.shiftDown and event.GetKeyCode() == wx.WXK_SPACE:
wx.MessageBox("لقد ضغطت على الاختصار control+shift+space", "رسالة")
event.Skip()
app = wx.App()
MyWindow()
app.MainLoop()

نموذج على شريط القوائم والاختصارات


import wx

class MyWindow(wx.Frame):
	def __init__(self):
		wx.Frame.__init__(self, parent=None, title="اختصارات wx")
		self.Centre()
		panel = wx.Panel(self)
		btn1 = wx.Button(panel, -1, "إظهار الرسالة الأولى")
		btn2 = wx.Button(panel, -1, "إظهار الرسالة الثانية")
		btn1.Bind(wx.EVT_BUTTON, self.onMSG1)
		btn2.Bind(wx.EVT_BUTTON, self.onMSG2)
# مخطط الاختصارات Accelerator Table
		shortcuts = wx.AcceleratorTable([(wx.ACCEL_CTRL, ord("1"), btn1.GetId()), (wx.ACCEL_CTRL, ord("2"), btn2.GetId())])
		self.SetAcceleratorTable(shortcuts)
		menuBar = wx.MenuBar()
		fileMenu = wx.Menu()
		menuBar.Append(fileMenu, "ملف")
		openItem = fileMenu.Append(-1, "فتح...\tctrl+o")
		self.Bind(wx.EVT_MENU, self.onOpenItem, openItem)
		self.SetMenuBar(menuBar)
# ربط النافذة بكل من الحدث EVT_CHAR_HOOK والحدث EVT_KEY_DOWN
		self.Bind(wx.EVT_CHAR_HOOK, self.onHook)
		for item in panel.GetChildren():
			item.Bind(wx.EVT_KEY_DOWN, self.onKeyDown)
		self.Show()

	def onMSG1(self, event):
		wx.MessageBox("تم عرض الرسالة من خلال الزر الأول", "رسالة ")

	def onMSG2(self, event):
		wx.MessageBox("تم عرض الرسالة من خلال الزر الثاني", "رسالة ")

	def onOpenItem(self, event):
		wx.FileSelector("اختر ملف")
# الدالة التي ستعالج دالة EVT_CHAR_HOOK
	def onHook(self, event):
		if event.GetKeyCode() == wx.WXK_ESCAPE:
		wx.Exit()
		event.Skip()
# الدالة التي ستعالج الحدث EVT_KEY_DOWN

	def onKeyDown(self, event):
		if event.controlDown and event.shiftDown and event.GetKeyCode() == wx.WXK_SPACE:
		wx.MessageBox("لقد ضغطت على الاختصار control+shift+space", "رسالة")
		event.Skip()

app = wx.App()
MyWindow()
app.MainLoop()

موضوعات أخرى مُتنوِعة

ونظرا لتعدد الموضوعات وتنوعها سنضع لكم هنا العنوان وبالضغط عليه سيتم الانتقال إلى صفحة أخرى. كيفية إرسال إشعار إلى ويندوز wx.FileSelector لإظهار محاورة اختيار ملف مربعات اختيار رقم wx.SpinCtrl إنشاء نافذة مُحاورة وكيفية إظهارهاwx.Dialog

تعليقات

إرسال تعليق

ما من كاتبٍ سيَبلى
ويُبقي الدهرُ ما كتبت يَداه
فلا تكتُبَنَّ بيديكَ غيرَ شيءٍ
يسُرُكَ في القيامةِ أن تراه.