GooCanvas

Intro

I'll be using python and pygoocanvas for code samples to talk about goocanvas today

There is good reference documentaion for goocanvas: http://people.gnome.org/~gianmt/pygoocanvas/

Unfortunately, it can be a bit tough to find code samples and tutorial

What is goocanvas

So what is a goocanvas?

A goocanvas is a 2d composing surface

You can use it to make pretty much any kind of image

It's kind of like an api around a drawing program

So you can have a ton of fun using a goocanvas, because you are pretty much freed from the constraints of a widget library in creating your UI

goocanvas is cairo under the coverse

and is designed to easily integrate into your gtk app

How to add one to your app

So let's add a goocanvas to a pygtk app

Add it just like a normal pygtk widget

#set up the goo canvas self.goo_canvas = goocanvas.Canvas() self.goo_canvas.set_size_request(640, 480) self.goo_canvas.show()

Be sure to set the size, otherwise it defaults to 1000,1000, it does not default to the size alloted to it in your window.

Handle window resizing to resize your goocanvas as well

For example, if your goocanvas is in a VBox, you can do this:

rect = self.builder.get_object("vbox2").get_allocation() self.goo_canvas.set_bounds(0,0,rect.width,rect.height)

remember the root item for your goocanvas, you'll need it later often self.root = self.goo_canvas.get_root_item()

The "root" is like the root of an item tree in XML

adding an item in general

So now that we have a goocanvas, we need to add "Items" to it.

Anything that can be added to a goocanvas is an Item. It get's it's capabilities by inheriting from ItemSimple, and by implementing the Item interface.

Let's add an item to the goocanvas to get a look at how it works in general.

We'll start by adding an image.

First, you need to get a gtk.pixbux for your image:

pb = gtk.gdk.pixbuf_new_from_file(path)

Then you calculate where you want the image to show on the goocanvas. You'll need a top and a left to place most items on a goo canvas.

For example, to center the image, I do this:

cont_left, cont_top, cont_right, cont_bottom = self.goo_canvas.get_bounds() img_w = pb.get_width() img_h = pb.get_height() img_left = (cont_right - img_w)/2 img_top = (cont_bottom - img_h)/2

Now I am ready to create the item.

Note that I create the Item, but there is nothing like goocanvas.add(item) rather, when you create the item, you set it's parent property.

The parent property is the root of the goocanvas

This is why I remember the root

goocanvas.Image(pixbuf=pb,parent=self.root, x=img_left,y=img_top)

This basic pattern is how you add all other types of items.

decide where to put the item, and set it's parent property to the root of the goocanvas.

To remove the item from the goocanvas, you don't tell the goocanvas to remove it

rather you tell the item to remove itself

item.remove()

item types

In my mind, there are really 3 types of items

normal items that you add to draw the stuff you want

this includes:

Ellipse, Image, Path, Polyline, Rect, and Text

Layout and gruop items include:

Group, Grid, and Table

And then there is also Widget. Widget is pretty cool.

You can add a gtk widget to your goocanvas, but note that it will live in a world seperate from the goocanvas

In other words, gtk.Widgets won't be rendered if you create images form our goocanvas and such

However, this is a cool way to add in situ editing to your goocanvas

We'll just be talking about normal items for the rest of this class though

general item capabilities

So what are some of the things that you do with an item? Well, you compose with it. So you scale it, move it, rotate it, change it's z-order and such

For a lot of things that you want to do with an item, you use set_property and get_property

For example, to set the a might make a Text item like this:

txt = goocanvas.Text(parent=self.root,text="some text", x=100, y=100, fill_color=self.ink_color)

then change the text in it like this:

txt.set_property("text","new text")

Let's look at colors for a moment. There are generally two color properties to work with, stork-color, and fill-color

If you've ever used a tool ink inkscape, this will make sense you to

for something like a rect, stroke-color is the outline of the rectangle, and fill-color is the inside of the rectangle

spacial transforms

You can move, rotate, resize, and skew items

The APIs for doing this are intuitive, imho

To grow something by 10%

item.scale(1.1,1.1)

And to shrink it a bit:

item.scale(.9,.9)

Note that the items always consider themeselves to be their original size and orientation, so doing this will cause an item to grow twice: item.scale(1.1,1.1) item.scale(1.1,1.1)

Now, when you start rotating and skewing items, some pretty confusing stuff can start happening

Essentially, an item tracks it's own coordinate system, and doesn't much care about the goocanvas's coordinate system

So if you rotate an item, for example, the coordinate systems are totally out of whack

So if you pass the x/ys to an item based on the canvas's coordinate system, it can get waaaay out of whack

Fortunately, goocanvas has some functions on it that just do these transforms for me

let's say I catch a mouse click event on an item

and I want to know where on the item the click happened

well, the click coordinate are reported in the goocanvas's coordinate system, so I need to do a quick calculation to determine where the click happened on the item:

e_x, e_y = self.goo_canvas.convert_to_item_space(self.selected_item,event.x,event.y)

Paths and clipping

Just a quick word on paths

A path is essentially a "squiggle"

It is defiened by a string that gets parsed into x,y coords, and then drawn with a bezier curve formula applied

This is a bit of math that I don't much understand

but just to get you started, here is a string that describes a scribble

line_data = "M 4.0 4.0C4.0 4.0 5.0 4.0 5.0 4.0 5.0 4.0 6.0 4.0 6.0 3.0 10.0 1.0 13.0 2.0 9.0 15.0 6.0 36.0 28.0 11.0 28.0 11.0 29.0 11.0 33.0 12.0 33.0 15.0 32.0 19.0 27.0 51.0 27.0 53.0 27.0 54.0 27.0 54.0 27.0 54.0 36.0 49.0 37.0 49.0"

then I can make a path out of this:

path = goocanvas.Path(data=line_data, parent=self.root, line_width=self.ink_width, stroke_color=self.ink_color)

so this will draw the path in the goocancas

Now, a path is also useful because you can use it to clip another object

You don't use a path object for this, just the string item.set_property("clip-path",line_data)

mousing

In terms of mousing, just fyi, a goocanvas has the normal gtk mouse tracking capabilities

to track mouse clicks, for example:

self.goo_canvas.connect("button_press_event",self.mouse_down)

Remember to convert the coordinates from these events to item coordinates if needed!

rendering

Finally, a goocanvas can use cairo surfaces to render off snapshots of itself

So if I want to make a png, I use an image surface

x, y, w, h = self.goo_canvas.get_bounds() surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(w), int(h)) context = cairo.Context(surface) context.rectangle(0, 0, 1000, 1000) context.set_source_rgb(1, 1, 1) context.fill() self.goo_canvas.render(context) surface.write_to_png(image_path)

There are other cairo surfaces as well, including a PDF surface

UbuntuOpportunisticDeveloperWeek/GooCanvas (last edited 2010-03-02 14:28:45 by 71)