Events

The base clutter.Actor has several signals that are emitted when the user interacts with the actor:

For instance, you can detect button clicks on an actor like so:

actor.connect("button-press-event", on_rect_button_press)

Alternatively, you might just handle signals from the parent clutter.Stage and use stage.get_actor_at_pos to discover which actor should be affected.

However, as a performance optimization, PyClutter does not emit all event signals by default. For instance, to receive event signals for an actor instead of just the stage, you must call actor.set_reactive(). If you don't need the motion event signals (motion-event, enter-event and leave-event), you may call the global clutter.set_motion_events_enabled() function with False to further optimize performance.

Your event signal handler should return True when it has fully handled the event, or False if you want the event to be sent also to the next actor in the event chain. PyClutter first allows the stage to handle each event via the captured-event signal. But if the stage does not handle the event then it will be passed down to the child actor, first passing through the actor's parent containers, giving each actor in the hierarchy a chance to handle the event via a captured-event signal handler. If the event has still not been handled fully by any actor then the event will then be emitted via a specific signal (such as button-press-event or key-press-event. These specific signals are emitted first from the child actor, then by its parent, passing all they way back up to the stage if no signal handler returns true to indicate that it has handled the event fully.

Actors usually only receive keyboard events when the actor has key focus, but you can give an actor exclusive access to any events by grabbing either the pointer or the keyboard, using clutter.grab_pointer() or clutter.grab_keyboard().

Example

The following example demonstrates handling of clicks on an actor:

Figure 4.4. Actor Events

Actor Events

Source Code

File: main.py

import sys

import clutter


def on_stage_button_press(stage, event):
    print "Clicked stage at (%f, %f)" % (event.x, event.y)

    # Discover whether there is an actor at that position.
    # Note that you can also connect directly to the actor's signals instead.
    rect = stage.get_actor_at_pos(clutter.PICK_ALL, int(event.x), int(event.y))
    if not rect:
        return False

    if isinstance(rect, clutter.Rectangle):
        print "  A rectangle is at that position."

    return True # Stop further handling of this event.


def on_rect_button_press(stage, event):
    print "Clicked rectangle at (%f, %f)" % (event.x, event.y)

    # clutter.main_quit()

    return True # Stop further handling of this event.


def on_rect_button_release(stage, event):
    print "Click-release on rectangle at (%f, %f)" % (event.x, event.y)

    return True # Stop further handling of this event.


def on_rect_motion(stage, event):
    print "Motion in the rectangle."

    return True # Stop further handling of this event.


def on_rect_enter(stage, event):
    print "Entered rectangle."

    return True # Stop further handling of this event.


def on_rect_leave(stage, event):
    print "Left rectangle."

    return True # Stop further handling of this event.


def main():
    stage_color = clutter.Color(0, 0, 0, 255)
    label_color = clutter.Color(255, 255, 255, 153)

    # Get the stage and set its size and color
    stage = clutter.Stage()
    stage.set_size(200, 200)
    stage.set_color(stage_color)

    # Connect signal handlers to handle mouse clicks on the stage
    stage.connect('button-press-event', on_stage_button_press)

    # Add a rectangle to the stage
    rect = clutter.Rectangle(label_color)
    rect.set_size(100, 100)
    rect.set_position(50, 50)
    rect.show()
    stage.add(rect)

    # Allow the actor to emit events.
    # By default only the stage does this.
    rect.set_reactive(True)

    # Connect signal handlers for events
    rect.connect('button-press-event', on_rect_button_press)
    rect.connect('button-release-event', on_rect_button_release)
    rect.connect('motion-event', on_rect_motion)
    rect.connect('enter-event', on_rect_enter)
    rect.connect('leave-event', on_rect_leave)

    # Show the stage
    stage.connect('destroy', clutter.main_quit)
    stage.show()

    # Start the main loop, so we can respond to events
    clutter.main()

    return 0


if __name__ == '__main__':
    sys.exit(main())