/srv/irclogs.ubuntu.com/2010/03/02/#ubuntu-classroom.txt

=== nhandler_ is now known as nhandler
=== Jamie is now known as Guest17177
zubin71hi im writing some documentation. id like to explain what an upstream is, in a clear manner. could someone please help?06:51
persiazubin71: I can't find a nice definition from a quick wiki search.  Hunt up jcastro in #ubuntu-community-team :  he tends to have good answers to this class of question.06:59
zubin71persia, ok... thankx! :)07:00
=== tux_race1 is now known as tux_racer
black_aprenticelet's hack13:21
black_aprentice?13:21
persiablack_aprentice: You might be early for a class.  You're looking for the opportunistic developer stuff?13:25
black_aprenticeyup, but I already got the new that it starts at 1 pm in my time here13:26
persiablack_aprentice: You might want to hang out in #ubuntu-app-devel in the meantime.  That's the all-day-every-day place for discussions about creating apps.13:29
black_aprenticepersia: already there, but no activity at this time also13:29
persiaOh well :(13:30
=== mohi_away is now known as mohi1
Umpawas isn nu eigentlich?15:33
rickspencer3class time?16:00
rickspencer3time to talk about goocanvas?16:00
=== ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - http://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Event: Ubuntu Opportunistic Developer Week - Current Session: Gooey Graphics with GooCanvas - Rick Spencer - Instructor: rickspencer3 || Questions in #ubuntu-classroom-chat
rickspencer3okay, let us rock16:00
rickspencer3as usual I have put my class notes on the wiki:16:01
rickspencer3https://wiki.ubuntu.com/UbuntuOpportunisticDeveloperWeek/GooCanvas16:01
rickspencer3I'll be using python and pygoocanvas for code samples to talk about goocanvas today16:01
rickspencer3There is good reference documentaion for goocanvas:16:01
rickspencer3http://people.gnome.org/~gianmt/pygoocanvas/16:01
rickspencer3llive it, learn it, love it ;)16:01
rickspencer3Unfortunately, it can be a bit tough to find code samples and tutorials16:01
rickspencer3:(16:01
rickspencer3so, let's take a look16:02
rickspencer3So what is a goocanvas?16:02
rickspencer3A goocanvas is a 2d composing surface16:02
rickspencer3You can use it to make pretty much any kind of "image"16:02
rickspencer3It's kind of like an api around a drawing program16:02
rickspencer3So 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 UI16:02
rickspencer3goocanvas is cairo under the covers16:03
rickspencer3and is designed to easily integrate into your gtk app16:03
rickspencer3so, before we get into specifics16:03
rickspencer3questions?16:03
rickspencer3and note, I will be trying Classbot for the first time, so bear with me for a bit16:03
rickspencer3no questions?16:04
ClassBoteviltwin asked: is goo canvas considered the "approved" replacement to libgnomecanvas?16:04
rickspencer3I don't know16:04
rickspencer3I like goocanvas for it's capabilities16:04
ClassBotsetimike asked: Is goocanvas sort of like glade?16:06
rickspencer3not at all16:06
rickspencer3glade is a tool which creates XML files that describe gtk layouts16:07
rickspencer3goocanvas is a widget which you embed in your program and then use as a drawing surface16:07
rickspencer3let's take a look16:07
ClassBotnadako asked: is it some OO-wrapper for basic cairo drawing things?16:07
rickspencer3yes16:07
ClassBotluismmontielg asked: How do we get pygoocanvas?16:07
rickspencer3from the repos16:08
ClassBotdamo12 asked: Can glade add a widget area for pygoocanvas?16:08
rickspencer3I don't think there is a way to do that16:08
rickspencer3but I will show you how to add it with code16:08
rickspencer3So let's add a goocanvas to a pygtk app16:08
rickspencer3Add it just like a normal pygtk widget16:08
rickspencer3here's some code16:09
rickspencer3self.__goo_canvas = goocanvas.Canvas()16:09
rickspencer3self.__goo_canvas.set_size_request(640, 480)16:09
rickspencer3self.__goo_canvas.show()16:09
rickspencer3Be sure to set the size, otherwise it defaults to 1000,1000, it does not default to16:09
rickspencer3the size alloted to it in your window.16:09
rickspencer3this is different than normal gtk.Widgets where size is managed for you16:09
rickspencer3Handle window resizing to resize your goocanvas as well16:09
rickspencer3For example, if your goocanvas is in a VBox, you can do this:16:09
rickspencer3rect = self.builder.get_object("vbox2").get_allocation()16:10
rickspencer3self.__goo_canvas.set_bounds(0,0,rect.width,rect.height)16:10
rickspencer3store a reference to the root item for your goocanvas, you'll need it later often16:10
rickspencer3self.__root = self.__goo_canvas.get_root_item()16:10
rickspencer3The "root" is kinda like the root of an item tree in XML16:10
rickspencer3so if you do this, you'll have a goocanvas in your app, ready to rock16:11
rickspencer3questions?16:11
rickspencer3ok16:11
rickspencer3let's look at adding things to your goocanvas surface16:12
rickspencer3I got into goocanvas while working on photobomb16:12
rickspencer3so I have some code that I can use for examples16:12
rickspencer3so let's take the example of adding an image to your goocanvas16:12
rickspencer3assuming you want to modify the image and combine it with other drawing items16:13
rickspencer3Anything 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.16:13
rickspencer3plus each items has some extra capabilities16:13
rickspencer3so look at the reference for the specific item class, as well as Item and ItemSimple16:13
rickspencer3Let's add an item to the goocanvas to get a look at how it works in general.16:13
rickspencer3We'll start by adding an image.16:14
rickspencer3First, you need to get a gtk.pixbux for your image:16:14
rickspencer3pb = gtk.gdk.pixbuf_new_from_file(path)16:14
rickspencer3Then 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.16:14
rickspencer3For example, to center the image, I do this:16:14
rickspencer3cont_left, cont_top, cont_right, cont_bottom = self.__goo_canvas.get_bounds()16:14
rickspencer3img_w = pb.get_width()16:14
rickspencer3img_h = pb.get_height()16:14
rickspencer3img_left = (cont_right - img_w)/216:14
rickspencer3img_top = (cont_bottom - img_h)/216:14
rickspencer3I've calculated the top and left of the item16:15
rickspencer3now I am ready to create it16:15
rickspencer3Note that I create the Item, but there is nothing like goocanvas.add(item)16:15
rickspencer3rather, when you create the item, you set it's parent property.16:15
rickspencer3it's different than most container relationships, like in PyGtk16:15
rickspencer3The parent property is the root of the goocanvas16:15
rickspencer3This is why I remember the root16:15
rickspencer3because I will be using it whenever I create items for the goocanvas16:16
rickspencer3finally, here's the code to create the image on teh goocanvas:16:16
rickspencer3goocanvas.Image(pixbuf=pb,parent=self.__root, x=img_left,y=img_top)16:16
rickspencer3This basic pattern is how you add all other types of items.16:16
rickspencer3decide where to put the item, and set it's parent property to the root of the goocanvas.16:16
rickspencer3To remove the item from the goocanvas, you don't tell the goocanvas to remove it16:16
rickspencer3rather you tell the item to remove itself16:17
rickspencer3item.remove()16:17
rickspencer3questions so far?16:17
rickspencer3no questions?16:18
rickspencer3alright then16:18
rickspencer3let's talk a bit about different types of items that you can add16:18
rickspencer3In my mind, there are really 3 types of items16:18
ClassBotnadako asked: why not standard add/remove functions from parent obj?16:19
rickspencer3I have no idea16:19
rickspencer3this is just how the API is16:19
rickspencer3I presume it's because it is a wrapper around a c-api16:19
rickspencer3as I was saying,16:19
rickspencer3In my mind, there are really 3 types of items16:19
rickspencer3first is normal items that you add to draw the stuff you want16:19
rickspencer3this includes:16:20
rickspencer3Ellipse, Image, Path, Polyline, Rect, and Text16:20
rickspencer3then there are Layout and group items include:16:20
rickspencer3Group, Grid, and Table16:20
rickspencer3And then there is also Widget. Widget is pretty cool.16:20
rickspencer3You can add a gtk widget to your goocanvas, but note that it will live in a world seperate from the goocanvas16:20
rickspencer3gtk.Widgets won't be rendered if you create images form our goocanvas and such16:21
rickspencer3However, this is a cool way to add in situ editing to your goocanvas16:21
rickspencer3We'll just be talking about normal items for the rest of this class though16:21
rickspencer3I'll take some questions now, if you've got 'em16:21
ClassBotfagan asked: What formats does GooCanvas support? (png,jpeg,svg..etc)16:21
rickspencer3ya know, I never really investigated16:22
rickspencer3I would presume the first two16:22
rickspencer3in terms of svg, perhaps that would be supported a bit differently, as many of the items are described with svg language16:22
rickspencer3or can be16:22
rickspencer3like an elipse in an svg would be turned into an elipse item16:23
rickspencer3rather than rendered as an image16:23
rickspencer3I would be interested to see what different things people try and how it works for them16:23
ClassBottm_lv asked: why goocanvas?16:23
rickspencer3this is a good question16:23
rickspencer3I used goocanvas for photobomb because:16:23
rickspencer31. it was an easy API16:24
rickspencer32. it integrate well with pygtk16:24
rickspencer33. it had good reference docs16:24
rickspencer34. it had the functionality to do the things I wanted16:24
rickspencer3for #4 that was stuff like adding text, paths, etc...16:24
rickspencer3and also the ability to render off images easily16:24
ClassBotnadako asked: is there any ready-to-use things like connector lines that connects two elements and follow their positions16:25
rickspencer3not that I know of16:25
ClassBotnadako asked: also, what about interactivity? drag/drop, mouse clicks, etc16:25
rickspencer3you handle that manually16:25
rickspencer3I'll mention mouse handling briefly16:25
rickspencer3late16:25
rickspencer3r16:25
rickspencer3ok, so moving on16:26
rickspencer3So what are some of the things that you do with an item?16:26
rickspencer3Well, you compose with it. So you scale it, move it, rotate it, change it's z-order and such16:26
rickspencer3For a lot of things that you want to do with an item, you use set_property and get_property16:26
rickspencer3this is the same way of interacting with properties in pygtk, but you do lots more of it in goocanvas16:26
rickspencer3For example, to set the a might make a Text item like this:16:27
rickspencer3txt = goocanvas.Text(parent=self.__root,text="some text", x=100, y=100, fill_color=self.__ink_color)16:27
rickspencer3so here I am adding some text and it will say "some text"16:27
rickspencer3it will be at x,y 100,10016:27
rickspencer3and will use the stored ink color16:27
rickspencer3then change the text in it like this:16:27
rickspencer3txt.set_property("text","new text")16:27
rickspencer3if I want to know what the text is, I can say:16:28
rickspencer3my_string = txt.get_property("text")16:28
rickspencer3some capabilities are accessed directly16:28
rickspencer3such as bounds16:28
rickspencer3but most are accessed like this16:29
rickspencer3Let's look at colors for a moment.16:29
rickspencer3There are generally two color properties to work with, stork-color, and fill-color16:29
rickspencer3stork-color?16:29
rickspencer3let's call that stoke-color16:29
rickspencer3:)16:29
rickspencer3If you've ever used a tool ink inkscape, this will make sense you to16:29
rickspencer3for something like a rect, stroke-color is the outline of the rectangle, and fill-color is the inside of the rectangle16:29
rickspencer3before I discuss a bit more about transforming items ...16:30
rickspencer3any questions?16:30
ClassBotnadako asked: so it's only about drawing things?16:30
rickspencer3sort of16:30
rickspencer3I was able to use goocanvas to make a pretty functional editing surface in photobomb16:30
rickspencer3it included dragging, drawing lines with a pen too, and saving pngs16:31
rickspencer3but essentially, yes, it's a drawing surface16:31
ClassBottm_lv asked: getters and setters seem to be result of crappy bindings. do you know if there is any work done to make them more pythonic?16:31
rickspencer3I agree16:31
rickspencer3but I don't know of any more work to make it more pythonic16:31
rickspencer3while the API is not perfect, it is functional and robust16:31
rickspencer3ok16:32
rickspencer3moving on a bit16:32
rickspencer3You can move, rotate, resize, and skew items16:32
rickspencer3The APIs for doing this are intuitive, imho16:32
rickspencer3To grow something by 10%16:32
rickspencer3:16:32
rickspencer3item.scale(1.1,1.1)16:32
rickspencer3that says make it 1.1 times as tall and wide as it is now16:33
rickspencer3And to shrink it a bit:16:33
rickspencer3item.scale(.9,.9)16:33
rickspencer3Note that the items always consider themeselves to be their original size and orientation, so doing this will cause an item to grow twice:16:33
rickspencer3item.scale(1.1,1.1)16:33
rickspencer3item.scale(1.1,1.1)16:33
rickspencer3Now, when you start rotating and skewing items, some pretty confusing stuff can start happening16:34
rickspencer3Essentially, an item tracks it's own coordinate system, and doesn't much care about the goocanvas's coordinate system16:34
rickspencer3So if you rotate an item, for example, the coordinate systems are totally out of whack16:34
rickspencer3So if you pass the x/ys to an item based on the canvas's coordinate system, it can get waaaay weird16:34
rickspencer3like if you have a an item rotated 180 degrees16:34
rickspencer3and you tell it to add 100 to it's x16:35
rickspencer3it will move left!16:35
rickspencer3because the items doesn't know or care that it is rotated16:35
rickspencer3Fortunately, goocanvas has some functions on it that just do these transforms for you16:35
rickspencer3let's say I catch a mouse click event on an item16:35
rickspencer3and I want to know where on the item the click happened16:35
rickspencer3well, 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:16:35
rickspencer3e_x, e_y = self.__goo_canvas.convert_to_item_space(self.selected_item,event.x,event.y)16:36
rickspencer3here self.selected_item is the item who's coordinate system I want to convert to16:36
rickspencer3so now e_x is x from the item16:36
rickspencer3's point of view, not the goocanvas point of view16:37
rickspencer3phew16:37
rickspencer3this is weird, but you get used to it16:37
rickspencer3so, good time for some questions if there are any16:37
ClassBottm_lv asked: can you nest items in goocanvas?16:37
rickspencer3yes, but I haven't done that16:37
ClassBotnadako asked: can you give a link to that photobomb app?16:38
rickspencer3sure, it's in my junk folder16:38
rickspencer3https://code.edge.launchpad.net/~rick-rickspencer3/+junk/photobomb16:39
ClassBotluismmontielg asked: What could be easier to implement, for a small pygtk app, using pyclutter or pygoocanvas? or whats the difference between those 216:39
rickspencer3clutter is also very cool16:39
rickspencer3but it's a wrapper around OpenGL16:40
rickspencer3I suspect that for a small 2d surface, goocanvas will be much better16:40
rickspencer3however, for rendering animations, clutter will be much better16:40
rickspencer3you can do drag and drop and such on goocanvas with no problem16:40
rickspencer3and it has some animation support16:40
rickspencer3by clutter is all about animations, especially in a cool 3d way16:41
ClassBottm_lv asked: following on the previous one, i'd like to insert a shameless plug - contender #3 the hamster graphics library, sprite-based and python pure: http://wiki.github.com/tbaugis/hamster_experiments/16:41
rickspencer3shameless plug accepted ;)16:41
rickspencer3ok16:41
rickspencer3moving on16:41
rickspencer3Just a quick word on paths16:41
rickspencer3A path is essentially a "squiggle"16:41
rickspencer3It is defined by a string that gets parsed into x,y coords, and then drawn with a bezier curve formula applied16:42
rickspencer3let's take a look16:42
rickspencer3the curve stuff is a bit of math that I don't much understand16:42
rickspencer3but just to get you started, here is a string that describes a scribble16:42
rickspencer3line_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"16:42
rickspencer3it just kind of traces some x/y coords16:42
rickspencer3then I can make a path out of this:16:43
rickspencer3path = goocanvas.Path(data=line_data, parent=self.__root, line_width=self.__ink_width, stroke_color=self.__ink_color)16:43
rickspencer3so this will draw the squiggle in the goocanvas16:43
rickspencer3Now, a path is also useful because you can use it to clip another object16:43
rickspencer3like you can draw an arbitrary path around an item and use it kind of like a pair of scissorrs16:44
rickspencer3You don't use a path object for this, just the string16:44
rickspencer3item.set_property("clip-path",line_data)16:44
rickspencer3pretty cool!16:44
rickspencer3any questions about paths and such?16:45
rickspencer3ok16:45
rickspencer3let's go on then16:45
rickspencer3somebody asked about managing mouse movements and such16:45
rickspencer3In terms of mousing a goocanvas has the normal gtk mouse tracking capabilities16:46
rickspencer3to track mouse clicks, for example:16:46
rickspencer3self.__goo_canvas.connect("button_press_event",self.mouse_down)16:46
rickspencer3so you can also connect to mousedown, mousemove, mouseup, etc... to handle dragging items around16:46
rickspencer3self.__motion_handler = self.__goo_canvas.connect("motion_notify_event",self.item_moved)16:47
rickspencer3                self.__mouse_up_handler = self.__goo_canvas.connect("button_release_event",self.drag_stop)16:47
rickspencer3so that's how I handle dragging items in photobomb16:47
rickspencer3it's a manual process16:47
rickspencer3questions?16:47
rickspencer3ok16:48
rickspencer3let's go on to the last section16:48
rickspencer3rendering16:48
rickspencer3a goocanvas can use cairo surfaces to render off snapshots of itself16:48
rickspencer3So if I want to make a png, I use an image surface16:48
rickspencer3x, y, w, h = self.__goo_canvas.get_bounds()16:48
rickspencer3surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(w), int(h))16:48
rickspencer3context = cairo.Context(surface)16:48
rickspencer3context.rectangle(0, 0, 1000, 1000)16:48
rickspencer3context.set_source_rgb(1, 1, 1)16:48
rickspencer3context.fill()16:48
rickspencer3self.__goo_canvas.render(context)16:48
rickspencer3surface.write_to_png(image_path)16:48
rickspencer3(I sniped this originally from segphaults grabbersnap code)16:48
rickspencer3There are other cairo surfaces as well, including a PDF surface16:49
rickspencer3so you can render to a pdf, for example16:49
rickspencer3okay, that's the last bit of material I prepared16:49
rickspencer3any questions about anything?16:49
ClassBotquappa1 asked: Can I try photobomb on Karmic? Seems that in your PPA it's only for Lucid.16:50
rickspencer3I suppose16:50
rickspencer3I think it should work16:50
rickspencer3just pull from trunk and give it a try16:51
ClassBotems asked: any chance you could add some goocanvas snippets to acire?16:51
rickspencer3I suppose16:51
rickspencer3I don't know when I would get to it16:51
rickspencer3I would be glad to help someone else who wants to give it a try16:51
ClassBotluismmontielg asked: is there any code examples for this? simple working examples?16:51
rickspencer3not too many16:51
rickspencer3that's why I wanted to do the class16:52
rickspencer3photobomb and grabber snap both use it a bit16:52
rickspencer3so lots of samples there, but no snippets yet that I know of16:52
ClassBotfagan asked: will photobomb get into the repo any time soon? :)16:52
rickspencer3uh16:52
rickspencer3well16:52
rickspencer3look at the code and let me know what you think16:52
rickspencer3there are lots of ideas cram in there, and some pretty bad bugs16:52
rickspencer3:)16:52
rickspencer3maybe sometime though16:53
rickspencer3unless someone wants to take over and finish it off16:53
rickspencer3no more questions and 7 minutes early!16:53
rickspencer3you folks were a great class, thanks for coming16:53
ClassBotquappa1 asked: photobomb requires something called quickly.prompts. so probably no luck on Karmic? :)16:54
rickspencer3oh goodness16:54
rickspencer3yes, that is not in karmic16:54
rickspencer3but if you are dedicated, you could hack it together16:54
rickspencer3like pull the quidgets project and stick the code in the photobomb library16:54
=== mohi_away is now known as mohi1
=== ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - http://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Current Session: Writing a Rhythmbox plug-in - Stuart Langridge - Instructor: aquarius || Questions in #ubuntu-classroom-chat
aquariusHi, everyone, I'm Stuart Langridge17:00
aquariusToday I'm going to talk about making a Rhythmbox plugin.17:01
aquariusbut first I should say: cool talk about goocanvas, rickspencer3 :)17:01
aquariusIf you're using Lernid, you'll see slides with code examples. If you don't have Lernid, don't worry; all the code for this plugin is available, and you can look through it to see what I'm talking about, or you can load the slide deck yourself from http://www.kryogenix.org/code/RBMicroBlog17:01
aquariusSpecifically, I'm going to talk about making a Python plugin. If you're hoping for in-depth C knowledge, you're talking to the wrong dude.17:01
aquariusFeel free to ask questions in #ubuntu-classroom-chat, and I'll answer them at the end; if someone could collect them, that'd be great.17:01
aquariusor, actually, I'll use ClassBot :)17:01
aquariusWhat with this being Opportunistic Developer Week and all, I'm aiming for something which scratches a little itch.17:02
aquariusThe itch I want to scratch is explained by a tweet I saw from @mandel_macaque, which said17:02
aquarius[SLIDE 2]17:02
aquarius♥ Space Oddity by David Bowie #lastfm: http://bit.ly/15zv417:02
aquariusand I thought: hey, that's quite cool, he's tweeting songs that he's playing that he likes.17:02
aquariusI'd like to do that. But I don't use last.fm, I use Rhythmbox to play my music.17:02
aquariusSo why not have a "tweet this song" button, so I can hit it when a song that I like comes along?17:02
aquariusA Rhythmbox Python plugin comes in two parts: an .rb-plugin file, which describes your plugin, and a Python module.17:02
aquarius[SLIDE 3]17:03
aquariusOur first stage, then, is to set up the plugin itself.17:03
aquariusCreate the Rhythmbox plugins folder, which you might already have:17:03
aquariusmkdir -p ~/.gnome2/rhythmbox/plugins17:03
aquariusNow, create a folder for our plugin, which we'll call RBMicroBlog17:03
aquariusmkdir -p ~/.gnome2/rhythmbox/plugins/RBMicroBlog17:03
aquariusIn there create a __init__.py file:17:04
aquariustouch ~/.gnome2/rhythmbox/plugins/RBMicroBlog/__init__.py17:04
aquariusand an .rb-plugin file:17:04
aquariusgedit ~/.gnome2/rhythmbox/plugins/RBMicroBlog/RBMicroBlog.rb-plugin17:04
aquariusYour rb-plugin file has a specific format:17:04
aquarius[SLIDE 4]17:04
=== edouard is now known as edouardp
aquarius[RB Plugin]17:04
aquariusLoader=python17:04
aquariusModule=RBMicroBlog17:04
aquariusIAge=117:04
aquariusName=Microblogging17:04
aquariusDescription=Microblog what you're listening to17:04
aquariusAuthors=Stuart Langridge <sil@kryogenix.org>17:04
aquariusCopyright=Copyright © 2010 Stuart Langridge17:04
aquariusWebsite=http://www.kryogenix.org/code/rhythmbox-microblog17:05
aquariusThe important lines in that are the two that tell Rhythmbox "this is a Python plugin", and "the module name for this plugin is RBMicroBlog".17:05
aquarius[SLIDE 5]17:05
aquariusPython, as you will (hopefully) know, treats a folder called X with an __init__.py file inside as a module named X.17:06
aquariusSo, we have our RBMicroBlog folder, with __init__.py inside, which is therefore a Python module17:06
aquariusand also in that folder a .rb-plugin file which says "this plugin is provided by the RBMicroBlog module".17:06
aquariusRight now, then, you have a plugin which doesn't do anything. Which means it has no bugs, true, but isn't that useful.17:06
aquariusOn to editing the Python. Opportunistic development is fun!17:06
aquariusBasically, we want our plugin to do this:17:07
aquarius1. be a plugin that can be enabled and disabled17:07
aquarius2. put a "tweet this" button on the toolbar, with the Gwibber icon in it, and a tooltip of "microblog this song"17:07
aquarius3. when the button is pressed, work out which song is currently playing...17:07
aquarius4. and use the Gwibber API to microblog a message saying "Listening to: One Vision by Queen"17:07
aquariusSo, start with the basics.17:07
aquarius[SLIDE 6]17:07
aquariusA Rhythmbox plugin is built as a subclass of rb.Plugin, named the same as the plugin module.17:08
aquariusSo, a basic plugin would look like:17:08
aquariusimport rb17:08
aquariusclass RBMicroBlog(rb.Plugin):17:08
aquarius    def activate(self, shell):17:08
aquarius        print "Activate!"17:08
aquarius    def deactivate(self, shell):17:08
aquarius        print "Deactivate!"17:08
aquariusYour plugin's "activate" function is called when the plugin is loaded, so it can do setup, and "deactivate" is called when the plugin is unloaded.17:08
aquariusSo, start Rhythmbox -- your plugin hasn't been enabled yet, so in Edit > Plugins, find "Microblogging", and tick it to turn it on.17:08
aquarius[SLIDE 7]17:09
aquariusYou might be thinking: where does the output from my print statement go?17:09
aquariusRhythmbox hides output from plugins unless you want to see it. So, quit Rhythmbox, and restart it as "rhythmbox -D RBMicroBlog"17:09
aquariusNow, when you tick your Microblogging plugin on and off in Edit > Plugins, you should see output in the terminal:17:10
aquarius(15:34:36) [0x8756028] [RBMicroBlog.activate] RBMicroBlog/__init__.py:9: Activate!17:10
aquarius(15:34:38) [0x8756028] [RBMicroBlog.deactivate] RBMicroBlog/__init__.py:13: Deactivate!17:10
aquariusNow, you've got an .rb-plugin file and a Python module, and it's loaded. That's the basics of every Python plugin. The only hard remaining bit is to, y'know, actually write the code that does what you want.17:10
aquariusOn, then, to stage 2: put a "tweet this" button on the toolbar.17:11
aquariusThis is about adding some UI, and is documented at http://live.gnome.org/RhythmboxPlugins/WritingGuide#Adding_UI17:11
aquariusThere are also more examples at http://live.gnome.org/Rhythmbox%20Plugins/Python%20Plugin%20Examples which is a very useful page.17:11
aquariusIf you come across more things that you want to do in Rhythmbox, please add them to that page!17:12
aquarius(Also, adding those Rhythmbox plugin "snippets" to the python-snippets project in Launchpad would be pretty cool.)17:12
aquariusAdding UI requires two stages: first, you define where you want to add the UI with some XML17:12
aquarius[SLIDE 8]17:12
aquariusand then you actually hook up a gtk.ActionGroup to the UI, as defined by the XML17:12
aquarius[SLIDE 9]17:12
aquariusTo be honest, you can just borrow that bit from another plugin. Understanding gtk.Actions and gtk.ActionGroups is fine for those that want to, but if you just want to add a toolbar button and a menu item, steal the code from somewhere else (like this plugin).17:13
aquariusNow, you'll have a button on the toolbar, and a menu item in the Tools menu. The button has no icon, though.17:13
aquariusA bit more cookbook programming, now: how to load an icon and add it. The icon we want is /usr/share/pixmaps/gwibber.svg17:14
aquariusSo, some code to load that icon and make it available for your button17:14
aquarius[SLIDE 10]17:14
aquariusAgain, don't worry too much about understanding this: the way Gtk deals with icons is pretty complicated. Borrow it for your own projects.17:15
aquariusNow, you have a button on the toolbar and a menu item, and we've connected them up to call a function self.microblog.17:15
aquariusStage 3 is to implement that.17:15
aquariusHandler functions, like this one, will be called with two parameters, "event" and "shell".17:15
aquariusWe want our microblog plugin to find out what's currently playing.17:16
aquariusThere are three ways you might approach the problem of "what do I do to find out what's playing?"17:16
aquariusYou could know already (if you're Jonoathan Matthew, the genius Rhythmbox maintainer)17:17
aquarius(er, Jonathan)17:17
aquarius(er, Jonathan)17:17
aquariusYou could look it up in the documentation (there isn't all that much; the pages linked above cover some things, but not lots)17:17
aquariusOr you can do what I do, which is poke around in the Python objects.17:17
aquariusFor this, enable the Python Console plugin in Rhythmbox, and then run it from the Tools menu.17:17
aquariusNow you have a Python console which is hooked up to Rhythmbox. You get a variable called "shell" for free, which is the Rhythmbox shell (and you'll notice that this shell is also being passed to your handler function).17:18
aquariusIn Python, to list all the properties of an object, use dir(). So, in the Python console, say: dir(shell)17:18
aquarius[SLIDE 11]17:18
aquariusHm, that "get_player" looks useful.17:18
aquariusSo, say: shell.get_player()17:18
aquarius<rb.ShellPlayer object at 0xa3ff9dc (RBShellPlayer at 0x9b68800)>17:18
aquariusOK, and what's a "ShellPlayer"?17:19
aquarius>>> player = shell.get_player()17:19
aquarius>>> dir(player)17:19
aquariusand "get_playing_entry" again looks useful17:19
aquarius>>> player.get_playing_entry()17:19
aquarius<RhythmDBEntry at 0xb3543150>17:19
aquariusTHe way Rhythmbox is set up is that there's a database, the RhythmDB, and each song in that database is defined by an "entry", of type RhythmDBEntry.17:20
aquariusSo, we've got the "entry" for the currently playing song. But an entry doesn't help much; we want the artist and the title.17:20
aquariusLooking at the properties of the entry doesn't help much here, either.17:20
aquariusFortunately, the documentation comes to the rescue: http://live.gnome.org/Rhythmbox%20Plugins/Python%20Plugin%20Examples#How_do_I_get_the_metadata_details_of_a_song.3F17:20
aquariusSo, given an entry, we can get its title with shell.props.db.entry_get(entry, rhythmdb.PROP_TITLE)17:21
aquariusThe first attempt at our microblog handler function, then, could look like this:17:21
aquarius    def microblog(self, event, shell):17:21
aquarius        entry = shell.get_player().get_playing_entry()17:21
aquarius        title = shell.props.db.entry_get(entry, rhythmdb.PROP_TITLE)17:21
aquarius        artist = shell.props.db.entry_get(entry, rhythmdb.PROP_ARTIST)17:21
aquarius        print "Listening to: %s by %s" % (title, artist)17:22
aquariusand that should, when we hit the button, print out the message to the terminal.17:22
aquariusAnd indeed it does (remember, rhythmbox -D RBMicroBlog !)17:22
aquarius(16:38:44) [0x907a028] [RBMicroBlog.microblog] RBMicroBlog/__init__.py:112: Listening to: You Shook Me All Night Long by AC/DC17:22
aquariusOne more thing, though: what if there's nothing playing?17:23
aquariusWell, poking around in dir(shell.get_player()) a bit more in the Python console reveals "get_playing".17:23
aquariusSo let's make the button not do anything if we're not playing.17:23
aquariusJust add one new line to the top of the microblog function:17:23
aquariusif not shell.get_player().get_playing(): return17:23
aquariusOK, so now we have a button which prints out our microblog message. The final stage is to use Gwibber to actually post it for us.17:24
aquariusFortunately, someone's already done the work for us here. There's a snippet in the acire program which shows how to post a message via gwibber.17:24
aquarius(If you don't know about acire, there's a session about it on Thursday; it's a library of useful Python snippets, and it's great.)17:24
aquariusYou can see the snippet itself, if you don't use acire, in Launchpad at http://bazaar.launchpad.net/~jonobacon/python-snippets/trunk/annotate/head:/gwibber/sendmessage.py17:25
aquariusSo add a couple more lines to our microblog function:17:25
aquarius        gw = gwibber.lib.GwibberPublic()17:25
aquarius        gw.SendMessage("Listening to: %s by %s" % (title, artist))17:25
aquarius[SLIDE 12]17:25
aquariusand we're all done!17:25
aquariusThis has been an incredibly brief tour of creating a Rhythmbox plugin, which ties into other cool bits of the Ubuntu platform.17:25
aquariusIf this has interested you, a few things you might want to try are:17:26
aquariusMake the plugin tell you when it's successfully posted!17:26
aquariusChange the plugin to disable the button and menu item when nothing is playing (you'll want to connect to Rhythmbox's start playing and stop playing signals)17:26
aquariusMake "quickly create rhythmbox-plugin" work so no-one has to remember all the boilerplate17:26
aquariusOK, that's it for the talk. I'll take some questions now.17:26
aquariusstrycore_lernid> IconSource, IconSet, IconFactory ... seems a bit confusing , at least to me17:26
aquariusit is. This is why I suggest just copying the code from an existing plugin, like RBMicroBlog :)17:27
aquariusyou don't really have to worry about why it works, just that it does17:27
aquariusenli> what PROP stands for?17:27
aquarius"property", as ems confirmed on #ubuntu-classroom-chat17:27
aquariusso PROP_ARTIST is the "artist" property of a RhythmDBEntry (song)17:27
aquariuseviltwin> QUESTION: Surely we should disable the button when playback stops and reenable it when playback starts rather than a dirty, dirty if statement?17:29
aquariushence the "extra credit" part at the end of the talk ;)17:29
aquariusdefinitely that would be the way to do it, yes17:29
aquariusbut I've tried to keep RBMicroBlog tiny so that it's a simple thing that can be learned from17:29
aquariusif someone wants to trick it up into a proper application, that's a great idea :)17:30
ClassBotdanyR asked: No way to get the plugin available to the masses? or in vanilla-lucid? just what i was looking for!17:30
aquariuswell...17:30
aquariusto do that, you'd need to package it17:30
aquariusyou can do that, certainly17:30
aquariusbut I'm not the person you want to talk to about that17:31
aquariusone of the reasons that I'd really like to see "quickly create rhythmbox-plugin" is that it could set up the packaging stuff for you as well17:31
ClassBotstevec49 asked: For those looking to add plugin support to their own applications, do you think RhythmBox is a good model to follow?17:31
aquariusbroadly, yes17:32
aquariusRhythmbox plugins have a number of special methods (like activate and deactivate, but there are lots more) because there are lots of places in the Rhythmbox startup and usage procedures that plugins might want to plug into.17:32
aquariusI, personally, like plugins to be one file rather than two, so when I implement Python plugin loading for an app, I put the stuff that's in .rb-plugin into the Python plugin itself, as variables.17:33
aquariusbecause I like single-file deployment of plugins -- it's easier to install a plugin if it's one file that you drop in a folder.17:33
aquariusbut that's flat out personal perference on my part :)17:33
ClassBoteviltwin asked: is this a plugin that you've written and made available somewhere? if so, where's the project homepage/code repo?17:34
aquariushttp://www.kryogenix.org/code/RBMicroBlog17:34
aquariusthe plugin code is downloadable from there17:34
ClassBotdanyR asked: Shouldn't this be available in out-of-the-box Lucid? "What i'm listening to" sharing?17:35
aquariusif someone makes RBMicroBlog better, so that it's worthy of inclusion, sure. You might have missed out on getting it into Rhythmbox in lucid by default, but it would make it into universe, I'm sure, and could them be installed17:36
ClassBotoskude asked: does a rhythmbox plugin have access to the audio stream/data ?17:36
aquariusit does, yes; you can work with the underlying gstreamer code. http://live.gnome.org/RhythmboxPlugins/WritingGuide#Adding_elements_to_the_GStreamer_playback_pipeline has details about that.17:37
ClassBotstevec49 asked: Is the RhythmBox "shell" itself a python module?17:37
aquariussort of.17:37
aquariusit's passed to your handler functions, and to your plugin17:38
aquariusand it's available in the Python console17:38
aquariusbut it only exists if you're a Rhythmbox plugin17:38
aquariusyou can't do "import rb.shell" from a random Python program and get access to control Rhythmbox17:38
aquariusif you want to control Rhythmbox from an outside application, use D-Bus.17:38
ClassBotquigs asked: is the UI stuff not included in the package you provided?17:39
aquariusdoes it not work, then?17:39
aquariusha. quigs points out there is a bug in the deactivate handler in the plugin :)17:40
aquariusmy fault.17:40
aquariusI'll take a look at that later17:40
aquariusAny other questions?17:41
aquariusThere is a rhythmbox-plugins package in lucid, which you may already have installed, which contains lots of plugins17:41
aquariusand there are others at http://live.gnome.org/RhythmboxPlugins/ThirdParty17:42
aquariusand http://live.gnome.org/RhythmboxPlugins/WishList lsts some plugins that people want but don't have, if you want to work on something but aren't sure what17:42
aquariuswhat with Rhythmbox being the default media player, more plugins will make it do more stuff and generally be cooler, which I'm all in favour of :017:43
ClassBotduanedesign_ asked: Where does the XML file go?17:43
aquariusI normally just put the XML in a string inside my plugin17:44
aquariussince it only defines where a button or menu go, it's normally pretty short17:44
aquariusif you want to put it in a separate file, then put the file in the folder with the plugin, and use shell.find_file("myxmlfile") to get its pathname, that way it'll work no matter where your plugin is installed17:45
aquariusOK, cool, no more questions?17:46
ClassBotstrycore_lernid asked: It's a bit off topic, but in the third party plugins there are some that say "It is not an Internet radio server", GloveSoap and IceCast can do the job, but is there a way to send the audio stream to a server without opeing ports on a firewall ?17:47
aquariuserm17:47
aquariusthere might be. I don't know the answer there, I'm afraid.17:47
aquariusthe people in #gstreamer may be able to give you some pointers on whether there are gstreamer elements that can do that, which you could add to the pipeline17:48
aquariusso, we get to finish a little bit early and everyone can go get a cup of tea before kenvandine explains how to *really* use the Gwibber API, beyond the two lines I've just used!17:48
aquariusthanks, all17:49
=== william is now known as Guest94411
=== kenvandine is now known as kenvandine[busy]
=== ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - http://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Event: Ubuntu Opportunistic Developer Week - Current Session: Microblog from your app with the Gwibber API - Ken VanDine - Instructor: kenvandine || Questions in #ubuntu-classroom-chat
=== kenvandine[busy] is now known as kenvandine
kenvandineYay!18:15
jcastro\o/18:16
kenvandinejcastro, as usual you are my hero18:16
kenvandineok... only 16 minutes late18:16
kenvandineok, we are here to talk about the Gwibber API18:16
kenvandineA quick intro to Gwibber18:17
kenvandineGwibber is a social networking client as well as a desktop service18:17
kenvandinethat can be used to post messages to a variety of services18:17
kenvandinefacebook, twitter, identi.ca18:17
kenvandineetc18:17
kenvandineit can also pull feeds from them, images, etc18:17
kenvandinethe client is just a frontend to the desktop service that does the real heavy lifting18:18
kenvandinethe desktop service also provides both dbus and python APIs18:18
kenvandineas an application developer, you can embed some social networking features into your application pretty easily18:18
kenvandinei have some examples prepared and we can browse through them18:19
kenvandinefirst there are some python docs at http://people.canonical.com/~kenvandine/gwibber/docs/html/18:19
kenvandineright now that mostly documents the python api18:19
kenvandinewe want to generate docs for the DBus API as well18:19
kenvandineso if anyone knows how to do that in epydoc, please let me know after the session... :)18:20
kenvandineall of the examples are available in bzr at lp:~ken-vandine/+junk/gwibber-api-examples18:20
kenvandinebzr branch lp:~ken-vandine/+junk/gwibber-api-examples18:20
kenvandineor18:20
kenvandineyou can get them in the browser as well, i'll provide links as we discuss them18:20
kenvandinemost common operation would be posting messages of course18:21
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/send_message.py.txt18:21
kenvandineas you can see the api is quite simple18:21
kenvandineimport gwibber.lib18:21
kenvandinegw = gwibber.lib.GwibberPublic()18:21
kenvandineGwibberPublic is the class we provide with all the public python methods18:22
kenvandineso most of the examples will use this18:22
kenvandinegw.SendMessage("This is a message")18:22
kenvandineSendMessage takes a single argument as a string18:22
kenvandinewhich is the content of the message to post18:22
kenvandinealso note, the method names match the DBus method names18:23
kenvandineso it should be pretty easy to discover how to do the same thing from a C, mono, vala, etc app18:23
kenvandinevia dbus18:23
kenvandinei should mention gwibber is broken down into two parts, gwibber and gwibber-service18:24
kenvandinethe service runs independently of any UI18:24
kenvandineand is dbus activated, so if it isn't already running it will start up automatically18:24
kenvandineso that is sending a message, pretty easy18:24
kenvandinethere are some other operations you can do as well18:25
kenvandineif you want to tell the service to refresh it's feeds18:25
kenvandineyou can call Refresh18:25
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/refresh.py.txt18:25
kenvandinealso very simple API18:25
kenvandineno arguments, it just tells the service to go refresh itself18:25
kenvandineyou might want to do something a little more complex18:26
kenvandinelike perhaps query accounts that are configured and find specific accounts or services18:26
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/get_accounts.py.txt18:27
kenvandinethe GetAccounts method returns json formated string of accounts18:27
kenvandineyou can then iterate over the accounts and find specific values, one of interest might be "send_enabled"18:28
kenvandineor "receive_enabled"18:28
kenvandinethose tell gwibber which accounts to post to or pull data from18:28
kenvandineso back in the SendMessage example, gwibber would post to all accounts that have send_enabled = True18:28
kenvandineyou may want to find all the accounts that are enabled and display that in your application18:29
kenvandinejust as an example18:29
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/get_services.py.txt18:29
kenvandineis very similar18:29
kenvandineit queries all services known to gwibber18:29
kenvandinesome of which you might not have accounts setup for18:30
kenvandineand if you want to use gwibber to shorten a url, there is a method for that as well18:30
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/shorten.py.txt18:30
kenvandineShorten takes an argument of a string and returns a shortened string using the user's configured preferred shortening service18:31
kenvandineis.gd, tinyurl.com, etc18:31
kenvandinethese are the basic method provided in GwibberPublic18:32
kenvandineany questions so far?18:32
kenvandinethat was the goal :)18:32
kenvandineok18:32
kenvandinemoving on18:32
kenvandinethere is also a gtk widget available that you can embed into your application18:33
kenvandineand we hope people will create a variety of useful gtk widgets and contribute those back to gwibber18:33
kenvandinemaking it even easier to make your application social :)18:34
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/gwibber-widget.py.txt18:34
kenvandinei am not going to talk about the part of this that creates the gtk window, etc18:34
kenvandinethat is just a place holder so you can run the example if you like18:34
kenvandinethere are just a couple of key lines you need18:34
kenvandinefrom gwibber.lib.gtk import widgets18:34
kenvandinethat gives you the widgets18:35
kenvandineposter = widgets.GwibberPosterVBox()18:35
kenvandineGwibberPosterVBox is a VBox that contains a text input, text overlay for character count, send button and toggles for each service18:35
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/poster.png18:36
kenvandinethat is what the widget looks like18:37
kenvandineso you could embed that into your application18:37
kenvandineso18:37
kenvandineposter = widgets.GwibberPosterVBox()18:37
kenvandinewindow.add(poster)18:37
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/gwibber-widget.py.txt18:37
kenvandineis really all you need18:38
kenvandinelet me put up the screenshot again18:38
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/poster.png18:38
kenvandinethe toggle buttons on the botton that have the icons for the services18:38
kenvandinethose are all live and tied into the gwibber service18:38
kenvandineso toggling those enables and disables sending to those services desktop wide18:39
kenvandineif you are running Lucid, you can see this in action by running gwibber-poster18:39
kenvandineanother variation there18:39
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/gwibber-widget-with-contents.py.txt18:39
kenvandinethis shows adding some default content18:40
kenvandineso if your application has some context about what you are going to post about, like perhaps in the rhythmbox example18:40
kenvandineyou just marked a song you liked, rated it or whatever18:40
kenvandineyou could have that pre-populate the text input with information about the song you just rated18:41
kenvandineand let the user edit the text if they like and hit Send18:41
kenvandineto post it18:41
kenvandineposter.input.set_text(contents)18:41
kenvandinewhere contents is a string that gets added18:41
kenvandinenote: if that text STARTS with a url18:42
kenvandineit will get shortened automatically18:42
kenvandinei don't think that works if the url isn't at the beginning, but i could be wrong18:42
kenvandineour hope here is to get lots of application developers to embed this functionality18:43
kenvandinereally utilize gwibber as a desktop service18:43
kenvandinenot just as a gui for viewing/posting tweets :)18:43
kenvandinei have just one more area i want to touch on, then we can open up some discussion18:44
kenvandinegwibber stores all of it's data in desktopcouch18:44
kenvandinea very nice document oriented database18:44
kenvandinewhich also has replication features, so your settings can replicate to other computers either directly through desktopcouch or through ubuntu one18:45
kenvandinesome features of gwibber aren't exposed (at least yet) through the python or dbus APIs18:45
kenvandinethings like retrieving message contents18:45
kenvandinebut it is easy to do with desktopcouch directly18:45
kenvandinehere is an example18:45
kenvandinehttp://people.canonical.com/~kenvandine/gwibber/api-examples/messages_from_couch.py.txt18:46
kenvandinethis uses the desktopcouch records API to connect to the database and get records18:46
kenvandinethat match the record_type for gwibber_messages18:46
kenvandineresults = messages_db.get_records(record_type = record_type, create_view = False)18:47
kenvandineresults would be a big json string of all your messages18:47
kenvandinewhich you can then iterate over and get individual messages out18:47
kenvandineand various bits of data18:47
kenvandineyour application could also define views for data your app uses regularly18:48
kenvandineso perhaps you want to get all the records that have images18:48
kenvandineso you could view all your facebook friend's albums18:48
kenvandinethe data is pretty easy to get at in desktopcouch18:48
kenvandinesomeone just needs to write an extension for f-spot to make it easy to browse them :)18:48
kenvandineand maybe even comment18:48
kenvandineok, i think that is all i have18:49
kenvandinequestions? comments?18:49
kenvandinethe widget doesn't provide that, but that would be useful18:49
kenvandineit is on my todo list :)18:49
kenvandinebut won't happen for 2.30, we are in feature freeze already18:50
kenvandinenext?18:50
kenvandine QUESTION: Are the special gtk widgets available in languages other than python? Say I want to use this in an app written in c# and use gtk#? or just plain c?18:52
kenvandinenot yet18:52
kenvandinebut for 2.32 (or 3.0, we are following gnome version numbering now)18:52
kenvandinei would like to have a libgwibber18:52
kenvandineto provide just that18:52
kenvandineif there are any C gurus out there and want to help18:53
kenvandineplease let me know18:53
kenvandineideally we want to move the widgets to C and just create python bindings for it18:53
kenvandineQUESTION: how can an user disable an app from using gwibber if that app doesn't have it as a preference? I don't really feel comfortable with random apps being able to send messages out to the public without me having to confirm it first18:53
kenvandinehadn't really thought about that18:54
kenvandinethere isn't really any mechanism for applications register with gwibber to control access18:54
kenvandinewe'll discuss that next cycle18:54
kenvandinewell, thanks everyone18:55
kenvandinei'll be back for the next session which is showcasing gwibber18:55
=== ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - http://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Event: Ubuntu Opportunistic Developer Week - Current Session: SHOWCASE: Gwibber - Ken VanDine - Instructor: kenvandine || Questions in #ubuntu-classroom-chat
kenvandineanything we have to do to get lernid to show the slides?19:03
kenvandine[SLIDE 1]19:04
kenvandineok everyone, welcome to our little showcase on Gwibber19:05
kenvandineGwibber is a social networking desktop service and client19:05
kenvandineGwibber has kind of a fun history, started out as just a playground and has really blossomed19:05
kenvandine[SLIDE 2]19:06
kenvandineRyan Paul created Gwibber as a tool to learn about pygtk development while writing an article for Ars Technica19:06
kenvandineback in 200719:06
kenvandineand for a while it was really just his little playground for experimenting with new technologies19:07
kenvandineresearch for other articles he wrote19:07
kenvandineetc19:07
kenvandinei don't think he had any idea it would become quite so popular :)19:07
kenvandineRyan is seg|ars :)19:08
kenvandinewho is listening in here, so hopefully he can correct me when needed :)19:08
kenvandine[SLIDE 3]19:08
kenvandinethe slides are mostly here as a place to stick some screenshots to show how gwibber has evolved19:08
kenvandinewhich is actually quite from to see, as a long time user19:09
kenvandines/from/fun19:09
kenvandine[SLIDE 4]19:09
kenvandinethis is gwibber in it's first incarnation19:09
kenvandineback in 200719:09
kenvandinemostly all pygtk19:10
kenvandinequite simplistic19:10
kenvandine[SLIDE 5]19:10
kenvandinethis was when Ryan was experimenting with using cairo directly to render the messages19:10
kenvandine[SLIDE 6]19:11
kenvandineGwibber was starting to look a bit nicer here19:11
kenvandineRyan had moved on to using webkit to render the messages pane19:11
kenvandineand styling with css19:11
kenvandineit was quite slick looking and easily themed at this point19:12
kenvandineand he had just added facebook support19:12
kenvandinethis was 2008ish19:12
kenvandine[SLIDE 7]19:13
kenvandinethis is from about the same time, maybe even the same version19:13
kenvandineshows the accounts dialog19:13
kenvandine[SLIDE 8]19:14
kenvandinethis was the 2.0 era, which is what we were working on getting into Karmic19:14
kenvandinebut i hate to say 2.0 was never really robust enough19:14
kenvandineit was the first version that split out the separate service19:14
kenvandineand relied heavily on DBus for all of it's message handling19:14
kenvandinewhich actually proved quite problematic19:15
kenvandinebut you can see it is starting to look a little differnent now19:15
kenvandine[SLIDE 9]19:15
kenvandinethis is what Gwibber looks like today19:15
kenvandineit has come a long way19:16
kenvandinethe desktop service is now working very well19:16
kenvandinethe python API is proving to be useful19:16
kenvandineas is the DBus API19:16
kenvandinethere have been quite a bit of buzz around about the new look, which is nice19:17
kenvandine[SLIDE 10]19:17
kenvandineand now it is included in Lucid Lynx by default19:17
kenvandineGwibber has come a long way!19:17
kenvandinethanks seg|ars, you rock!19:17
kenvandineGwibber has matured enough that we are including it in Ubuntu, and in an LTS even19:18
kenvandinei don't have screenshots of this19:18
kenvandinebut if you are running Lucid19:18
kenvandineyou can see it for yourself19:18
kenvandinethe Me Menu, up to the left of the Session menu in the top panel19:18
kenvandinethere is a text entry now19:19
kenvandinethat posts to Gwibber19:19
kenvandineso right from the panel you can fire off a quick post19:19
kenvandinehttp://arstechnica.com/open-source/reviews/2010/03/hands-on-ubuntu-goes-social-gains-me-menu-in-1004-alpha-3.ars19:19
kenvandinean article from Ryan about it19:19
kenvandinehttp://static.arstechnica.com/ubuntu1004a3/me-menu.png19:19
kenvandinethere's a screenshot19:20
kenvandinelets talk a little about some of the technology used in the current gwibber and talk about how Gwibber has benefited19:20
kenvandine[SLIDE 11]19:21
kenvandinepython, pygtk and webkit19:21
kenvandineGwibber has been using those since nearly the beginngin19:21
kenvandinea bit about webkit19:21
kenvandinethe messages pane is completely rendered in Webkit which makes themes, etc easy19:22
kenvandineand it fits the model well19:22
kenvandineone thing to note, which is interesting19:22
kenvandinethe navigation bar on the left side and the bar to the bottom of the text input with the toggle buttons19:22
kenvandinethat isn't gtk19:22
kenvandinethose are all rendered in webkit19:22
kenvandineRyan was having a hard time bending pygtk to do what he needed to do, so he moved all that to webkit and made them look like they belong19:23
kenvandinequite nice19:23
kenvandineso just something you might want to look at the source for if you ever feel restricted and want to break outside the box19:24
kenvandinenot saying it is good or bad... it would be nice if pygtk was able to do what he wanted19:24
kenvandinealso in the 2.30 series Gwibber has moved to desktopcouch for data storage19:25
kenvandineso preference, accounts and messages are all stored in desktopcouch19:25
kenvandinethere are many pluses to this19:25
kenvandineone is we get syncing for free, if you pair desktopcouch instances on your local network or if you are an ubuntu one user, your gwibber accounts/settings sync automatically19:26
kenvandineso you only have to configure it in one place19:26
kenvandinebut desktopcouch also helped us solve the robustness problems we had with DBus19:26
kenvandinewe still use DBus for quite a bit of stuff19:26
kenvandinebut we never pass data around19:26
kenvandinethe message data is a pretty complex data structure that used to make dbus tip over19:26
kenvandinenow we access the message data directly from couch19:27
kenvandinedesktopcouch also gives us events so we could remove code, which is always good19:27
kenvandinefor example19:27
kenvandinenotifications and messaging indicator support19:27
kenvandinenow instead of the service getting a new message and telling a bunch of different moving pieces to go off and do things19:28
kenvandineit just writes it out to the database19:28
kenvandinewe see the event for a new record added to the database, and we show a notification for it19:28
kenvandineremoved a bunch of the logic we had in place before19:28
kenvandineand let couchdb do the work19:29
kenvandinesame for adding replies/mentions to the messaging menu19:29
kenvandinealso the client uses those same events to know when to render the messages pane, etc19:29
kenvandineit is used all over the place19:29
kenvandineso desktopcouch was a huge win for Gwibber imho19:30
kenvandineok, now lets move on to questions19:30
kenvandine QUESTION: Joined later but is there in Lucid with gwibber a daemon that monitors incomming messages (and notifies ofcourse) instead of having Gwibber open all the time?19:30
kenvandineyes19:30
kenvandinethe gwibber-service will run in the background without the client open19:30
kenvandinewhile running, if you have notifications enabled19:31
kenvandineit will display them19:31
kenvandineetc19:31
kenvandinewhen you launch the client, it will just talk to the existing service that is running19:31
kenvandineif you "Quit" the client in the Gwibber menu, it will shutdown the service19:31
kenvandineto give users a way to stop it if they like19:31
kenvandinebut19:31
kenvandineif you close the window it doesn't kill the service19:32
kenvandinein the next cycle, i would like to make that configurable19:32
kenvandineQUESTION: with those bars rendered in webkit, do they look out of place if you change the theme or does it somehow adapt to the colours of your gtk theme?19:32
kenvandinethey do honor some of the gtk values19:33
kenvandinelike color and such (i think)19:33
kenvandinebut for example the icons don't change19:33
kenvandinei would like to move to a model where we provide icons that can be over ridden by the theme19:34
kenvandineQUESTION: the desktopcouch is restricted but how are the passwords stored in the DB?19:34
kenvandineit is stored as a string19:35
kenvandinewe should move those into the keyring in the next cycle, hopefully19:35
kenvandineright now you need keyring access to access couch19:36
kenvandinebut ideally we shouldn't store them that way anyway19:36
kenvandineQUESTION: As many users really don't get along well with microblogging, shouldn't the RSS functionality of gwibber get back and be improved, for them use gwibber, at least, like an rss reader?19:37
kenvandinenot sure i agree19:38
kenvandinei personally don't see RSS reading as social networking, but i know others disagree with me19:38
kenvandinei am more keen on growing the use cases19:38
kenvandinelike photo sharing19:38
kenvandineetc19:39
kenvandinethere are many ways we can utilize gwibber outside of "microblogging"19:39
kenvandinei don't really consider Gwibber a microblogging client anymore19:39
kenvandinei see it is a social networking aggrigator19:39
kenvandinea single mechanism for you and the applications you use more effectively interact with your friends19:40
kenvandinelike viewing your friend's facebook photo albums, tagging people in photos, commenting on them, etc19:40
kenvandineall from inside of f-spot19:40
kenvandineinstead of through a web interface19:40
kenvandinethere are many other use cases like that19:41
kenvandinewhich i would like to focus more on19:41
kenvandineany more questions?19:42
kenvandineok... so thanks for your time19:43
kenvandinei hope everyone enjoys gwibber!19:43
=== ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - http://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi || Event: Ubuntu Opportunistic Developer Week - Current Session: Building multimedia into your app with GStreamer - Laszlo Pandy - Instructor: laszlok || Questions in #ubuntu-classroom-chat
laszlokAlright I guess that's me20:01
laszlokI'm going to be talking about GStreamer20:02
laszlokI'm not sure how much you guys know already, so if you have any questions or I missed something, please ask in #ubuntu-classroom-chat20:02
laszlok[SLIDE 1]20:02
laszlokSeems the schedule in lernid isn't working20:03
laszlokI'll get a link for those who aren't using lernid as well20:04
laszlokFor those that do still see the gwibber slides, is the schedule list loaded? On my lernid neither is there20:05
laszlokLets do it manually then: http://laszlopandy.com/files/opportunistic-developer-gstreamer.pdf20:05
laszlokThanks for reporting guys, it must be a server issue20:07
laszlokI'm gonna start now and you can follow along manually from the link above20:08
laszlokFirst slide!20:08
laszlokSo I'm talking about adding gstreamer support to your app20:08
laszlokThis means multimedia stuff20:08
laszlokaudio and video all that cool jazz20:08
laszlokGStreamer can do pictures too, but most apps use separate libraries for images because of performance20:09
laszlokHowever I am not talking about playing event sounds like an alert20:09
laszlokthose sounds which are really short and you just play, never pause, seek, etc20:10
laszlokGStreamer is overkill for that, and there are better libraries for that (which could be covered in another talk)20:11
laszlokSecond slide!20:11
laszlokGStreamer is a framework for putting decoders, filters, encoders, file readers, etc. together20:12
laszlokif you are writing a decoder to integrate with ubuntu you are going to want to know about the framework aspects20:12
laszlok[SLIDE 2]20:12
laszlokbut for us, in most cases we only care about GStreamer as a library20:13
laszlokand GStreamer is a very good library, it takes very little code to achieve a great deal in your application20:14
laszlokSlide three!20:14
laszlokGStreamer can automatically detect which plugin to use depending on the protocol (file://, http://, etc) the media container, codec20:15
laszlokyou just have to tell it where to get the data and it works like magic20:16
laszlokanother important aspect is that it spawns its own threads for each stream and provides a nice interface so we don't have to deal with it20:17
laszlokwhen you are playing video there is a thread for audio and one for video, but all the signals come in to your main thread like gtk callbacks; nice and clean20:17
laszlokQUESTION: How portable is it? Can I make gstreamer apps for Win/Mac?20:18
laszlokyes you can20:18
laszlokits supports many platforms20:18
laszlokI am part of the Jokosher project which uses GStreamer for an audio multitracker20:19
laszlokand we have been doing windows releases for more than a year i think20:19
laszlokthe official releases don't have builds for any platform, but like the GStreamer PPA for ubuntu there is a win-builds which has all the latest releases for windows20:20
laszlokI don't have any experience using it on the Mac, but I know it is supported20:20
laszlok[SLIDE 4]20:20
laszlokokay now we get into the real stuff20:20
laszloktoday's talk is mostly a high level usage of gstreamer, but i think it is important to understand a few concepts20:21
laszlokQUESTION: This is a really specific one, and may want to wait, but can I seek into a movie and display a still thumbnail?20:21
laszlokyes, remind me about that at the end and I will tell you specifically how to do it20:22
laszlokSo Gstreamer is concerned with moving data20:22
laszlokdata flow happens inside a pipeline20:22
laszlokthere are a bunch of elements all linked together20:23
laszlokthe source(s) provide data to the pipeline, and the sink(s) take it out and put it somewhere20:23
laszlokan example pipeline would start with a file source, move it through a decoder, pass it to audioconvert (which makes sure it is in the right format) and that would pass it to the Pulseaudio sink20:24
laszlokA bin is another important concept. It is just a collection of elements within a pipeline20:25
laszlokyou can think of it as a pipeline inside a pipeline20:25
laszlok[SLIDE 5]20:25
laszlokhere are some example sources20:26
laszlokgstreamer has lots20:26
laszlokgiosrc is important because it provides all the protocols of the gnome virtual file system20:26
laszlokif you run the debugging tool gst-inspect-0.10 with no arguments in the terminal it will tell you alll the plugins you have installed20:27
=== w1nGNUtz_ is now known as w1nGNUtz
laszlokgst-inspect-0.10 pulsesrc will give you all the info about the pulse audio source20:27
laszlok[SLIDE 6]20:28
laszloksame deal with sinks20:28
laszlokQUESTION: how does changing framerates fit into the pipline?20:30
laszloksince its been brought up i will quickly cover another topic20:31
laszlokthe link between each element has a thing called caps (short for capabilities)20:31
laszlokit specifies what kind of data is allowed to be sent from one element to another20:31
laszlokfor example pulsesink does not want to receive ogg vorbis data, it wants raw audio20:32
laszlokso we have to feed it through the ogg vorbis decoder first20:32
laszlokit is possible to manually specify the caps of each link, so you can say you want this audio converted to 44100Hz before sending to pulsesink20:33
laszlokin the same way you can force the framerate of a video stream20:33
laszlokbut if you get the caps wrong, the elements will complain that they can't link20:33
laszlokso its a bit more advanced, we won't be doing caps today20:34
laszlok[SLIDE 7]20:34
laszlokso here is an example pipeline20:34
laszlokthe gstreamer language syntax has the ! meaning link to20:34
laszlokthere is a debugging tool which uses this syntax, and you can test out all sorts of cool pipelines20:35
laszlokevery can try:20:35
laszlokgst-launch-0.10 audiotestsrc wave=ticks ! pulsesink20:35
laszlokgst-inspect-0.10 audiotestsrc will tell you near the bottom what other types of waves you can use20:36
laszlok[SLIDE 8]20:36
laszlokas I have mentioned there is all sorts of complicated stuff going on in gstreamer20:37
laszlokbut playback is a pretty simple use case, so they invented playbin220:37
laszlokit is used by totem and many gstreamer apps who just want to play audio and video20:38
laszlokit internally manages almost everything for us20:38
laszlokreading from the right protocol, finding decoders, it even handles subtitles and can do gapless playback20:39
laszlok[SLIDE 9]20:39
laszlokso lets try a really simple playbin example using some python20:39
laszlokhttp://laszlopandy.com/files/playbin.html20:39
laszlokas you can see in the code, we have to import gst and create a new playbin2 object20:40
laszlokall gstreamer plugins are dynamically loaded, so you have to use the factory to create it20:41
laszlokgst.element_factory_make('playbin2') will fail if the plugin 'playbin2' is not installed20:41
laszlokthe next step we set the URI20:41
laszlokthen we get the bus, which allows us to monitor the pipeline through signals, just like in GTK20:42
laszlokwe can attach a callback to the "message::eos" signal (EOS is end of stream)20:42
laszlokthen we set the playbin to the playing state20:42
laszlokthere are four states in gstreamer: NULL, READY, PAUSED and PLAYING20:43
laszlokNULL is for when you want to free the resources and destroy the object20:43
laszlokafter we set the state we start the mainloop so we get receeve signals20:44
laszlok*receive20:44
laszlokany questions about this code?20:44
laszlok[SLIDE 10]20:44
laszlokQUESTION: what if it's a file not a url?20:45
laszlokdo it like file:///home/laszlo/... (it has to be an absolute path)20:45
laszlokQUESTION: can you elaborate on states? what state a pipeline should have if we don't want to destroy it, but rather play next source?20:45
laszlokyou can have it in whichever you like20:46
laszlokgstreamer allows the pipeline to be changed while its playing20:46
laszlokthough its probably best to move it to paused, switch the uri, and put it back to playing20:46
laszlokQUESTION: what other proteries can we set with playbin.set_property()? And sourcefile is it allway an uri?20:47
laszlokgst-inspect-0.10 playbin220:47
laszlokthat will tell you about the properties20:47
laszlokplaybin2 only deals with URIs, but those can be file:/// ones too20:47
laszlokQUESTION: and if we want to stop playing and restart it from beginning?20:47
laszlokyou should set it to PAUSED and seek to the start (seeking is slide 17 if we get there)20:48
laszlokto clarify PAUSED means the same thing as PLAYING except that the data is not moving20:48
laszlokthe data is there on the edge, but the damn is closed20:49
laszlokif you have a video in PAUSED you will see the first frame20:49
laszlokQUESTION: does using playbin also handle installing packages for missing codecs like with mp3's? Or that's something that should still be manually programmed?20:49
laszloki dont think playbin does this itself20:50
laszlokGStreamer will send an error on the pipeline ("missing codec!")20:50
laszlokand you can catch the error and use another module (import gst.pbutils) to launch the codec install window20:51
laszlokokay everyone got that code from slide 10?20:51
laszlokits a GTK window, pretty simpel20:51
laszlokits needs some gstreamer love20:51
laszlok[SLIDE 11]20:51
laszlokhttp://laszlopandy.com/files/playback_interface.html20:52
laszlok[SLIDE 12]20:52
laszlokhttp://upload.wikimedia.org/wikipedia/commons/d/df/Hurricane_Connie_1955.ogg20:53
laszlokand back to the code http://laszlopandy.com/files/playback_interface.html20:53
laszlokso we are gonna take the playbin2 example and put it into the GUI class20:54
laszlokjust make sure all code in __init__() is before the call to self.main_window.show_all()20:54
laszlok[SLIDE 13]20:55
laszlokon_finish becomes a class method, and we update the GUI when it is called20:55
laszlokwhoever was asking about to restart from the beginning, this is it20:55
laszlokseek_simple, FORMAT_TIME, to position 020:56
laszlok[SLIDE 14]20:56
laszlokthis is straightforward20:56
laszlokin the button handler we either set the state to PLAYING or PAUSED20:56
laszlokright before we do gtk.main_quit() we should set the state to NULL otherwise gstreamer will print an error on the console about resources not being cleaned up properly20:57
laszlok[SLIDE 15]20:57
laszlokif you guys are building this code, it should sorta work now20:57
laszlokQUESTION: the uris in playbin need to be absolute, or I can write them like relative paths for files?20:58
laszlokI have not figured out a way to make file:/// work without absolute paths20:58
laszlokyou should get the current working durectory using the os.path.abspath('.') command in python20:58
laszlok[SLIDE 16]20:59
laszlokheres the tricky part, we have to attach the video to our GTK window20:59
laszlokplaybin2 has a video-sink property which allows us to switch where it sends the video20:59
laszlokwe should wait until the widget is realized before connecting it, cause it might not be on the screen and video_area.window will be None21:00
=== ChanServ changed the topic of #ubuntu-classroom to: Welcome to the Ubuntu Classroom - http://wiki.ubuntu.com/Classroom || Support in #ubuntu || Upcoming Schedule: http://is.gd/8rtIi
laszlokthen we just set the xwindow id21:00
laszlokand we're done21:01
laszlok[SLIDE 17]21:01
laszlokheres how to seek from the value on the slider21:01
laszlok[SLIDE 18]21:01
laszlokif there any questions about that, ask me21:02
laszlok[SLIDE 19]21:02
laszlok QUESTION: How do I seek into a movie and display a still thumbnail?21:02
laszlokthanks for reminding me21:02
laszlokyou should keep the state in PAUSED so that the first frame shows21:03
laszlokand then seek with FLUSH and KEY21:03
laszlokthis is important because if it is plaused, there is data in the pipeline thats not moving21:03
laszlokFLUSH will tell the elements to throw out the old data and only use the data from the new position21:04
laszlokKEY_UNIT will tell the decoder to seek to a key frame, so you don't get a partially decoded blocky frame from the video21:04
laszlokQUESTION: what do that seek flags mean?21:04
laszlokis it clear from what i said on slide 18?21:05
laszlok[SLIDE 18]21:05
laszlok[SLIDE 19]21:05
laszlokif you have not seen gobject.timeout_add() before, it will call your function at the time interval given as long as the mainloop is still running21:06
laszlok100 means 100 milliseconds, or 10 times per second21:06
laszlokif you have some other intensive operation which is blocking the mainloop it will be called much less often and your GUI will be unresponsive21:07
laszlokso every 100ms we query the position and update the slider, make sense?21:07
laszlok[SLIDE 20]21:07
laszlokhere I am querying both the position and duration, because sometimes the duration changes21:08
laszlokfor many audio types like mp3, the duration is an estimate21:08
laszlokthere is no way to know the exact length without playing the file to the end and saving the result21:09
laszlokQUESTION: Why would the duration change? for streaming files?21:09
laszlokoften with streaming files you don't know how long it is, the duration query will fail21:09
laszlokother files like mp3 i mentioned the duration is estimated by calculating it form the size of the file and the bitrate21:10
laszlokif the file is a poorly encoded variable bitrate file, the duration estimate may change21:10
laszlokgstreamer requires you specify the format you want21:11
laszlokbut almost always we want TIME (which comes back in nanoseconds)21:11
laszlokyou can call query bytes or percent for example21:11
laszlok[SLIDE 21]21:11
laszlokso once we have the values from the query here is how we update the slider21:12
laszlokmake sure you block the on_slider_change function or that will be called with you do set_value()21:12
laszlok[SLIDE 22]21:12
laszlokhttp://laszlopandy.com/files/playback_interface_complete.html21:12
laszlokheres the completed code21:12
laszlokI added a little thing there to always read the file from the current directory21:13
laszlok120 lines and you can do the basics of what totem does21:13
laszlokthanks guys, i'm just gonna answer questions now21:14
laszlokQUESTION: possible to extract/record a slice of audio/video from a file to a file ? faster than real-time ? (yes/no answer is enough)21:14
laszlokyes it is possible21:14
laszlokif you don't have an element which requires real time (like going from files to files) gstreamer will go as fast as possible21:15
laszlokto extract slices of audio/video files the elements you want are in the gnonlin package21:15
laszlokthey are used by pitivi for example21:15
laszlokQUESTION: is it possible to seek to/display a still of a non-keyframe?21:16
laszlokyes, i believe this is what happens when you leave out the KEY_UNIT21:16
laszlokit will take longer to seek, because the decoder has to find a key frame and decode everything after that to get the full frame21:16
laszlokbut this is what totem does for its frame-by-frame step feature21:17
laszlokQUESTION: Can you help me to find "wmap" codec for .wmv files?21:17
laszlokfind me later in #jokosher and i'll see21:17
laszlokQUESTION: If I have a video in HD and only want to display it as a thumbnail, is this simple, and does gstreamer do clever stuff to provide low memory usage?21:18
laszlokgstreamer is fairly good with memory usage21:18
laszloki believe the nautilus thumbnailer which gets those pictures of your videos uses gstreamer and just sets it to paused to extract a single frame21:19
laszloksometimes it does use a lot of IO though21:19
laszlokokay so if the movie is playing, and you want a smaller copy of it21:20
laszlokthis is just the same as when you scale down the totem window21:20
laszlokthere is an element called tee to make copies of streams21:21
laszlokso you can have one small copy and one large playing at the same time even21:21
laszlokbut for an HD video it sill have to decode the entire thing, then scale it down which will require a lot of cpu21:21
laszlokhowever the gstreamer magicians have some more tricks which are currently only prototype and use cairo to draw the scaled video directly instead of copying the HD video around21:22
laszlokyou may have seen it on the planet, it sounds pretty much like what mattmole is looking for21:22

Generated by irclog2html.py 2.7 by Marius Gedminas - find it at mg.pov.lt!