PyGTK FAQ Signals & events

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

(Различия между версиями)
Перейти к: навигация, поиск
(Новая: In this part of the PyGTK programming tutorial, we will talk about signals & events. All GUI applications are event driven. PyGTK applications are no exception. The applications sta...)
(Удалено по требованию автора...)
 
(1 промежуточная версия не показана)
Строка 1: Строка 1:
-
In this part of the PyGTK programming tutorial, we will talk about signals & events.
 
-
All GUI applications are event driven. PyGTK applications are no exception. The applications start a main loop with the <b>gtk.main()</b> call, which continuously checks for newly generated events. If there is no event, the application waits and does nothing.
 
-
 
-
 
-
<b>Events</b> are messages from the X server to the application.
 
-
When we click on a button widget, the clicked signal will be <b>emitted</b>.
 
-
There are signals that all widgets inherit, such as destroy, and there are signals that are widget specific, such as toggled on a toggle button.
 
-
 
-
Programmers use signal handlers to react to various signals. These handlers are called <b>callbacks</b> among GTK programmers.
 
-
 
-
<source lang="python">
 
-
handler_id = button.connect("clicked", self.on_clicked)
 
-
</source>
 
-
 
-
Here we use the <b>connect()</b> method of the GObject class, (GtkButton is a GObject), to connect a callback <b>clicked</b>.
 
-
The connect() method returns a handler id, which is used to uniquely identify the callback method. The id can be used with the following methods:
 
-
 
-
<source lang="python">
 
-
def disconnect(handler_id)
 
-
def handler_disconnect(handler_id)
 
-
def handler_is_connected(handler_id)
 
-
def handler_block(handler_id)
 
-
def handler_unblock(handler_id)
 
-
</source>
 
-
 
-
These methods enable to disconnect a handler from an GObject, or block/unblock it.
 
-
 
-
== Signals vs events ==
 
-
There is generally a lot of confusion about the difference between the two.
 
-
 
-
Signals and events are two different things.
 
-
An event is an almost one-to-one mapping of window system events.
 
-
Key press, window resizement or button press are typical window system events. Window system events are reported to the application main loop.
 
-
Gdk interprets the window system events and passes them along via signals.
 
-
 
-
A signal is nothing other than a callback mechanism.
 
-
If one object wants to be notified about an other object's action or state change, it registers a callback. When the object emits a signal, it looks in the list of callbacks which have been registered with it and calls the callback(s) for the specific signal. It can optionally send some predefined data with it.
 
-
 
-
Signals are a general purpose notification framework.
 
-
They are not used only for notifications about UI changes.
 
-
They can be used for notifications about application state changes. Signals are general, powerful, their usage is very broad.
 
-
Any GObject can emit and receive a signal.
 
-
A type may have one or more signals, each of which may  have an argument list and return value. Handlers can then be connected to instances of the type. When the signal is emitted on an instance, each of the connected handlers will be called.
 
-
 
-
The only connection between signals and events is that signals are used  to send notifications about events from the X server.
 
-
 
-
Signals are a feature of gtk.Object and its subclasses, events are a Gdk/Xlib concept.
 
-
 
-
== Simple example ==
 
-
The next example shows, how we react to two basic signals.
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# ZetCode PyGTK tutorial
 
-
#
 
-
# The example shows how to work with
 
-
# destroy and clicked signals
 
-
#
 
-
# author: jan bodnar
 
-
# website: zetcode.com
 
-
# last edited: February 2009
 
-
 
-
import gtk
 
-
 
-
class PyApp(gtk.Window):
 
-
    def __init__(self):
 
-
        super(PyApp, self).__init__()
 
-
       
 
-
        self.set_title("Quit Button")
 
-
        self.set_size_request(250, 200)
 
-
        self.set_position(gtk.WIN_POS_CENTER)
 
-
        self.connect("destroy", self.on_destroy)
 
-
       
 
-
        fixed = gtk.Fixed()
 
-
 
-
        quit = gtk.Button("Quit")
 
-
        quit.connect("clicked", self.on_clicked)
 
-
        quit.set_size_request(80, 35)
 
-
 
-
        fixed.put(quit, 50, 50)
 
-
 
-
        self.add(fixed)
 
-
        self.show_all()
 
-
       
 
-
    def on_destroy(self, widget):
 
-
        gtk.main_quit()
 
-
       
 
-
    def on_clicked(self, widget):
 
-
        gtk.main_quit()
 
-
 
-
 
-
PyApp()
 
-
gtk.main()
 
-
</source>
 
-
 
-
The destroy signal is triggered, when we close the window.  By default, the application does not quit, when we click on the close button in the titlebar.
 
-
 
-
<source lang="python">
 
-
self.connect("destroy", self.on_destroy)
 
-
</source>
 
-
 
-
The <b>connect()</b> method plugs the <b>on_destroy()</b> method to the <b>destroy</b> signal.
 
-
 
-
<source lang="python">
 
-
quit.connect("clicked", self.on_clicked)
 
-
</source>
 
-
 
-
Pressing the quit button, the <b>clicked</b> signal is triggered. When we click on the quit button, we call the <b>on_clicked()</b> method.
 
-
 
-
<source lang="python">
 
-
def on_destroy(self, widget):
 
-
    gtk.main_quit()
 
-
</source>
 
-
 
-
In the <b>on_destroy()</b> method, we react to the <b>destroy</b> signal. We call the <b>gtk.main_quit()</b> method, which terminates the application.
 
-
 
-
<source lang="python">
 
-
def on_clicked(self, widget):
 
-
    gtk.main_quit()
 
-
</source>
 
-
 
-
Here is the <b>on_clicked()</b> method. It takes two parameters. The widget parameter is the object, which triggered this signal. In our case it is the quit button. Different objects send different signals. Signals and the parameters sent to methods can be found in the reference manual of the PyGTK library.
 
-
 
-
[http://pygtk.org/docs/pygtk/index.html pygtk.org/docs/pygtk/index.html]
 
-
 
-
== Creating a custom signal ==
 
-
In the following code example, we create and send a custom singal.
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# ZetCode PyGTK tutorial
 
-
#
 
-
# This example shows how to create
 
-
# and send a custom singal
 
-
#
 
-
# author: jan bodnar
 
-
# website: zetcode.com
 
-
# last edited: February 2009
 
-
 
-
import gobject
 
-
 
-
 
-
class Sender(gobject.GObject):
 
-
    def __init__(self):
 
-
        self.__gobject_init__()
 
-
       
 
-
gobject.type_register(Sender)
 
-
gobject.signal_new("z_signal", Sender, gobject.SIGNAL_RUN_FIRST,
 
-
                  gobject.TYPE_NONE, ())
 
-
 
-
 
-
class Receiver(gobject.GObject):
 
-
    def __init__(self, sender):
 
-
        self.__gobject_init__()
 
-
       
 
-
        sender.connect('z_signal', self.report_signal)
 
-
       
 
-
    def report_signal(self, sender):
 
-
        print "Receiver reacts to z_signal"
 
-
 
-
 
-
def user_callback(object):
 
-
    print "user callback reacts to z_signal"
 
-
 
-
if __name__ == '__main__':
 
-
   
 
-
    sender = Sender()
 
-
    receiver = Receiver(sender)
 
-
 
-
    sender.connect("z_signal", user_callback)
 
-
    sender.emit("z_signal")
 
-
</source>
 
-
 
-
We create two <b>GObjects</b>. Sender and receiver objects. The sender emits a signal, which is received by the receiver object. We also plug a callback to the signal.
 
-
 
-
<source lang="python">
 
-
class Sender(gobject.GObject):
 
-
    def __init__(self):
 
-
        self.__gobject_init__()
 
-
</source>
 
-
 
-
This is a sender object. It is created with a default constructor.
 
-
 
-
<source lang="python">
 
-
gobject.type_register(Sender)
 
-
gobject.signal_new("z_signal", Sender, gobject.SIGNAL_RUN_FIRST,
 
-
                  gobject.TYPE_NONE, ())
 
-
</source>
 
-
 
-
We register a new object and a new signal. The <b>signal_new()</b> function registers a signal  called <b>z_signal</b> for the Sender object. The <b>SIGNAL_RUN_FIRST</b> parameter means that the default handler of the object that receives the signal is called as first. The last two parameters are the return value type and parameter types. In our example we do not return any value and send no parameters.
 
-
 
-
<source lang="python">
 
-
sender.connect('z_signal', self.report_signal)
 
-
</source>
 
-
 
-
The receiver listens for the <b>z_signal</b>.
 
-
 
-
<source lang="python">
 
-
sender = Sender()
 
-
receiver = Receiver(sender)
 
-
</source>
 
-
 
-
Sender and receiver objects are instantiated. The receiver takes a sender as a parameter, so that it can listen to its signals.
 
-
 
-
<source lang="python">
 
-
sender.connect("z_signal", user_callback)
 
-
 
-
</source>
 
-
 
-
Here we plug the signal to the user callback.
 
-
 
-
<source lang="python">
 
-
sender.emit("z_signal")
 
-
</source>
 
-
 
-
The <b>z_signal</b> is being emitted.
 
-
 
-
<source lang="python">
 
-
class Sender(gobject.GObject):
 
-
 
-
    __gsignals__ = {
 
-
        'z_signal': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
 
-
    }
 
-
 
-
    def __init__(self):
 
-
        self.__gobject_init__()
 
-
       
 
-
gobject.type_register(Sender)
 
-
</source>
 
-
 
-
We can also use the <b>__gsignals__</b> class attribute to register a new singal.
 
-
 
-
== Predefined signal handlers ==
 
-
Objects in PyGTK may have predefined signal handlers. These handlers begin with do_*. For example do_expose(), do_show() or do_clicked().
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# ZetCode PyGTK tutorial
 
-
#
 
-
# This example overrides predefined
 
-
# do_configure_event() signal handler
 
-
#
 
-
# author: jan bodnar
 
-
# website: zetcode.com
 
-
# last edited: February 2009
 
-
 
-
import gtk
 
-
import gobject
 
-
 
-
class PyApp(gtk.Window):
 
-
    __gsignals__ = {
 
-
        "configure-event" : "override"
 
-
        }
 
-
 
-
    def __init__(self):
 
-
        super(PyApp, self).__init__()
 
-
 
-
        self.set_size_request(200, 150)
 
-
        self.set_position(gtk.WIN_POS_CENTER)
 
-
     
 
-
        self.connect("destroy", gtk.main_quit)
 
-
 
-
        self.show_all()
 
-
 
-
    def do_configure_event(self, event):
 
-
       
 
-
        title = "%s, %s" % (event.x, event.y)
 
-
        self.set_title(title)
 
-
        gtk.Window.do_configure_event(self, event)
 
-
     
 
-
 
-
PyApp()
 
-
gtk.main()
 
-
</source>
 
-
When we move or resize a window, the X server sends configure events. These are then transformed into <b>configure-event</b> signals.
 
-
 
-
In our code example, we display the x, y coordinates of the top-left corner of the window in the titlebar. We could simply connect a signal handler to the <b>configure-event</b> signal. But we take a different strategy. We override the default class handler, where we implement the logic needed.
 
-
 
-
<source lang="python">
 
-
__gsignals__ = {
 
-
    "configure-event" : "override"
 
-
    }
 
-
</source>
 
-
 
-
This tells, that we are going to override the default <b>on_configure_event()</b> method.
 
-
 
-
<source lang="python">
 
-
def do_configure_event(self, event):
 
-
       
 
-
    title = "%s, %s" % (event.x, event.y)
 
-
    self.set_title(title)
 
-
    gtk.Window.do_configure_event(self, event)
 
-
</source>
 
-
 
-
Here we override the predefined <b>do_configure_event()</b> method. We set the x, y coordinates of the window to the title of the window.  Also note the last line. It explicitely calls the superclass <b>do_configure_event()</b> method. This is beacuse it does some important job. Try to comment this line to see what happens. Resizing of windows would not work correctly. If we override a default handler, we may or may not call the superclass method. In our case we have to.
 
-
 
-
[[image: pygtk_faq_move.png | center]]
 
-
 
-
== Signals of a button ==
 
-
The following example shows various button signals.
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# ZetCode PyGTK tutorial
 
-
#
 
-
# This program shows various signals
 
-
# of a button widget
 
-
# It emits a button-release-event which
 
-
# triggers a released singal
 
-
#
 
-
# author: jan bodnar
 
-
# website: zetcode.com
 
-
# last edited: February 2009
 
-
 
-
import gtk
 
-
 
-
class PyApp(gtk.Window):
 
-
    def __init__(self):
 
-
        super(PyApp, self).__init__()
 
-
       
 
-
        self.set_title("Signals")
 
-
        self.set_size_request(250, 200)
 
-
        self.set_position(gtk.WIN_POS_CENTER)
 
-
        self.connect("destroy", gtk.main_quit)
 
-
       
 
-
        fixed = gtk.Fixed()
 
-
       
 
-
        self.quit = gtk.Button("Quit")
 
-
       
 
-
        self.quit.connect("pressed", self.on_pressed)
 
-
        self.quit.connect("released", self.on_released)
 
-
        self.quit.connect("clicked", self.on_clicked)
 
-
       
 
-
        self.quit.set_size_request(80, 35)
 
-
 
-
        fixed.put(self.quit, 50, 50)
 
-
       
 
-
        self.add(fixed)
 
-
        self.show_all()
 
-
        self.emit_signal()
 
-
       
 
-
    def emit_signal(self):
 
-
               
 
-
        event = gtk.gdk.Event(gtk.gdk.BUTTON_RELEASE)
 
-
        event.button = 1
 
-
        event.window = self.quit.window
 
-
        event.send_event = True
 
-
               
 
-
        self.quit.emit("button-release-event", event)
 
-
       
 
-
       
 
-
    def on_clicked(self, widget):
 
-
        print "clicked"
 
-
       
 
-
    def on_released(self, widget):
 
-
        print "released"
 
-
       
 
-
    def on_pressed(self, widget):
 
-
        print "pressed"
 
-
 
-
 
-
PyApp()
 
-
gtk.main()
 
-
</source>
 
-
 
-
A button can emit more than just one type of signal.  We work with three of them. The <b>clicked</b>, <b>released</b> signals. We also show, how an event signal triggers another signal.
 
-
 
-
<source lang="python">
 
-
self.quit.connect("pressed", self.on_pressed)
 
-
self.quit.connect("released", self.on_released)
 
-
self.quit.connect("clicked", self.on_clicked)
 
-
</source>
 
-
 
-
We register callbacks for all three signals.
 
-
 
-
<source lang="python">
 
-
self.emit_signal()
 
-
</source>
 
-
 
-
Upon the start of the application, we emit a specific signal.
 
-
 
-
<source lang="python">
 
-
def emit_signal(self):
 
-
               
 
-
    event = gtk.gdk.Event(gtk.gdk.BUTTON_RELEASE)
 
-
    event.button = 1
 
-
    event.window = self.quit.window
 
-
    event.send_event = True
 
-
               
 
-
    self.quit.emit("button-release-event", event)
 
-
</source>
 
-
 
-
We emit the <b>button-release-event</b> signal. It takes an <b>Event</b> object as a parameter. After the application starts, we should see "released" text in  our console window. When we click on the button, all three signals are triggered.
 
-
 
-
== Blocking an event handler ==
 
-
We can block a signal handler. The next example shows this.
 
-
<source lang="python">
 
-
#!/usr/bin/python
 
-
# ZetCode PyGTK tutorial
 
-
#
 
-
# This example shows how to block/unblock
 
-
# a signal handler
 
-
#
 
-
# author: jan bodnar
 
-
# website: zetcode.com
 
-
# last edited: February 2009
 
-
 
-
import gtk
 
-
 
-
class PyApp(gtk.Window):
 
-
 
-
    def __init__(self):
 
-
        super(PyApp, self).__init__()
 
-
 
-
        self.set_title("Blocking a callback")
 
-
        self.set_size_request(250, 180)
 
-
        self.set_position(gtk.WIN_POS_CENTER)
 
-
       
 
-
        fixed = gtk.Fixed()
 
-
        button = gtk.Button("Click")
 
-
        button.set_size_request(80, 35)
 
-
        self.id = button.connect("clicked", self.on_clicked)
 
-
        fixed.put(button, 30, 50)
 
-
 
-
        check = gtk.CheckButton("Connect")
 
-
        check.set_active(True)
 
-
        check.connect("clicked", self.toggle_blocking, button)
 
-
        fixed.put(check, 130, 50)
 
-
 
-
 
-
        self.connect("destroy", gtk.main_quit)
 
-
 
-
        self.add(fixed)
 
-
        self.show_all()
 
-
 
-
    def on_clicked(self, widget):
 
-
        print "clicked"
 
-
 
-
    def toggle_blocking(self, checkbox, button):
 
-
        if checkbox.get_active():
 
-
          button.handler_unblock(self.id)
 
-
        else:
 
-
          button.handler_block(self.id)
 
-
 
-
PyApp()
 
-
gtk.main()
 
-
</source>
 
-
 
-
In the code example, we have a button and a check box. We show "clicked" text in the console, when we click on the button and the check box is active.
 
-
The check box blocks/unblocks a handler method from the button <b>clicked</b> signal.
 
-
 
-
<source lang="python">
 
-
self.id = button.connect("clicked", self.on_clicked)
 
-
</source>
 
-
 
-
The <b>connect()</b> method returns a handler id. This id is used to block and unblock the handler.
 
-
 
-
<source lang="python">
 
-
def toggle_blocking(self, checkbox, button):
 
-
    if checkbox.get_active():
 
-
        button.handler_unblock(self.id)
 
-
    else:
 
-
        button.handler_block(self.id)
 
-
</source>
 
-
 
-
These lines block and unblock the callback with the appropriate methods.
 
-
 
-
[[image: pygtk_faq_blocking.png | center]]
 
-
 
-
In this chapter of the PyGTK tutorial, we worked with signals.
 
-
 
-
[[Категория:Python]]
 
-
[[Категория:GTK]]
 

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