This tuto­r­ial is out­dated: it refers to ver­sion 0.4 of clut­ter that now is VERY old.

Last time we have seen how to draw the (maybe) most basic shape of clut­ter: the rec­tan­gles. We also posi­tioned them together in the stage, and said (in part one) that the stage is a spe­cial kind of container.

Today it’s time to explain better what groups are and how to use them, so I’ll intro­duce to you the Clut­ter­Group and the ClutterHBox/ClutterVBox.

In the first part of this tuto­r­ial we said that the stage is a top level “window” on which child actors are placed and manip­u­lated. That’s cor­rect, but we also said that a stage is a spe­cial kind of con­tainer. Con­tainer, but it is self explana­tory, con­tains objects: in the clutter’s case, these objects can be other con­tain­ers or actors.

If you have some expe­ri­ence with GTK, you know that GTK uses HBox and VBox for plac­ing the wid­gets respec­tively hor­i­zon­tally and ver­ti­cally. Clut­ter has the same con­cept with Clut­ter­HBox and Clut­ter­VBox but, since clut­ter uses 3D effects, we also have a depth dimen­sion, and we must be able to handle it in some way. Here comes the Clut­ter­Group: it con­tains objects exactly like HBox/VBox, but they’re not added sequen­tially hor­i­zon­tally or ver­ti­cally, they’re added one up the other. Addi­tion­ally, with Clut­ter­Groups, we can use the rel­a­tive posi­tion­ing (we’ll see how later).

But before to talk about groups, it’s better to talk about HBox/VBox. I already said that HBox or VBox are used to place actors (or even other con­tain­ers) sequen­tially. That said, if we add a label to a HBox and then we add a rec­tan­gle, the label and the rec­tan­gle will be placed sequen­tially start­ing from left.

Maybe it’s better to see this behav­iour directly in action:

import clutter

def main():
    stage = clutter.stage_get_default()
    stage.set_size(420, 200)
    stage.set_color(clutter.color_parse("#FFF"))

    hbox = clutter.HBox()
    hbox.set_position(10, 10)
    rect_h1 = clutter.Rectangle()
    rect_h1.set_size(200, 50)
    rect_h1.set_color(clutter.color_parse("#FF0000"))
    rect_h2 = clutter.Rectangle()
    rect_h2.set_size(200, 50)
    rect_h2.set_color(clutter.color_parse("#800000"))
    rect_h2.set_border_width(1)
    rect_h2.set_border_color(clutter.color_parse("#000000"))

    vbox = clutter.VBox()
    vbox.set_position(10, 80)
    rect_v1 = clutter.Rectangle()
    rect_v1.set_size(200, 50)
    rect_v1.set_color(clutter.color_parse("#00FF00"))
    rect_v2 = clutter.Rectangle()
    rect_v2.set_size(200, 50)
    rect_v2.set_color(clutter.color_parse("#008000"))
    rect_v2.set_border_width(1)
    rect_v2.set_border_color(clutter.color_parse("#000000"))

    rect_h1.show()
    rect_h2.show()
    rect_v1.show()
    rect_v2.show()
    hbox.add(rect_h1, rect_h2)
    vbox.add(rect_v1, rect_v2)
    stage.add(hbox, vbox)

    hbox.show_all()
    vbox.show_all()
    stage.show_all()

    stage.connect("key-press-event", clutter.main_quit)
    clutter.main()

if __name__ == '__main__':
    main()

The above code will pro­duce this:

Example 05: HBox and VBoxes

You may ask your­self why we use mul­ti­ple show() instead of having a single show_all() on the stage. The reason is that show_all() doesn’t recurse inside a Group because you might be hiding some of the actors inside that group for a later use. Anyway, it’s pos­si­ble to sub­class a clutter.Group and over­ride the show_all() method so it recurse inside all its chil­dren, but that’s a bit more advanced topic that I hope I’ll be able to cover in another part of the tutorial.

As you can see, the rec­tan­gles have been placed sequen­tially in hor­i­zon­tal (for the HBox) or ver­ti­cal (for the VBox) direc­tion. In this way we can build a basic inter­face, since we can have nested boxes, that said HBoxes that con­tains other HBoxes or VBoxes (this is true for VBox too, obvi­ously):

import clutter

def main():
    stage = clutter.stage_get_default()
    stage.set_size(420, 200)
    stage.set_color(clutter.color_parse("#FFF"))

    hbox = clutter.HBox()
    hbox.set_position(10, 10)
    rect_h1 = clutter.Rectangle()
    rect_h1.set_size(200, 50)
    rect_h1.set_color(clutter.color_parse("#FF0000"))

    vbox = clutter.VBox()
    vbox.set_position(10, 80)
    rect_v1 = clutter.Rectangle()
    rect_v1.set_size(200, 50)
    rect_v1.set_color(clutter.color_parse("#00FF00"))
    rect_v2 = clutter.Rectangle()
    rect_v2.set_size(200, 50)
    rect_v2.set_color(clutter.color_parse("#008000"))
    rect_v2.set_border_width(1)
    rect_v2.set_border_color(clutter.color_parse("#000000"))

    rect_h1.show()
    rect_v1.show()
    rect_v2.show()
    vbox.add(rect_v1, rect_v2)
    hbox.add(rect_h1, vbox)
    stage.add(hbox)

    hbox.show_all()
    vbox.show_all()
    stage.show_all()

    stage.connect("key-press-event", clutter.main_quit)
    clutter.main()

if __name__ == '__main__':
    main()

It works in this way:

Example 06: nested HBox and VBoxes

Let do a step back: we intro­duced the set_​position method pre­vi­ously, but we didn’t explained exten­sively how this method works. The set_​position set the spec­i­fied actor’s posi­tion to the coor­di­nates rel­a­tives to its father con­tainer. So, if we don’t have any con­tainer it reverts to the stage, that is the father of all the childs in a window (and we have absolute posi­tion­ing), oth­er­wise it will set the actor’s posi­tion to the first father it finds. Let see this scheme to clar­ify a bit:

A scheme illusrtating the relative and absolute positionment

Groups shares most of the prop­er­ties of HBox and VBox, but instead of plac­ing the ele­ments hor­i­zon­tally or ver­ti­cally, it posi­tions them on the z-axis (it put the actors one up the other). In this way we can also have a con­tainer that help us in achiev­ing the rel­a­tive posi­tion­ing:

import clutter

def main():
    stage = clutter.stage_get_default()
    stage.set_size(500, 500)
    stage.set_color(clutter.color_parse("#FFF"))

    rect1, rect2, rect3 = clutter.Rectangle(), clutter.Rectangle(), clutter.Rectangle()
    group1, group2, group3 = clutter.Group(), clutter.Group(), clutter.Group()

    group1.add(rect1, group2)
    group2.add(rect2, group3)
    group3.add(rect3)

    group1.set_position(100, 100)
    group2.set_position(100, 100)
    group3.set_position(100, 100)

    rect1.set_position(0, 0)
    rect2.set_position(0, 0)
    rect3.set_position(0, 0)

    rect1.set_size(150, 150)
    rect2.set_size(150, 150)
    rect3.set_size(150, 150)

    rect1.set_color(clutter.color_parse("#FF000090"))
    rect2.set_color(clutter.color_parse("#00FF0090"))
    rect3.set_color(clutter.color_parse("#0000FF90"))

    stage.add(group1)

    stage.show_all()
    group1.show_all()
    group2.show_all()
    group3.show_all()

    stage.connect("key-press-event", clutter.main_quit)
    clutter.main()

if __name__ == '__main__':
    main()

With the above code, we’ll posi­tion each group rel­a­tively to its father. Only the first group will have the stage as father, all the others are rel­a­tive posi­tioned to the pre­vi­ous stage.

Example 07: groups

That’s all for today :)