مدونتى حيث اشارك اهتماماتي البرمجية و الفلاحية و الثقافية

Tkinter: TopLevel windows نوافد منبتقة

الصنف ()Toplevel في tkinter يسمح بصنع نوافذ "منبتقة" لتطبيقك الاصلي. هذه النوافذ مستقلة، لكنها تغلق تلقائيا عند إغلق النافذة الرئيسية. يتم التعامل معها بالطريقة المعتادة التي تتم في النوافد الرئيسية : يمكن وضع أي مجموعة من الويدجات او الادوات المراد استعمالها بشكل عادي.
التطبق الصغير أدناه يوضح لك بعض قدرات هاته النوافد. سيتكون التطبيق من نافذة رئيسية عادية جدا، تحتوي على 3 أزرار.
هذه الازرار صنعت بصنف مشتق من صنف Button() الأساسي، وذلك لتعرف مرة أخرى كم هو من السهل تكييف أصناف الكائن الموجودة لاجل اعمالك. سوف تلاحظ بعض خيارات "الديكور او التنسيق" المثيرة للاهتمام.
الرز Top1 يظهر لك أول نافذة منبتقة تحتوي على لوحة فيها سورة. تتميز هذه النافذة بالخاصيات التالية : ليس لديها لا شعار لاعنوان ولا حدود، ومستحيل إعادة تحجيمها بواسطة الفأرة. بالاضافة إلى ذلك، هذه النافذة تشترط تواجدها في مقدمة جميع النوافذ الاخرى للتطبيقات الأخرى التي قد تكون موجودة على الشاشة.
الرز Top2 يعرض نافذة منبتقة كلاسيكية، تحتوي على نسختين من الودجات SpinBox. هذا الودجة تتكون من زران و شريط يظهر قيمة رقمية مرتبطة بالزرين. فالزران يسمحان بزيادة أو انقاص القيمة المعروضة على الشريط. بالاضافة إلى هذان SpinBoxe ، النافذة تحتوي على زر كبير مزين. عند الضغط عليه، يمكن للمستخدم تغيير حجم اللوحة (canvas) في نافذة منبتقة اخرى، حسب القيم الرقمية المعروضة في الودجتان  SpinBoxe .
الزر Exit لمغادرة النافذة
class FunnyButton(Button):
    "زر يتغير من اللون الاخضر الى الاحمر عند الضغط عليه"
    def __init__(self, boss, **Arguments):
        Button.__init__(self, boss, bg ="dark green", fg ="white", bd=5, activebackground ="red",
                        activeforeground ="yellow",font =('Helvetica', 12, 'bold'), **Arguments)
 
إذا كنت ترغب في الحصول على نفس تصميم الازرار المختلفة في الاجزاء المختلفة من مشروعك، لا تتردد في صنع فئة مشتقة، كما فعلنا هنا. وهذا سيوفر عليك اعادة برمجة نفس الخيارات المحددة لكل زر.
لاحظ النجمتين التي بدأنا بها اسم نهاية برامتر المنشئ : ** Arguments (**برامترات). وهذا يعني أن هذا المتغير هو عبارة عن قاموس، قادر على تلقي تلقائيا  مجموعة من البرامترات. هذه البرامترات يمكنها تمرير نفسها للمنشئ  او الصنف الاصل. وهذا يعفينا من الاضطرار إلى إعادة كتابة جميع الخيارات  للزر والتي هي كثيرة جدا. وأيضا يمكنك تخصيص هذه الازرار مع أي مجموعة من الخيارات، طالما أنها متاحة لزر الاساس. ونسمي هذا بالبرامترات الضمنية. يمكنك استخدام هذا الشكل من البرامترات مع أي دالة أو أسلوب.
class SpinBox(Frame):
    "ودجة تتكون من شريط و زرين زائد و ناقص"
    def __init__(self, boss, largC=5, largB =2, vList=[0], liInd=0, orient =Y):
        Frame.__init__(self, boss)
        self.vList =vList       #قائمة القيم المتوفرة
        self.liInd =liInd       # مؤشر قيمة العرض الافتراضي
        if orient ==Y:
            s, augm, dimi = TOP, "^", "v"           # التوجيه ' عمودي '
        else:
            s, augm, dimi = RIGHT, ">", "<"     # التوجيه ' أفيق '
        Button(self, text =augm, width =largB, command =self.up).pack(side =s)
        self.champ = Label(self, bg ='white', width =largC,text =str(vList[liInd]), relief =SUNKEN)
        self.champ.pack(pady =3, side =s)
        Button(self, text=dimi, width=largB, command =self.down).pack(side =s)
 
 منشئ الودجة الخاص ب SpinBox لا يحتاج إلا القليل من الشرح. إعتمادا على التوجيه المطلوب، الاسلوب pack()  يتيح لك أزرار وملصقات من الاعلى الى الاسفل أو اليمين الى اليسار )حسب البرامترات TOP او RIGHT في الخيار side.
    def up(self):
        if self.liInd < len(self.vList) -1:
            self.liInd += 1
        else:
            self.bell() # صوت تنبيه
            self.champ.configure(text =str(self.vList[self.liInd]))

    def down(self):
        if self.liInd > 0:
            self.liInd -= 1
        else:
            self.bell() # صوت تنبيه
            self.champ.configure(text =str(self.vList[self.liInd]))

    def get(self):
        return self.vList[self.liInd]


هذان الاسلوبان لا يفعلان شيئا أكثر من تعديل القيمة المعروضة في الملصق او الشريط. لاحظ أن الصنف Frame () لديه أسلوب bell () لاصدار صوت "بيب"


class FenDessin(Toplevel):
    "Fenêtre satellite (modale) contenant un simple canevas"
    def __init__(self, **Arguments):
        Toplevel.__init__(self, **Arguments)
        self.geometry("250x200+100+240")
        self.overrideredirect(1) # => نافذة دون حدود
        self.transient(self.master) # => نافذة 'modale'          
        self.can =Canvas(self, bg="ivory", width =200, height =150)
        self.img = PhotoImage(file ="papillon2.gif")
        self.can.create_image(90, 80, image =self.img)
        self.can.pack(padx =20, pady =20)


تعريف النافذة المنبتقة الاولى هنا. لاحظ مرة أخرى استخدام البرامتر الضمني للمنشئ بمساعدة المتغير ** Arguments . هذا هو الذي يسمح لنا بتشكيل هذه النافذة عن طريق تحديد لون الخلفية (الحدود، إلخ ...). تم ايضا هنا استدعاء تعريف بعض خصائص التي تنطبق على أي نافذة. الاسلوب geometry () يسمح لك بتعيين إحداثيات النافذة ومكانها غلى الشاشة.  100+240 تعني أنه ينبغي تحويل الزاوية العلوية اليسرى 100 بيكسل إلى اليمين و 240 بيكسل أسفل زاوية اليسرى العلوية من الشاشة.

class FenControle(Toplevel):
    "Fenêtre satellite contenant des contrôles de redimensionnement"
    def __init__(self, boss, **Arguments): 


يرجى ملاحظة الفرق الصغير بين قوائم البرامترات لهذه الاسطر. في منشئ FenDessin ،تم حذف البرامتر boss ، الموجود في منشئ FenControle . هل تعلم أن هذا البرامتر يستخدم لتمرير مرجع الودجة  "السيد" ل "عبيده". وهو عموما ضروري، لكن لم يكن ضروريا للغاية : في FenDessin ليست له اي فائدة. سوف تجد الفرق واضحا في تعليمات التمثيل لهذه النوافذ.

        Toplevel.__init__(self, boss, **Arguments)
        self.geometry("250x200+400+230")
        self.resizable(width =0, height =0) # => منع تغيير
        p =(10, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300)
        self.spX =SpinBox(self, largC=5,largB =1,vList =p,liInd=5,orient =X)
        self.spX.pack(pady =5)
        self.spY =SpinBox(self, largC=5,largB =1,vList =p,liInd=5,orient =Y)
        self.spY.pack(pady =5)
        FunnyButton(self, text ="Dimensionner le canevas",command =boss.redimF1).pack(pady =5)


 باستثناء الفرق المذكور أعلاه، منشئ الودجة FenControle مشابه جدا ل FenDessin . لاحظ الاسلوب الذي يسمح بمنع تغيير حجم النافذة ( في حالة النافذة بدون حدود وبدون عنوان، مثل FenDessin ، سيكون هذا السلوب بدون جدى)

class Demo(Frame):
    "Démo. de quelques caractéristiques du widget Toplevel"
    def __init__(self):
        Frame.__init__(self)
        self.master.geometry("400x300+200+200")
        self.master.config(bg ="cadet blue")
        FunnyButton(self, text ="Top 1", command =self.top1).pack(side =LEFT)
        FunnyButton(self, text ="Top 2", command =self.top2).pack(side =LEFT)
        FunnyButton(self, text ="Quitter", command =self.quit).pack()
        self.pack(side =BOTTOM, padx =10, pady =10)
    def top1(self):
        self.fen1 =FenDessin(bg ="grey")
    def top2(self):
        self.fen2 =FenControle(self, bg ="khaki")
    def redimF1(self):
        dimX, dimY = self.fen2.spX.get(), self.fen2.spY.get()
        self.fen1.can.config(width =dimX, height =dimY)

if __name__ =="__main__": # --- يبرنامج التجريةب ---
    Demo().mainloop()   
   

في هذا الجزء جميع الاصناف المشتقة هي ويدجات tkinter مميزة تلقائيا بسمة master ، الذي يحتوي على مرجع الصنف الاصل. الذي يسمح لنا بالوصول إلى أبعاد ولون خلفية النافذة الرئيسية.
الاسلوب بعد الدالة redimF1 يسترد القيم الرقمية المعروضة في نافذة التحكم. فالاسلوب get () لهذه الودجة يسمح باعادة تحجيم الوحة في النافذة المنبتقة. هذا المثال البسيط يبين لك كيفية تحقيق تواصل بين مخلتف مكونات البرنامج.