Създаване на прозорчета

„ Програмиране с Python“, ФМИ

Нено Ганчев

04.06.2007г.

Как да си създадем прозорци с Python?

Откъде да започнем?

  1. Първо да инсталираме магическата пръчица за прозорци - питонския модул wx
    • сваляме си инсталацията, демотата и документацията от http://wxpython.org/
    • инсталацията е в готови изпълними файлове, но за ентусиастите има и source-ове
  2. Не е лоша идея преди да почнем да си правим собствено приложение първо да погледнем примерните програми и overview частта в документацията
  3. Има и страхотно демо в което можете да видите примери за всички възможности които wxPython ви предоставя както в код, така и нагледно(незаменим помощник по време на разработка:)
  4. Недостатък на документацията - има я само в C++ вариант със забележки за wxPython

Основни концепции в wxPython

  1. Йерархия на класовете, представляващи прозорчетата по екрана
    • Основни класове: wxApp, wxWindow, wxFrame, wxDialog, wxPanel
    • Класове-контроли: всевъзможни бутончета, текстови полета, менюта и т.н.
  2. Обработка на събития(event handling) и main loop

Основните класове отблизо: wxApp

wxApp има няколко метода които трябва да се дефинират или извикат:

  1. OnInit()
    • инициализира графичното ни приложение(играе ролята на конструктор)
    • трябва да върне True, иначе wxPython няма да покаже приложението
  2. SetTopWindow(window)
    • Казва на програмата кой от всичките прозорци е главен
    • Програмата може да има няколко главни прозореца
    • Главните прозорци са специални
  3. MainLoop()
    • стартира главния цикъл за прехващане на събития
    • без него главния прозорец може и да се покаже но ще стои "замръзнал" и няма да иска нито да си играе с нас, нито да си ходи
  4. OnExit()
    • правилното място за деинициализация на потребителски данни
    • вика се след като потребителя е казал че ще затваря програмата, но преди вътрешните структури на wxPython да се деинициализират и да не можете да ги ползвате

Основните класове отблизо: wxWindow

Основните класове отблизо: wxFrame

Основните класове отблизо: wxDialog

Основните класове отблизо: wxPanel

Малко практика

Да видим как изглежда казаното досега като код:

Малки по-надълбоко: обработване на събития

За да реагира на действията на потребителя, wxPython използва предварително зададени възможни събития( например натискане на бутон или преместване на прозореца) с които можете да асоциирате ваши функции които да обработват по желания от вас начин събитието. Това става като "свържете" някой прозорец с дадено събитие, което е възникнало в него, и с вашата функция която ще го обработи

За удобство събитията не се виждат само от прозореца в който са възникнали, а и от всички прозорци нагоре в родителското дърво. Това означава че ако в панел имате няколко бутона, не е нужно събитието "натискане на бутон" да се прехваща във всеки бутон, може директо да ги прехванете в панела.

Това става като извикате метода Bind на прозореца в който искате да прехванете събитието и му подадете типа събитие, функцията която ще се извика, и източника на събитието

Да си добавим меню в примера

В конструктора на MyFrame добавяме:


menuBar = wx.MenuBar()
        menu = wx.Menu()
        menu.Append(self.ID_Exit, "E&xit")
        menuBar.Append(menu, "&File")
        menu = wx.Menu()
        menu.Append(self.ID_About, "&About")
        menuBar.Append(menu, "&Help")
        self.SetMenuBar(menuBar)
	

Свързваме менютата с наши функции които искаме да се изпълнят когато менюте се избере:


self.Bind(wx.EVT_MENU, self.OnExit, id = self.ID_Exit)
        self.Bind(wx.EVT_MENU, self.OnAbout, id = self.ID_About)
	

И добавяме handler`ите ни:


def OnExit(self, event):
        answer = wx.MessageBox("Do you really want to quit?", "Confirm", style = wx.YES_NO | wx.ICON_QUESTION)
        if answer == wx.YES: self.Close()

    def OnAbout(self, event):
        wx.MessageBox("wxPython greeter program", "About", style = wx.OK)
	

Как да си подредим бутончетата в прозореца

При слагането на повече контроли в един прозорец става малко трудно да ги наредите всички така че да не си пречат. Указването на точна позиция(в координати) на всеки е дейност, достойна за наказание в програмисткия ад. Едно решение на проблема са така наречените sizers

Sizer-ите разчитат на това че в повечето случаи разположението на обектите в прозореца следва прости геометрични правила като например в колона, редица или таблица. Използването им е съвсем просто - при създаване на прозорец създавате и sizer който ще отговаря за него, добавяте всички деца на прозореца в sizer-а и накрая не забравяте да кажете на прозореца кой му е sizer-a. След това sizer-а автоматично поема отговорността за разположението на обектите в прозореца

При добавяне на обекти в sizer-а можете да укажете дали обекта ще се разширява при разширяване на целия прозорец и в какво съотношение в сравнение с останалите обекти в прозореца

Да позапълним прозореца

Засега прозореца в приложението ни е много пуст и на самотния низ му е много скучно, а и му е писнало да поздравява само едни и същи хора. Нека му разнообразим съществуването: ще добавим поле в което да се казва кого ще поздравява програмата ни и бутон с който да поздравим въведеното същество

За целта създаваме BoxSizer в конструктора на MyFrame и добавяме текста и новите контроли в него


	self.textField = wx.TextCtrl(panel, -1)
        self.greetButton = wx.Button(panel, ID_Greet, "Greet")
        buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
        buttonSizer.Add(wx.StaticText(parent = panel, label = "Enter name:"), 0, wx.ALL, 10)
        buttonSizer.Add(self.textField, 1, wx.ALL | wx.EXPAND, 10)
        buttonSizer.Add(self.greetButton, 0, wx.ALL, 10)
        panelSizer = wx.BoxSizer(wx.VERTICAL)
        panelSizer.Add(self.greeting, 1, wx.ALL | wx.EXPAND | wx.CENTRE, 10)
        panelSizer.Add(buttonSizer, 0, wx.ALL | wx.EXPAND, 10)
        
        panel.SetSizer(panelSizer)
        panel.Fit()
        
        self.Bind(wx.EVT_BUTTON, self.OnGreet, self.greetButton)

        frameSizer = wx.BoxSizer(wx.VERTICAL)
        frameSizer.Add(panel, 1, wx.EXPAND)
        self.SetSizerAndFit(frameSizer)
	

Да позапълним прозореца 2

Остана да добавим handler-а за новия бутон:


def OnGreet(self, event):
        name = self.textField.GetValue()
        if len(name) == 0:
            wx.MessageBox("Enter a name to greet first", "Error", style = wx.OK | wx.ICON_EXCLAMATION)
            return
        self.greeting.SetLabel("Hello %s!" % name)
        self.Refresh()
        self.Update()
	

Честито, вече си имаме прозорче с което можем да поздравяваме когото ни хареса:)