WxPython FAQ Events

Материал из Wiki.crossplatform.ru

(Различия между версиями)
Перейти к: навигация, поиск
(Новая: Events are integral part of every GUI application. All GUI applications are event-driven. An application reacts to different event types which are generated during its life. Events are g...)
(Удалено по требованию автора...)
 
Строка 1: Строка 1:
-
Events are integral part of every GUI application. All GUI applications are event-driven. An application reacts to different event types which are generated during its life. Events are generated mainly by the user of an application. But they can be generated by other means as well. e.g. internet connection, window manager, timer. So when we call MainLoop() method, our application waits for events to be generated. The MainLoop() method ends when we exit the application.
 
-
== Definitions ==
 
-
 
-
<b>Event</b> is a piece of application-level information from the underlying framework, typically the GUI toolkit.
 
-
<b>Event loop</b> is  a programming construct that waits for and dispatches events or messages in a program. The event loop repeatedly looks for events to process. A <b>dispatcher</b> is a process which maps events to <b>event handlers</b>. Event handlers are methods that react to events. 
 
-
 
-
<b>Event object</b> is an object associated with the event. It is usually a window. <b>Event type</b> is a unique event, that has been generated. <b>Event binder</b> is an object, that binds an event type with an event handler.
 
-
 
-
== A simple event example ==
 
-
In the following section we will describe a simple event. We will talk about a move event.
 
-
 
-
A move event is generated, when we move a window to a new position. The event type is <b>wx.MoveEvent</b>. The event binder for this event is <b>wx.EVT_MOVE</b>
 
-
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
 
-
# moveevent.py
 
-
 
-
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()
 
-
</source>
 
-
 
-
The example displays the current position of the window.
 
-
 
-
<source lang="python">
 
-
self.Bind(wx.EVT_MOVE, self.OnMove)
 
-
</source>
 
-
 
-
Here we bind the <i>wx.EVT_MOVE</i> event binder to the <i>OnMove()</i> method.
 
-
 
-
<source lang="python">
 
-
def OnMove(self, event):
 
-
    x, y = event.GetPosition()
 
-
</source>
 
-
 
-
The event parameter in the <i>OnMove()</i> method is an object specific to a particular event type. In our case it is the instance of a <i>wx.MoveEvent</i> class. This object holds information about the event. For example the Event object or the position of the window. In our case the Event object is the wx.Frame widget. We can find out the current position by calling the <i>GetPosition()</i> method of the event.
 
-
 
-
[[image: wxPython_faq_moveevent.jpg | center]]
 
-
 
-
== Event binding ==
 
-
Working with events is straightforward in wxPython. There are three steps:
 
-
 
-
* Identify the event binder name: wx.EVT_SIZE, wx.EVT_CLOSE etc
 
-
* Create an event handler. It is a method, that is called, when an event is generated
 
-
* Bind an event to an event handler.
 
-
In wxPython we say to bind a method to an event. Sometimes a word hook is used.
 
-
 
-
You bind an event by calling the Bind() method. The method has the following parameters:
 
-
 
-
<source lang="python">
 
-
Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY)
 
-
</source>
 
-
* event is one of EVT_* objects. It specifies the type of the event.
 
-
* handler is an object to be called. In other words, it is a method, that a programmer binds to an event.
 
-
* source parameter is used when we want to differentiate between the same event type from different widgets.
 
-
* id parameter is used, when we have multiple buttons, menu items etc. The id is used to differentiate among them.
 
-
* id2 is used when it is desirable to bind a handler to a range of ids, such as with EVT_MENU_RANGE.
 
-
 
-
Note that method Bind() is defined in class EvtHandler. It is the class, from which wx.Window inherits. wx.Window is a base class for most widgets in wxPython. There is also a reverse process. If we want to unbind a method from an event, we call the Unbind() method. It has the same paremeters as the above one.
 
-
 
-
== Vetoing events ==
 
-
Sometimes we need to stop processing an event. To do this, we call the method <i>Veto()</i>.
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# veto.py
 
-
 
-
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()
 
-
</source>
 
-
 
-
In our example, we process a <i>wx.CloseEvent</i>. This event is called, when we click the X button on the titlebar, press Alt + F4 or select close from the system menu. In many applications, we want to prevent from accidentally closing the window, if we made some changes. To do this, we must bind the <i>wx.EVT_CLOSE</i> event binder.
 
-
 
-
<source lang="python">
 
-
dial = wx.MessageDialog(None, 'Are you sure to quit?', 'Question',
 
-
    wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
 
-
ret = dial.ShowModal()
 
-
</source>
 
-
 
-
During the close event, we show a message dialog.
 
-
 
-
<source lang="python">
 
-
if ret == wx.ID_YES:
 
-
    self.Destroy()
 
-
else:
 
-
    event.Veto()
 
-
</source>
 
-
 
-
Depending on the return value, we destroy the window, or veto the event. Notice that to close the window, we must call the <i>Destroy()</i> method. By calling the <i>Close()</i> method, we would end up in an endless cycle.
 
-
 
-
== Event propagation ==
 
-
There are two types of events. Basic events and command events. They differ in propagation. Event propagation is travelling of events from child widgets to parent widgets and grand parent widgets etc. Basic events do not propagate. Command events do propagate. For example <i>wx.CloseEvent</i> is a basic event. It does not make sense for this event to propagate to parent widgets.
 
-
 
-
By default, the event that is catched in a event handler stops propagating. To continue propagation, we must call the <i>Skip()</i> method.
 
-
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# propagate.py
 
-
 
-
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()
 
-
</source>
 
-
 
-
In our example, we have a button on a panel. The panel is placed in a frame widget. We define a handler for all widgets.
 
-
 
-
<source lang="python">
 
-
event reached button class
 
-
event reached panel class
 
-
event reached frame class
 
-
</source>
 
-
 
-
We get this, when we click on the button. The event travels from the button to panel and to frame.
 
-
 
-
Try to omit some Skip() methods and see, what hapens.
 
-
 
-
== Window identifiers ==
 
-
 
-
Window identifiers are integers that uniquely determine the window identity in the event system.
 
-
There are three ways to create window id's.
 
-
 
-
* let the system automatically create an id
 
-
* use standard identifiers
 
-
* create your own id
 
-
 
-
Each widget has an id parameter. This is a unique number in the event system. If we work with multiple widgets, we must differantiate among them.
 
-
 
-
<source lang="python">
 
-
wx.Button(parent, -1)
 
-
wx.Button(parent, wx.ID_ANY)
 
-
</source>
 
-
 
-
If we provide -1 or wx.ID_ANY for the id parameter, we let the wxPython automatically create an id for us. The automatically created id's are always negative, whereas user specified id's must always be positive. We usually use this option when we do not need to change the widget state. For example a static text, that will never be changed during the life of the application. We can still get the id, if we want. There is a method <i>GetId()</i>, which will determine the id for us.
 
-
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# automaticids.py
 
-
 
-
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()
 
-
</source>
 
-
 
-
In this example, we do not care about the actual id value.
 
-
 
-
<source lang="python">
 
-
self.Bind(wx.EVT_BUTTON,  self.OnExit, id=exit.GetId())
 
-
</source>
 
-
 
-
We get the automatically generated id by calling the <i>GetId()</i> method.
 
-
 
-
Standard identifiers should be used whenever possible. The identifiers can provide some standard graphics or behaviour on some platforms.
 
-
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# identifiers.py
 
-
 
-
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()
 
-
</source>
 
-
 
-
In our example we use standard identifiers on buttons. On linux, the buttons have small icons.
 
-
 
-
[[image: wxPython_faq_identifiers.jpg | center]]
 
-
 
-
The last option is to use own identifiers. We define our own global ids.
 
-
 
-
== Miscellaneous events ==
 
-
 
-
=== Focus event ===
 
-
 
-
The focus indicates the currently selected widget in application. The text entered from the keyboard or pasted from the clipboard is sent to the widget, which has the focus. There are two event types concerning focus. The <b>wx.EVT_SET_FOCUS</b> event, which is generated when a widget receives focus. The <b>wx.EVT_KILL_FOCUS</b> is generated, when the widget looses focus. The focus is changed by clicking or by a keybord key. Usually Tab/Shift+Tab.
 
-
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# focusevent.py
 
-
 
-
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()
 
-
</source>
 
-
 
-
In our example, we have four panels. The panel with focus is highlighted.
 
-
 
-
[[image: wxPython_faq_focusevent.jpg | center]]
 
-
 
-
=== ScrollEvent ===
 
-
The following code is an example of a wx.ScrollWinEvent. This event is generated, when we click on a built in Scrollbar. Built-in Scrollbar is activated with the SetScrollbar() method call. For stand-alone Scrollbars, there is another event type, namely wx.ScrollEvent.
 
-
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# myscrollwinevent.py
 
-
 
-
import wx
 
-
 
-
class ScrollWinEvent(wx.Frame):
 
-
    def __init__(self, parent, id, title):
 
-
        wx.Frame.__init__(self, parent, id, title)
 
-
        panel = wx.Panel(self, -1)
 
-
        self.st = wx.StaticText(panel, -1, '0', (30,0))
 
-
        panel.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
 
-
        panel.SetScrollbar(wx.VERTICAL, 0, 6, 50);
 
-
        self.Centre()
 
-
        self.Show(True)
 
-
 
-
    def OnScroll(self, evt):
 
-
        y = evt.GetPosition()
 
-
        self.st.SetLabel(str(y))
 
-
 
-
app = wx.App()
 
-
ScrollWinEvent(None, -1, 'scrollwinevent.py')
 
-
app.MainLoop()
 
-
</source>
 
-
 
-
=== SizeEvent ===
 
-
A wx.SizeEvent is generated, when our window is resized. In our example, we show the size of the window in the titlebar.
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# sizeevent.py
 
-
 
-
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()
 
-
 
-
</source>
 
-
 
-
 
-
<source lang="python">
 
-
self.SetTitle(str(event.GetSize()))
 
-
</source>
 
-
 
-
To get the current size of the window, we call the <i>GetSize()</i> method of the event object.
 
-
 
-
[[image: wxPython_faq_sizeevent.png | center]]
 
-
 
-
=== PaintEvent ===
 
-
A paint event is generated when a window is redrawn. This happens when we resize a window or when we maximize it. A paint event can be generated programatically as well. For example, when we call SetLabel() method to change a wx.StaticText widget. Note that when we minimize a window, no paint event is generated.
 
-
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# paintevent.py
 
-
 
-
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
 
-
 
-
 
-
app = wx.App()
 
-
PaintEvent(None, -1, 'paintevent.py')
 
-
app.MainLoop()
 
-
</source>
 
-
 
-
In our example we print the number of paint events generated into the console.
 
-
 
-
=== KeyEvent ===
 
-
When we press a key on our keyboard, wx.KeyEvent is generated.  This event is sent to the widget that has currently focus.
 
-
 
-
There are three different key handlers:
 
-
* wx.EVT_KEY_DOWN
 
-
* wx.EVT_KEY_UP
 
-
* wx.EVT_CHAR
 
-
A common request is to close application, when Esc key is pressed.
 
-
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# keyevent.py
 
-
 
-
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()
 
-
</source>
 
-
 
-
<source lang="python">
 
-
keycode = event.GetKeyCode()
 
-
</source>
 
-
 
-
Here we get the key code of the pressed key.
 
-
 
-
<source lang="python">
 
-
if keycode == wx.WXK_ESCAPE:
 
-
</source>
 
-
 
-
We check the key code. The Esc key has <i>wx.WXK_ESCAPE</i> code.
 
-
 
-
[[Категория:wxWidgets]]
 
-
[[Категория:Python]]
 

Текущая версия на 12:00, 7 апреля 2009