wxPython - události
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu
Reklama

wxPython - událostiwxPython - události

 

wxPython - události

Google       Google       8. 11. 2008       25 756×

Události jsou nedílnou součástí každé GUI aplikace. Všechny GUI aplikace jsou jimi vlastně řízeny. Události jsou většinou vygenerované uživatelem. Jakmile zavoláme metodu MainLoop(), bude naše aplikace v nekonečné smyčce odchytávat události a zpracovávat je. Tato smyčka skončí společně s koncem programu.

Reklama
Reklama

První příklad

Než začneme se samotným příkladem, měli bychom si vysvětlit několik pojmů. Jak už bylo řečeno, program ve smyčce MainLoop() zachytává všechny události a předává je do rozvodny. Tam se zkontroluje, zda je pro danou událost registrován nějaký ovladač. Pokud je, zavolá se funkce/metoda svázaná s ovladačem.

V následujícím příkladě si popíšeme velmi jednoduchou věc - ukážeme si, jaká událost se vygeneruje, pohneme-li oknem.

Vždy když pohneme oknem, vygeneruje se událost typu wx.MoveEvent. Ovladač pro ni je wx.EVT_MOVE.

import wx

class MoveEvent(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(250, 180))

        wx.StaticText(self, -1, 'x:', (10,10))
        wx.StaticText(self, -1, 'y:', (10,30))
        self.st1 = wx.StaticText(self, -1, '', (30, 10))
        self.st2 = wx.StaticText(self, -1, '', (30, 30))

        self.Bind(wx.EVT_MOVE, self.OnMove)

        self.Centre()
        self.Show(True)

    def OnMove(self, event):
        x, y = event.GetPosition()
        self.st1.SetLabel(str(x))
        self.st2.SetLabel(str(y))


app = wx.App()
MoveEvent(None, -1, 'move event')
app.MainLoop()

V tomto příkladu vypisujeme do wx.StaticText aktuální pozici okna (ta se automaticky přepíše, pokud oknem pohneme).

self.Bind(wx.EVT_MOVE, self.OnMove)

Pomocí předcházejícího kusu kódu jsme svázali ovladač wx.EVT_MOVE s metodou self.OnMove.

def OnMove(self, event):
    x, y = event.GetPosition()

Jistě jste si všimli, že metoda OnMove() přijímá kromě parametru self ještě parametr event. V tomto případě je to instance třídy wx.MoveEvent. Pomocí této třídy můžete získat nějaké informace o události. Například aktuální pozici okna. Tu zjistíme zavoláním metody GetPosition().

Registrování událostí

Registrování událostí je ve wxPythonu poměrně jednoduchá záležitost. Skládá se ze dvou kroků:

  • Vytvořit metodu, která se zavolá, pokud se vygeneruje daná událost.
  • Registrovat ovladač (wx.EVT_SIZE, wx.EVT_BUTTOM, wx.EVT_CLOSE apod.) a svázat ho s metodou vytvořenou v předešlém kroku.
button = Button(self, label = 'I am button', id = -1)
button.Bind(wx.EVT_BUTTON, self.OnClick)

Vetování událostí

Občas potřebujeme provádění události zastavit (vetovat). K tomu slouží metoda Veto().

import wx

class Veto(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(250, 200))


        self.Bind(wx.EVT_CLOSE, self.OnClose)

        self.Centre()
        self.Show(True)

    def OnClose(self, event):

        dial = wx.MessageDialog(None, 'Are you sure to quit?', 'Question',
            wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
        ret = dial.ShowModal()
        if ret == wx.ID_YES:
            self.Destroy()
        else:
            event.Veto()


app = wx.App()
Veto(None, -1, 'Veto')
app.MainLoop()

V tomto příkladě odchytáváme událost wx.CloseEvent. Tato událost je zavolána, když se pokoušíme zavřít okno. V mnoha aplikacích chceme zabránit uživateli, aby okno jen tak zavřel, protože například nestihl uložit rozdělanou práci. Musíme tedy registrovat ovladač wx.EVT_CLOSE.

dial = wx.MessageDialog(None, 'Are you sure to quit?', 'Question',
    wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
ret = dial.ShowModal()

V průběhu metody wx.OnClose vytvoříme dialog, kde se ptáme, zda uživatel chce aplikaci opravdu vypnout.

if ret == wx.ID_YES:

    self.Destroy()
else:
    event.Veto()

V závislosti na proměnné ret buď zničíme okno, nebo zastavíme událost. Všimněte si, že okno ničíme pomocí Destroy(). Kdybychom použili metodu Close(), skončili bychom v nekonečné smyčce.

Šíření událostí

Existují dva druhy událostí: šiřitelné a nešiřitelné. Ty šiřitelné putují od dceřiných komponent až k hlavnímu rodičovskému oknu. Nešiřitelné putovat přestanou, jakmile jsou jednou zachyceny. Pokud chceme, aby se událost šířila dál, musíme zavolat metodu Skip().

import wx


class MyPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)

        self.Bind(wx.EVT_BUTTON, self.OnClicked)

    def OnClicked(self, event):
        print 'event reached panel class'
        event.Skip()


class MyButton(wx.Button):
    def __init__(self, parent, id, label, pos):
        wx.Button.__init__(self, parent, id, label, pos)

        self.Bind(wx.EVT_BUTTON, self.OnClicked)

    def OnClicked(self, event):
        print 'event reached button class'
        event.Skip()


class Propagate(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(250, 150))

        panel = MyPanel(self, -1)

        MyButton(panel, -1, 'Ok', (15, 15))

        self.Bind(wx.EVT_BUTTON, self.OnClicked)

        self.Centre()
        self.Show(True)

    def OnClicked(self, event):
        print 'event reached frame class'
        event.Skip()


app = wx.App()
Propagate(None, -1, 'Propagate')
app.MainLoop()

V tomto příkladě máme jedno tlačítko v panelu, který je umístěn v okně wx.Frame. Vytvořili jsme ovladače pro všechny tři komponenty.

event reached button class
event reached panel class
event reached frame class

Tento výstup dostaneme, pokud klikneme na tlačítko. Událost putuje od tlačítka přes panel až k hlavnímu oknu.

Zkuste vynechat metodu Skip() a uvidíte, co se stane.

Identifikátory komponent

Identifikátory komponent jsou unikátní čísla (nelze použít nějaké číslo dvakrát), pomocí nichž poznáme, jaké komponentě máme předat danou událost. Existují tři způsoby, jak vytvořit identifikátor:

  • Nechat Python jej vytvořit.
  • Použít některý z předdefinovaných identifikátorů.
  • Přiřadit komponentě identifikátor dle vlastního výběru.
wx.Button(parent, -1)
wx.Button(parent, wx.ID_ANY)

Pokud jako identifikátor poskytneme -1 nebo wx.ID_ANY, necháváme jeho výběr na wxPythonu. Takto automaticky vytvořený identifikátor je vždy záporný, zatímco námi určené identifikátory musí být vždy kladné. Automatické indentifikátory používáme hlavně tehdy, když víme, že s danou komponentou již nebudeme pracovat (například wx.StaticText, který se po celou dobu aplikace nezmění). Pokud ale budeme chtít, můžeme získat hodnotu automatického identifikátoru pomocí metody GetId().

import wx

class AuIds(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(170, 100))

        panel = wx.Panel(self, -1)
        exit = wx.Button(panel, -1, 'Exit', (10, 10))

        self.Bind(wx.EVT_BUTTON,  self.OnExit, id=exit.GetId())

        self.Centre()
        self.Show(True)

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


app = wx.App()
AuIds(None, -1, '')
app.MainLoop()

V předchozím příkladě jsme neurčili hodnotu identifikátoru → wxPython ji vybral za nás.

self.Bind(wx.EVT_BUTTON,  self.OnExit, id=exit.GetId())

Hodnotu automaticky vytvořeného identifikátoru získáme metodou GetId().

Je doporučeno používat předdefinované indentifikátory co nejvíce. Na některých platformách to totiž zlepší vzhled aplikace.

import wx

class Identifiers(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(200, 150))

        panel = wx.Panel(self, -1)
        grid = wx.GridSizer(3, 2)

        grid.AddMany([(wx.Button(panel, wx.ID_CANCEL), 0, wx.TOP | wx.LEFT, 9),
            (wx.Button(panel, wx.ID_DELETE), 0, wx.TOP, 9),
            (wx.Button(panel, wx.ID_SAVE), 0, wx.LEFT, 9),
            (wx.Button(panel, wx.ID_EXIT)),
            (wx.Button(panel, wx.ID_STOP), 0, wx.LEFT, 9),
            (wx.Button(panel, wx.ID_NEW))])


        self.Bind(wx.EVT_BUTTON, self.OnQuit, id=wx.ID_EXIT)

        panel.SetSizer(grid)
        self.Centre()
        self.Show(True)

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

app = wx.App()
Identifiers(None, -1, '')
app.MainLoop()

V předchozí ukázce jsme použili předdefinované identifikátory na tlačítka. Na linuxu budou mít tato tlačítka automaticky malé ikony.

wx.Focus event

Pomocí focusu dává aplikace vědět uživateli, která komponenta je zrovna vybraná. Stisky kláves a další události jsou odeslány komponentě, která má focus. K jeho manipulaci existují dva ovladače: wx.EVT_SET_FOCUS je zavolán, když daná komponenta získá focus. Druhý ovladač je wx.EVT_KILL_FOCUS. Ten je zavolán vždy, když komponenta ztratí focus. Focus se mění kliknutím myši na jinou komponentu nebo pomocí klávesové zkratky (obvykle Tab/Shift+Tab).

import wx


class MyWindow(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent, -1)

        self.color = '#b3b3b3'

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
        self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)

    def OnPaint(self, event):
        dc = wx.PaintDC(self)

        dc.SetPen(wx.Pen(self.color))
        x, y = self.GetSize()
        dc.DrawRectangle(0, 0, x, y)

    def OnSize(self, event):
        self.Refresh()

    def OnSetFocus(self, event):
        self.color = '#0099f7'
        self.Refresh()

    def OnKillFocus(self, event):
        self.color = '#b3b3b3'
        self.Refresh()

class FocusEvent(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(350, 250))

        grid = wx.GridSizer(2, 2, 10, 10)
        grid.AddMany([(MyWindow(self), 1, wx.EXPAND|wx.TOP|wx.LEFT,9),
            (MyWindow(self), 1, wx.EXPAND|wx.TOP|wx.RIGHT, 9), 
            (MyWindow(self), 1, wx.EXPAND|wx.BOTTOM|wx.LEFT, 9), 
            (MyWindow(self), 1, wx.EXPAND|wx.BOTTOM|wx.RIGHT, 9)])


        self.SetSizer(grid)
        self.Centre()
        self.Show(True)

app = wx.App()
FocusEvent(None, -1, 'focus event')
app.MainLoop()

V předchozím příkladě jsme měli čtyři panely. Panel s focusem byl zvýrazněn.

wx.SizeEvent

Událost wx.SizeEvent je vygenerována vždy, když se změní velikost okna. V následujícím příkladu zobrazujeme aktuální velikost okna v titlebaru.

import wx

class SizeEvent(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title)

        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Centre()
        self.Show(True)

    def OnSize(self, event):
        self.SetTitle(str(event.GetSize()))


app = wx.App()
SizeEvent(None, 1, 'sizeevent.py')
app.MainLoop()

Nastavení titulku okna provádíme metodou SetTitle().

self.SetTitle(str(event.GetSize()))

Aktuální velikost okna získáme zavoláním metody GetSize().

wx.PaintEvent

Kdykoliv se okno překresluje, je vyvolána událost wx.PaintEvent. Okno se překresluje celkem často. Například když změníte jeho velikost, nebo ho maximalizujete.

import wx

class PaintEvent(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title)

        self.count = 0
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Centre()
        self.Show(True)

    def OnPaint(self, event):
        self.count = self.count + 1
        print self.count
        event.Skip()

app = wx.App()
PaintEvent(None, -1, 'paintevent.py')
app.MainLoop()

wx.KeyEvent

Pokud uživatel zmáčkne nějakou klávesu, vyvolá se událost wx.KeyEvent. Tato událost je poslána komponentě, která má v té době focus. Pro zpracování wx.KeyEvent existují tři různé ovladače:

  • wx.EVT_KEY_DOWN
  • wx.EVT_KEY_UP
  • wx.EVT_CHAR

Je celkem obvyklé, že pokud uživatel zmáčkne klávesu Escape, aplikace se zavře. A to se také pokusíme naprogramovat v následujícím kódu:

import wx

class KeyEvent(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title)

        panel = wx.Panel(self, -1)
        panel.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
        panel.SetFocus()

        self.Centre()
        self.Show(True)


    def OnKeyDown(self, event):
        keycode = event.GetKeyCode()
        if keycode == wx.WXK_ESCAPE:
            ret  = wx.MessageBox('Are you sure to quit?', 'Question', 
		wx.YES_NO | wx.NO_DEFAULT, self)
            if ret == wx.YES:
                self.Close()
        event.Skip()


app = wx.App()
KeyEvent(None, -1, 'keyevent.py')
app.MainLoop()

Analýza kódu:

keycode = event.GetKeyCode()

Získali jsme kód stisknuté klávesy.

if keycode == wx.WXK_ESCAPE:

Zde kontrolujeme, jaká klávesa byla zmáčknuta. Kód klávesy Escape je wx.WXK_ESCAPE.

Zdroj: http://zetcode.com/wxpython/events/

×Odeslání článku na tvůj Kindle

Zadej svůj Kindle e-mail a my ti pošleme článek na tvůj Kindle.
Musíš mít povolený příjem obsahu do svého Kindle z naší e-mailové adresy kindle@programujte.com.

E-mailová adresa (např. novak@kindle.com):

TIP: Pokud chceš dostávat naše články každé ráno do svého Kindle, koukni do sekce Články do Kindle.

Hlasování bylo ukončeno    
0 hlasů
Google
Autor studuje na FIT ČVUT a je šéfredaktorem portálu Matematika pro každého.
Web    

Nové články

Obrázek ke článku Blockchain & Bitcoin konference

Blockchain & Bitcoin konference

V pátek 19. 5. 2017 se v pražském konferenčním centru Andel’s konala Blockchain & Bitcoin konference. Řada odborníků a podnikatelů v oboru blockchainu a kryptoměn představila možnosti budoucího směřování tohoto oboru. Speakeři většinou rusky mluvící provenience prezentovali řešení svých firem založená na technologii blockchainu.

Reklama
Reklama
Obrázek ke článku Malware KONNI se úspěšně skrýval 3 roky. Odhalil ho bezpečnostní tým Cisco Talos

Malware KONNI se úspěšně skrýval 3 roky. Odhalil ho bezpečnostní tým Cisco Talos

Bezpečnostní tým Cisco Talos odhalil celkem 4 kampaně dosud neobjeveného malwaru, který dostal jméno KONNI. Ten se dokázal úspěšně maskovat od roku 2014. Zpočátku se malware zaměřoval pouze na krádeže citlivých dat. Za 3 roky se ale několikrát vyvinul, přičemž jeho současná verze umožňuje útočníkovi z infikovaného počítače nejenom krást data, ale i mapovat stisky na klávesnici, pořizovat screenshoty obrazovky či v zařízení spustit libovolný kód. Pro odvedení pozornosti oběti zasílali útočníci v příloze také obrázek, zprávu a výhružkách severokorejského režimu či kontakty na členy mezinárodních organizací.

Obrázek ke článku Pouze jedna z deseti lokálních firem ví o pokutách plynoucích z GDPR

Pouze jedna z deseti lokálních firem ví o pokutách plynoucích z GDPR

Trend Micro, celosvětový lídr v oblasti bezpečnostních řešení a VMware, přední světový dodavatel cloudové infrastruktury a řešení pro podnikovou mobilitu, oznámily výsledky výzkumu mezi českými a slovenskými manažery zodpovědnými za ochranu osobních údajů, který zjišťoval, jak jsou připraveni na nové nařízení o ochraně osobních údajů (GDPR). Většina firem v České republice a na Slovensku nad 100 zaměstnanců je již s novým nařízením GDPR obeznámena. Výzkum provedený ve spolupráci s agenturou Ipsos ukázal, že téměř 8 firem z 10 o nařízení ví, přičemž jeho znalost je o něco vyšší na Slovensku (89 %) než v České republice (69 %).

Obrázek ke článku Vyděračský software Locky se vrací, tváří se jako potvrzení platby, odhalil tým Cisco Talos

Vyděračský software Locky se vrací, tváří se jako potvrzení platby, odhalil tým Cisco Talos

Jeden z nejznámějších ransomwarů, Locky, se vrací. Po většinu roku 2016 patřil mezi nejrozšířenější vyděračské softwary. Ke svému šíření využíval emailové kampaně s infikovanými přílohami. Ransomware Locky byl rozesílán prostřednictvím botnetu (internetový robot zasílající spamy) Necurs. Jeho aktivita na konci roku 2016 téměř upadla a spolu s ní i šíření ransomwaru Locky. Před několika týdny se Necurs opět probudil a začal posílat spamy nabízející výhodný nákup akcií. Dne 21. dubna zaznamenal bezpečnostní tým Cisco Talos první velkou kampaň ransomwaru Locky prostřednictvím botnetu Necurs za posledních několik měsíců.

loadingtransparent (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })();
Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032017 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý