[15:59] <dpm> Everyone ready for the last day or UADW?
[16:00] <dpm> the people on #ubuntu-classroom-chat say yes! :)
[16:01] <ClassBot> Logs for this session will be available at http://irclogs.ubuntu.com/2011/09/09/%23ubuntu-classroom.html following the conclusion of the session.
[16:01] <dpm> Hello everyone and welcome to this last edition of the Ubuntu App Developer Week
[16:01] <dpm> lots of sessions with great speakers today,
[16:02] <dpm> speaking of which, let's give a warm welcome to Satoris, who'll be talking about adding multitouch support to your GTK+ apps with libgrip!
[16:03] <Satoris> Hi all. My name is Jussi Pakkanen, and I am a member of the uTouch team at Canonical.
[16:03] <Satoris> Today I'll be talking about adding gesture support for existing apps using libgrip.
[16:04] <Satoris> Libgrip is a simple, "GTK-flavored" library for gestures.
[16:04] <Satoris> The uTouch stack has lots of other parts as well, but I won't talk about them. If there is demand and time at the end, I can give an overview.
[16:05] <Satoris> The first thing you might want to do is to open the libgrip tutorial page, which is here: https://wiki.ubuntu.com/Multitouch/GripTutorial
[16:06] <Satoris> I'm going to go through it so it pays to have it open. Get the code as well, it is attatched to the page.
[16:06] <Satoris> A word of warning, I'm going to describe libgrip 0.3.1 and newer.
[16:07] <Satoris> This is not available in Natty, and won't be. So if you are using Natty, you need to compile libgrip from source.
[16:07] <Satoris> There is also a known bug in Oneiric's libgrip. We pushed a fix to that some 20 minutes ago.
[16:08] <Satoris> So it should be available soon.
[16:09] <Satoris> The main things to know about libgrip are device types and subscriptions.
[16:09] <Satoris> There are three different kinds of devices: touchscreens, touchpads and independent devices (Apple Magic Mice).
[16:10] <Satoris> Whenever you say you want certain types of gestures, you create a subscription.
[16:11] <Satoris> So the basic structure of a libgrip application is to subscribe to gestures, such as "one finger drags on touchscreens" or "two finger rotates on any device".
[16:11] <Satoris> When these gestures are performed, libgrip calls your callback functions.
[16:11] <Satoris> It is very similar to how GTK sends "redraw" or "mouse motion" events.
[16:12] <Satoris> I forgot to mention that libgrip 0.3 requires GTK 3.0. You can't use it on GTK+ 2 apps.
[16:12] <Satoris> But who has those anymore. :)
[16:13] <Satoris> The subscription is describe on the tutorial page under "subscription".
[16:14] <Satoris> It is very straightforward.
[16:14] <Satoris> In the tutorial all subscriptions have the same callback function.
[16:15] <Satoris> You can have different ones, it does not really matter.
[16:15] <Satoris> People seem to prefer having one and then demultiplexing stuff by themselves.
[16:16] <Satoris> In the test app we have a viewport and we want to be able to move it around with finger drags.
[16:17] <Satoris> So we subscribe two finger drags for all devices and also one finger drags for touchscreens.
[16:18] <Satoris> Very simple.
[16:18] <Satoris> The gesture callback function is also quite straightforward.
[16:19] <Satoris> We check how much the user has dragged and update the viewport correspondingly.
[16:19] <Satoris> However there is one thing to note.
[16:20] <Satoris> When dragging on a touchpad, the scroll directions need to be changed.
[16:20] <Satoris> This is because historically dragging down on a touchpad moves the "document" up.
[16:21] <Satoris> This is something that may change in the future, newest OSX has the option to make drags on touchpad the same as on touchscreens.
[16:22] <Satoris> Libgrip does not invert the axes for you, because it can't know whether you are dragging an object or scrolling a view.
[16:23] <Satoris> This is the app developer's responsibility.
[16:24] <Satoris> The rest of the code is pretty standard GTK.
[16:24] <Satoris> So in this case adding gestures took a couple of dozen of lines of code.
[16:25] <Satoris> For a more complicated example, I recommend you to look at the gesture patches for Evince and EOG.
[16:25] <Satoris> They can be found in the respective oneiric source packages.
[16:26] <Satoris> This means that the oneiric's EOG and Evince will be gesture enabled out of the box.
[16:28] <Satoris> Those patches have drags, document rotations and pinch to zoom.
[16:29] <Satoris> Subscribing to gestures is quite simple, but there are certain things that will most likely surprise you.
[16:29] <Satoris> The main thing being that gestures that human beings make are rarely pure.
[16:30] <Satoris> Simply dragging two fingers along most likely triggers pinches or rotations as well.
[16:30] <Satoris> That is because the human beings are not robots, at least not yet.
[16:31] <Satoris> Their fingers move involuntarily, even if they try to keep them steady.
[16:32] <Satoris> Unfortunately it is impossible to differentiate between "drag only, but fingers get slightly rotated accidentally" and "drag with a touch of rotation" in the general case.
[16:33] <Satoris> So again, the app developer has to do work here.
[16:34] <Satoris> Usually it is best to design the UI so that spurious events do not matter.
[16:35] <Satoris> The basic guideline is that the gesture recognition stack can not read the user's mind. That is the job of an app developer.
[16:36] <Satoris> Another thing to note is that trackpads especially have very different sizes.
[16:37] <Satoris> A basic mini-laptop's touchpad is only about one quarter (or less) of a MacBook Pro's touch pad.
[16:38] <Satoris> So you really have to consider these things when dealing with, e.g. pinch sensitivity.
[16:38] <Satoris> And on the other hand if you want your app to be used on a touch screen, those are even bigger.
[16:40] <Satoris> And the last thing: you should probably never subscribe to single finger drags on touchpads.
[16:40] <Satoris> That grabs the mouse pointer, because one finger drags are mapped to mouse motion.
[16:42] <Satoris> That's roughly what I had prepared. I'm open for questions.
[16:44] <ClassBot> tomalan asked: The MacBook Pro touchpad can distinguish between "tap" and "click". Is libgrip also capable of this?
[16:45] <Satoris> If by "click" you mean pushing the pad so that it produces a mouse click, then yes.
[16:45] <Satoris> Pushing it so hard that it produces the clicking sound, that is.
[16:46] <Satoris> I use a Macbook for development (and am using it at this very moment) and have set it up so that taps do not produce mouse clicks and you use two fingers for scrolling.
[16:47] <Satoris> Rather than using the edge for scrolling.
[16:48] <Satoris> You can get the taps by subscribing to the GRIP_GESTURE_TAP event.
[16:49] <Satoris> I'm not sure what happens if you have set it up so that tapping the pad lightly causes a mouse click.
[16:50] <Satoris> I find that behaviour hugely irritating and always disable it as the first thing I do.
[16:50] <ClassBot> tomalan asked: In OSX i also like the feature that i open the context menu by "clicking" with two fingers and if I keep clicked and move to the menu entry and then release the selected menu entry gets activated. Do you know if it's possible to configure Ubuntu to behave the same?
[16:50] <ClassBot> There are 10 minutes remaining in the current session.
[16:51] <Satoris> You can set up Ubuntu so that clicking with two fingers creates a right-click. That brings up a context menu most of the time.
[16:51] <Satoris> This behaviour might even be the default. Not sure though.
[16:53] <Satoris> You have to release one finger to be able to select stuff on the menu.
[16:53] <ClassBot> tomalan asked: I meant that i keep the pad pressed with one finger, navigate to my target with the other one and the release the second finger to activate the entry, so i don't have to click twice
[16:54] <Satoris> This needs some plumbing and code changes that are not present currently AFAIK. But we will work on these issues in the next cycle.
[16:55] <ClassBot> There are 5 minutes remaining in the current session.
[16:58] <Satoris> Last chance for questions.
[17:00] <dpm> thanks Satoris for a great session!
[17:00] <ClassBot> Logs for this session will be available at http://irclogs.ubuntu.com/2011/09/09/%23ubuntu-classroom.html following the conclusion of the session.
[17:01] <njpatel> Hi! My name is Neil Patel, I'm the System Architect in the Canonical Desktop Experience (DX) Team and I am also the Technical Lead for the Unity project. I'd like to talk today about the massive changes we've done to the Lens infrastructure in Unity this cycle, with an introduction to writing a simple Google Docs Lens & Scope!
[17:01] <njpatel> Although I have a rough outline for at least the first half of this session, feel free to ask questions in #ubuntu-classroom-chat as you have them, and I'll answer as soon as it makes sense :)
[17:01] <njpatel> I should add, all the information here, and much more, will be available on https://wiki.ubuntu.com/Unity/Lenses later on today and across the weekend.
[17:02] <njpatel> So, a quick history on Lenses: They started off being named "Places", and they were, in Natty, little daemons that would show up on your Launcher and allow you to search through Applications/Files/AskUbuntu/Gwibber etc.
[17:02] <njpatel> Although Lenses in that form were pretty good, we wanted them to be more powerful (as did the Lens authors!) so for Oneiric we decided that we should swallow our pride and re-archtect them to provide more features, and make it easier to add new features later on i.e. make a base API that we can carry forward through 12.04 and beyond.
[17:02] <njpatel> So, people who are currently re-writing their Places as Lenses, sorry :) We promise it won't be this bad again!
[17:02] <njpatel> One of the largest changes we made to the Lenses was to split out the concept of what controls the renderering/filters of a Lens to what provides data. In essence, we wanted to be able to make a Lens be able to just define it's properties, but then let anything be able to provide data to the Lens.
[17:03] <njpatel> So, the job of a Lens is to create an Lens instance, add some categories, add some filters and thats it, it no longer does any searching of it's own. So what does the searching? Scopes!
[17:03] <njpatel> Scopes are the engine of the Dash. They plug into Lenses over D-Bus and then provide data to the Lens.
[17:03] <njpatel> This is best described in an example: We now ship a Music Lens by default in Oneiric. It ships with a Banshee Scope which provides search results for Banshee. However, instead of being stuck with a usesless Lens because you use Amarok or Rhythmbox (or you having to create an alternative Lens for your favourite music player), you can just create the Amarok or Rhythmbox Scope for the Music Lens.
[17:03] <njpatel> The Scope would plug right into the Lens, providing results from Amarock in the main Lens.
[17:04] <njpatel> Also, a Lens is not confined to having only one Scope. The Music Lens could easily have a Banshee Scope, a GrooveShark Scope, a Spotify Scope etc. So you can search multiple data sources from one Lens, just install the ones you like :)
[17:04] <njpatel> Due to not many people likely to want to ship a Lens by itself without a Scope (wouldn't be very useful!) we have made it easy for you to ship a default Scope with your Lens, without having to create an extra daemon, and you'll see this later on.
[17:04] <njpatel>  
[17:05] <njpatel> The largest user-visible change we made for a Lens is it can now create Filters
[17:05] <njpatel> Filters allow the user to easily, er, filter the search results
[17:05] <njpatel> We ship with four filters by default: CheckOption, RadioOption, MultiRange and Ratings
[17:06] <njpatel> You can create as many as you'd like, and show/hide them as needed
[17:07] <njpatel> The filters API is meant to be easy to use and we hope to add more filters in the 12.04 cycle
[17:07] <njpatel> (we'd love to hear ideas!)
[17:08] <njpatel> woah, lag :)
[17:09] <njpatel> Andy80 asked: how do we control how the data found by a scope is displayed? The Lens provide the filters, ok... but can I design the view and incorporate it into the dash?
[17:09] <njpatel> The Lens controls how the results are displayed per-category
[17:09] <njpatel> So, we ship with a vertical or horizontal orientation for the results, but we really would like to see more
[17:10] <njpatel> The complexity comes in that the results are rendered by Unity, and so must be written with Nux
[17:10] <njpatel> We'd happily accept new renderers and are planning to add some whizzbang ones for 12.04!
[17:11] <njpatel>  
[17:11] <njpatel> On that point, the two most important things a Lens will do is add Filters and Categories to itself
[17:12] <njpatel> We spoke about filters (and will come back to that in the example), so i should cover Categories
[17:12] <njpatel> Categories are headings inside the dash when you search for something inside a Lens
[17:13] <njpatel> e.g. the Apps lens has "Most Frequently Used", "Installed" and "Apps Available for Download"
[17:14] <njpatel> When you want to add a result to the results model inside a Scope, you tell Unity which category to put the result in. If a Category has no results, Unity will not show it
[17:14] <njpatel> We normally add three Categories, but there is no limit
[17:14] <njpatel> three just seems to fit nicely in the way Unity renderers the Dash :D
[17:15] <ClassBot> Andy80 asked: how do we control how the data found by a scope is displayed? The Lens provide the filters, ok... but can I design the view and incorporate it into the dash?
[17:15] <njpatel> woops
[17:15] <ClassBot> Andy80 asked: I suppose we will have to write two different Lens (one for Unity and one for Unity-2D) but since the Scope exposes data trough an API/d-bus ecc... is the same Scope usable on both versions of Unity?
[17:16] <njpatel> Any Lens you write works fine with both Unity and Unity-2D, they share the same code for talking to Lenses
[17:17] <njpatel> This is because a Lens does not contain any specific UI code (i.e. gtk or Qt or Nux), it just requests that a category should use the horizontal tile, for instance, and it's up to the Unitys to render it correctly
[17:18] <njpatel> It's actually one of my favourite things about Lenses/Scopes, they are purely data with requests for renderering, the actual renderering is handled elsewhere and therefore can be changed to match it's environment (i.e. searching your lenses from a webpage ;)
 njpatel: so they can be written in any language? C, C++, Python, Vala ecc...?
[17:18] <njpatel> Yep
[17:19] <njpatel> Due to this boundary between the renderering-side (Unity) and the data side(Lenses or Scopes), you can write them in any language
[17:19] <njpatel> libunity (the library you use to write them in) is a GObject library with full GObject introspection support
[17:20] <njpatel> it also means, C++ Unity can have a Vala Applications Lens which has a Python Freedesktop-menus Scope and a Javascript Web Apps Scope :)
[17:20] <njpatel> we like that :D
[17:21] <njpatel>  
[17:21] <njpatel> Okay, so the other important thing is for Unity to actually find the Lens
[17:22] <njpatel> for this, you need to choose a "id" for your lens i.e. Music lens = "music", Google Docs Lens = gdocs
[17:22] <njpatel> you use that id and install your .lens file in /usr/share/unity/lenses/$id/$id.lens
[17:22] <njpatel> This is an example .lens file: http://paste.ubuntu.com/685980/
[17:23] <njpatel> We use DBus activation to start a lens, so you also need to install a .service file into /usr/share/dbus-1/services
[17:24] <njpatel> with the .lens file, Unity can start up, have a look at what lenses are installed, be able to load their icon etc, and then, when the user does  a search, activate the Lens daemon (which will in turn activate it's scopes)
[17:25] <njpatel> if you are writing a Scope, you have a Scope file which only contains the DBusName and DBusPath attributes and you install it in the Lens's folder in /usr/share/unity/lenses
[17:26] <njpatel> you'll also need a dbus .service file as Scopes are also started with dbus activation
[17:26] <njpatel>  
[17:26] <njpatel> As I mentioned earlier, we recognise that there are not many Lenses that people would like to ship which don't have at least one Scope readily available to make it useful
[17:27] <njpatel> What we didn't want was to  have you jump through hoops to create .scope files, .service files and a new binary just for this, so we have a little function that allows you to ship a Lens daemon with an in-built Scope easily
[17:28] <njpatel> this does't stop other Scopes from plugging into your Lens, but just lets you get up-and-running much more easily
[17:28] <njpatel>  
[17:29] <njpatel> So, for this session I had originally wanted to write a  Scope for the Music lens, but ran into some difficulty with getting an API key for an OSS project (urgh), so I decided to be a bit daring and started a Google Documents Lens + Scope using Python (a language which we did not have support for until this morning!)
[17:30] <njpatel> The code is available at lp:unity-lens-gdocs
[17:30] <njpatel> but I have some pastes of the a few stages of the code which I thought would be useful to go over
[17:31] <njpatel> So, the first commit I did looked something like: http://paste.ubuntu.com/685979/
[17:31] <njpatel> I should say, excuse any bad python in there, I'm a C/C++ guy :)
[17:32] <njpatel>  
[17:32] <njpatel> So, from line 27
[17:32] <njpatel> You can see we create the Lens object
[17:33] <njpatel> hopefully the properties we set are obvious enough
[17:33] <njpatel> but you have some top-level control here over your Lens, like whether you want it to show in the global search results
[17:33] <njpatel> what the search hint is, etc etc
[17:33] <njpatel> then the first two important things are done
[17:34] <njpatel> populate_filters() does exactly what it says on the tin
[17:34] <njpatel> As in the comments, I love the RadioOption filter as it's one of the easiest to use :)
[17:35] <njpatel> CheckOption, RadioOption and MultiRange filters are all "OptionFilter" sub-classes in libunity. This type of filter is basically one which can have multiple options in it, and an option can be active or inactive
[17:36] <ClassBot> Andy80 asked: you defined both scope + lens in that single python code?
[17:37] <njpatel> Yep, I'm using the lens.add_local_scope() method to have my Lens be automatically useful when installed without having to also ship a separate scope in the package
[17:37] <njpatel> this does mean that my lens and scope are in the same process, and this might be okay for some lenses and not okay for others, you would need to judge that yourself
[17:38] <njpatel> due to this Lens being very much tied to a single service, having the scope ship with the lens and in the same process is fine
[17:38] <njpatel> My goal is to make gdocs lens also ship a scope for the files lens, if you'd just like to search all files in one lens
[17:38] <njpatel> but this is a good enough example for now  :)
[17:38] <njpatel>  
[17:39] <njpatel> So, the filters you add here are synced with Unity (so it knows what to display) and also down to the Scopes (so they can query them during search)
[17:39] <njpatel> I'll show you how the Scope uses the filters during a search later on
[17:40] <njpatel> As mentioned, check, radio and multi-range work very similarly, the Ratings filter is the simplest, providing you with a rating of 0.0 to 1.0 depending on how many stars the user has clicked
[17:41] <njpatel> Using the visible property on a filter, you should be able to show/hide filters depending on which other options are clicked
[17:41] <njpatel> be a bit careful, though, i don't think either Unity has a nice transition for that change yet, so it might surprise the user a bit!
[17:42] <njpatel>  
[17:42] <njpatel> so, in populate_categories() we add the categories we'd like to use
[17:43] <njpatel> for each category, we can choose and icon, set it's name and choose how we'd like it rendered
[17:44] <njpatel> The order in which you add these categories is the numbering you use in the Scope when adding a result. So, for the first category, the integer you'd use when adding the result would be 0
[17:44] <njpatel> usually an enum would be best to make your code readable for others and yourself down the line!
[17:45] <njpatel> coming back up, if you ignore line 46 & 47, calling lens.export() would get you ready for Scopes that want to plug in and ready fro Unity to start showing/search your lens
[17:45] <njpatel> if you plan on making a Lens and separate scopes, this is all you need to do
[17:46] <njpatel> however, for our GDocs lens we want to also provide some results from GDocs (as we're not really expecting anything else to plug into such a specific Lens)
[17:46] <njpatel>  
[17:47] <njpatel> so, looking at lines 46 & 47, we can see we create a class that internally creates a Scope, and then inform our Lens that a "local scope" exists i.e. tell it that there is a scope that doesn't have a .scope file and cannot be activated, but is actually in the same process as itself
[17:48] <njpatel>  
[17:48] <njpatel> Okay, so looking at the UserScope class
[17:48] <njpatel> My idea is to give this Lens the ability to handle multiple google accounts (say work + personal), so i've encapsulated the logic of talking to a single account into one class and one scope
[17:49] <njpatel> therefore, when I do add support for multiple accounts, I'll be creating a UserScope for each, and calling lens.add_local_scope() for each too
[17:49] <njpatel> the start of the __init__ of the class mostly deals with the gdata bits
[17:50] <njpatel> line 129 onwards does the fun stuff, namely it creates the Scope by telling the Unity.Scope class where to export it's internal dbus object
[17:50] <ClassBot> There are 10 minutes remaining in the current session.
[17:50] <njpatel> after that, it connects to two signals to know when the normal search and global search changes
[17:52] <njpatel> we keep them different, including two different results models, as we fully expect the Lens to behave a bit differently with global search versus with normal search. A good example of this is that a normal search will take into account filters but the global one won't
[17:52] <njpatel> moving on, you can see there are some methods to grab the search string and then just one method to actually update the results, either global or normal
[17:53] <njpatel> in update_results_model(), we have a placeholder that just grabs all the documents and throws it in the results model, it doesn't deal with the search string yet
[17:54] <njpatel> so, the next thing would be to get it to do that
[17:54] <njpatel> http://paste.ubuntu.com/685988/
[17:54] <njpatel> that is the diff for making the Scope  actually search
[17:54] <njpatel> as you can see, we now take into account the global search (though as we are not doing any filtering we can still keep it simple when searching)
[17:55] <njpatel> the rest of the diff is mostly just constructing a good gdata search URI
[17:55] <njpatel> running out of time!
[17:55] <ClassBot> There are 5 minutes remaining in the current session.
[17:55] <njpatel> moving on, this http://paste.ubuntu.com/686007/ shows us responding to filters
[17:56] <njpatel> if you look at apply_filters()
[17:57] <njpatel> You can see that, for each filter that we know/care about, we query the scope to get the Filter object, and then we query the filter to see what the current active option is
[17:57] <njpatel> however, we only do this if we are not doing a global search
[17:58] <njpatel> the "id" property of the filter is the same as what the Lens set when creating the filter. Through this mechanism, you can always be sure you are reacting to the correct filter
[17:59] <njpatel> also you can see I changed the filters a bit so I could be a bit lazy with ids :)
[17:59] <njpatel> so, we're at the end, it would have been good to have more time but I'll be updating the wiki page with lots more info
[17:59] <njpatel> https://wiki.ubuntu.com/Unity/Lenses
[18:00] <njpatel> also, I'll be continuing to develop this lens if you want to subscribe to the branch on Launchpad to keep up!
[18:00] <dpm> thanks a lot njpatel, that was awesome!
[18:00] <ClassBot> Logs for this session will be available at http://irclogs.ubuntu.com/2011/09/09/%23ubuntu-classroom.html following the conclusion of the session.
[18:01] <dpm> talking of which, next up mterry will be talking about connecting your apps to the cloud with ubuntu one :)
[18:01] <mterry> Hello everybody!  Thanks njpatel!
[18:01] <mterry> So I'm Michael Terry, an Ubuntu developer as well as the developer of Deja Dup, a backup program
[18:02] <mterry> I recently added support for Ubuntu One to my program, and I thought I'd share how that went, and some simple examples of how to connect to Ubuntu One Files
[18:02] <mterry> I have lots of notes for this session here: https://wiki.ubuntu.com/mterry/UbuntuOneFilesNotes11.10
[18:02] <mterry> Which you may be interested in going through as I talk
[18:02] <mterry> Please ask questions any old time
[18:03] <mterry> So, for this session (and for my purposes with Deja Dup), I only needed simple file functionality
[18:03] <mterry> get, put, list, delete basically
[18:03] <mterry> So we'll go through each of those basic ideas to help anyone else that's interested in integrating do so easily
[18:03] <mterry> We'll use Python, since that's most convenient
[18:04] <mterry> So let's create together a simple python script that can do basic file operations with U1
[18:04] <mterry> You'll need an updated ubuntuone-couch package from Ubuntu 11.10
[18:04] <mterry> I've backported it in my PPA
[18:04] <mterry> For this sessoin
[18:04] <mterry> So if you want to play along at home, but are stuck on Ubuntu 11.04, please do the following:
[18:05] <mterry> sudo add-apt-repository ppa:mterry/ppa2
[18:05] <mterry> sudo apt-get update
[18:05] <mterry> sudo apt-get upgrade
[18:05] <mterry> That will give you a new ubuntuone-couch
[18:05] <mterry> So to start, let's write a super simple Python script that can just accept an argument
[18:05] <mterry> [18:05] <mterry> #!/usr/bin/python
[18:05] <mterry> import sys
[18:05] <mterry> if len(sys.argv) <= 1:
[18:05] <mterry>   print "Need more arguments"
[18:05] <mterry>   sys.exit(1)
[18:05] <mterry> print sys.argv[1:]
[18:05] <mterry> [18:05] <mterry> Very basic.  We'll augment this with more in a second
[18:06] <mterry> Save that as u1file.py
[18:06] <mterry> And open a terminal in that same directory
[18:06] <mterry> The first thing you have to do when interacting with U1 is make sure the user is logged in
[18:06] <mterry> There is a helper library for that in ubuntuone.platform.credentials
[18:06] <mterry> It's designed to work with Twisted and be asynchronous
[18:07] <mterry> But we just want simple synchronous behavior for now
[18:07] <mterry> So I'll show you a function that will fake synchronousity by opening an event loop and waiting for login to finish
[18:07] <mterry> [18:07] <mterry> #!/usr/bin/python
[18:07] <mterry> import sys
[18:07] <mterry> _login_success = False
[18:07] <mterry> def login():
[18:07] <mterry>   from gobject import MainLoop
[18:07] <mterry>   from dbus.mainloop.glib import DBusGMainLoop
[18:07] <mterry>   from ubuntuone.platform.credentials import CredentialsManagementTool
[18:07] <mterry>   global _login_success
[18:07] <mterry>   _login_success = False
[18:07] <mterry>   DBusGMainLoop(set_as_default=True)
[18:07] <mterry>   loop = MainLoop()
[18:07] <mterry>   def quit(result):
[18:07] <mterry>     global _login_success
[18:08] <mterry>     loop.quit()
[18:08] <mterry>     if result:
[18:08] <mterry>             _login_success = True
[18:08] <mterry>   cd = CredentialsManagementTool()
[18:08] <mterry>   d = cd.login()
[18:08] <mterry>   d.addCallbacks(quit)
[18:08] <mterry>   loop.run()
[18:08] <mterry>   if not _login_success:
[18:08] <mterry>     sys.exit(1)
[18:08] <mterry> if len(sys.argv) <= 1:
[18:08] <mterry>   print "Need more arguments"
[18:08] <mterry>   sys.exit(1)
[18:08] <mterry> if sys.argv[1] == "login":
[18:08] <mterry>   login()
[18:08] <mterry> [18:08] <mterry> This may be easier to see on the wiki page https://wiki.ubuntu.com/mterry/UbuntuOneFilesNotes11.10#Logging_In
[18:08] <mterry> The important bit is from ubuntuone.platform.credentials import CredentialsManagementTool
[18:08] <mterry> followed by
[18:08] <mterry>    cd = CredentialsManagementTool()
[18:08] <mterry>    d = cd.login()
[18:08] <mterry> The rest is just wrappers to support the event loop
[18:08] <mterry> And to support calling "python u1file.py login"
[18:09] <mterry> So try running that now, and you should see a neat little U1 login screen
[18:09] <mterry> Unless...  You've already used U1, in which case, nothing happens (because login() doesn't do anything in that case)
[18:09] <mterry> So let's add a logout function for testing purposes
[18:09] <mterry> [18:09] <mterry> def logout():
[18:09] <mterry>   from gobject import MainLoop
[18:09] <mterry>   from dbus.mainloop.glib import DBusGMainLoop
[18:09] <mterry>   from ubuntuone.platform.credentials import CredentialsManagementTool
[18:09] <mterry>   DBusGMainLoop(set_as_default=True)
[18:09] <mterry>   loop = MainLoop()
[18:09] <mterry>   def quit(result):
[18:09] <mterry>     loop.quit()
[18:09] <mterry>   cd = CredentialsManagementTool()
[18:09] <mterry>   d = cd.clear_credentials()
[18:09] <mterry>   d.addCallbacks(quit)
[18:09] <mterry>   loop.run()
[18:09] <mterry> if sys.argv[1] == "logout":
[18:10] <mterry>   logout()
[18:10] <mterry> [18:10] <mterry> Now add that to your script and you can call "python u1file.py logout" to go back to a clean slate
[18:10] <mterry> OK.  So we have a skeleton script that can talk to U1, but it doesn't do anything yet!
[18:10] <mterry> Let's upload a file
[18:10] <mterry> Oh, whoops
[18:10] <mterry> First, we have to make sure we create a volume
[18:10] <mterry> In U1-speak, a volume is a folder that can be synchronized between the user's computers
[18:11] <mterry> By default, new volumes are not synchronized anywhere
[18:11] <mterry> But let's create a testing volume so that we can upload files without screwing anything up
[18:11] <mterry> Note that the "Ubuntu One" volume always exists
[18:11] <mterry> Creating a volume is a simple enough call:
[18:11] <mterry> [18:11] <mterry> def create_volume(path):
[18:11] <mterry>   import ubuntuone.couch.auth as auth
[18:11] <mterry>   import urllib
[18:11] <mterry>   base = "https://one.ubuntu.com/api/file_storage/v1/volumes/~/"
[18:11] <mterry>   auth.request(base + urllib.quote(path), http_method="PUT")
[18:11] <mterry> if sys.argv[1] == "create-volume":
[18:11] <mterry>   login()
[18:12] <mterry>   create_volume(sys.argv[2])
[18:12] <mterry> [18:12] <mterry> You'll see that we make a single PUT request to a specially crafted URL
[18:12] <mterry> There is no error handling in my snippets of code.  I'll get into how to handle errors at the end
[18:12] <mterry> Add that to your u1file.py, and now you can call "python u1file.py create-volume testing"
[18:12] <mterry> If you open http://one.ubuntu.com/files/ you should be able to see the new volume
[18:13] <mterry> Congratulations if so!
[18:13] <mterry> You'll also note that I included a call to login() before creating the volume
[18:13] <mterry> This was to ensure that the user was logged in first
[18:13] <mterry> You'll also note that I made this weird auth.request call
[18:14] <mterry> This is a wrapper function provided by ubuntuone-couch that handles the OAuth signature required by U1 to securely identify the user
[18:14] <mterry> This is why you had to log in first
[18:14] <mterry> And the 11.10 version has some important fixes, which is why I backported it for this session
[18:14] <mterry> OK, *now* let's upload a file
[18:14] <mterry> (any questions?)
[18:14] <mterry> Uploading is a two-step process
[18:15] <mterry> First, we tell the server we want to create a new file
[18:15] <mterry> Then the server tells us a URL path to upload the contents to
[18:15] <mterry> I'll give you the code then we can talk about it
[18:15] <mterry> [18:15] <mterry> def put(local, remote):
[18:15] <mterry>   import json
[18:15] <mterry>   import ubuntuone.couch.auth as auth
[18:15] <mterry>   import mimetypes
[18:15] <mterry>   import urllib
[18:15] <mterry>   # Create remote path (which contains volume path)
[18:15] <mterry>   base = "https://one.ubuntu.com/api/file_storage/v1/~/"
[18:15] <mterry>   answer = auth.request(base + urllib.quote(remote),
[18:15] <mterry>                         http_method="PUT",
[18:15] <mterry>                         request_body='{"kind":"file"}')
[18:15] <mterry>   node = json.loads(answer[1])
[18:15] <mterry>   # Read info about local file
[18:16] <mterry>   data = bytearray(open(local, 'rb').read())
[18:16] <mterry>   size = len(data)
[18:16] <mterry>   content_type = mimetypes.guess_type(local)[0]
[18:16] <mterry>   content_type = content_type or 'application/octet-stream'
[18:16] <mterry>   headers = {"Content-Length": str(size),
[18:16] <mterry>              "Content-Type": content_type}
[18:16] <mterry>   # Upload content of local file to content_path from original response
[18:16] <mterry>   base = "https://files.one.ubuntu.com"
[18:16] <mterry>   url = base + urllib.quote(node.get('content_path'), safe="/~")
[18:16] <mterry>   auth.request(url, http_method="PUT",
[18:16] <mterry>                headers=headers, request_body=data)
[18:16] <mterry> if sys.argv[1] == "put":
[18:16] <mterry>   login()
[18:16] <mterry>   put(sys.argv[2], sys.argv[3])
[18:16] <mterry> [18:16] <mterry> There are three parts to this
[18:16] <mterry> First is the request to create a file
[18:16] <mterry> We give a URL path and PUT a specially crafted message "{'kind':'file'}"
[18:16] <mterry> Then, we read the local content
[18:16] <mterry> And push it to where the server told us to
[18:16] <mterry> (this is the "content_path" bit)
[18:16] <mterry> The response from the server (and the specially crafted message we gave it) is called JSON
[18:17] <mterry> It's a special format for encoding data structures as strings
[18:17] <mterry> Looks very Python-y
[18:17] <mterry> The 'json' module has support for reading and writing it
[18:17] <mterry> As you can see
[18:17] <mterry> We also use a different base URL for uploading the content
[18:17] <mterry> We use "files.one.ubuntu.com"
[18:18] <mterry> So now, let's try this new code out:
[18:18] <mterry> "python u1file.py put u1file.py testing/u1file.py"
[18:18] <mterry> This will upload our script to the new testing volume we created
[18:18] <mterry> Again, you can visit the U1 page in your browser and refresh it to see if it was created
[18:19] <mterry> If so, congrats!
[18:19] <mterry> Also note that we had to specify the content length and content type
[18:19] <mterry> These are mandatory
[18:19] <mterry> I calculated both in my example (using the mimetypes module)
[18:19] <mterry> But if you already know the mimetype, you can skip that bit of course
[18:20] <mterry> OK, let's try downloading the script we just uploaded
[18:20] <mterry> This is very similar, but uses GET requests instead of PUT ones
[18:20] <mterry> Again, two step process
[18:20] <mterry> We first get the metadata about the file, which tells us the content_path
[18:20] <mterry> And then we get the content
[18:20] <mterry> [18:20] <mterry> def get(remote, local):
[18:20] <mterry>   import json
[18:20] <mterry>   import ubuntuone.couch.auth as auth
[18:20] <mterry>   import urllib
[18:20] <mterry>   # Request metadata
[18:20] <mterry>   base = "https://one.ubuntu.com/api/file_storage/v1/~/"
[18:20] <mterry>   answer = auth.request(base + urllib.quote(remote))
[18:20] <mterry>   node = json.loads(answer[1])
[18:20] <mterry>   # Request content
[18:21] <mterry>   base = "https://files.one.ubuntu.com"
[18:21] <mterry>   url = base + urllib.quote(node.get('content_path'), safe="/~")
[18:21] <mterry>   answer = auth.request(url)
[18:21] <mterry>   f = open(local, 'wb')
[18:21] <mterry>   f.write(answer[1])
[18:21] <mterry> if sys.argv[1] == "get":
[18:21] <mterry>   login()
[18:21] <mterry>   get(sys.argv[2], sys.argv[3])
[18:21] <mterry> [18:21] <mterry> Nothing ground breaking there
[18:21] <mterry> Again, we hit files.one.ubuntu.com for the content
[18:21] <mterry> And again, there is no error checking here
[18:21] <mterry> We'll get to that later
[18:21] <mterry> Let's try to download that script we uploaded
[18:21] <mterry> "python u1file.py get testing/u1file.py /tmp/u1file.py"
[18:22] <mterry> This will put it in /tmp/u1file.py
[18:22] <mterry> Now let's see what we downloaded
[18:22] <mterry> "less /tmp/u1file.py"
[18:22] <mterry> It should look right
[18:22] <mterry> So we can create volumes, upload, and download files
[18:23] <mterry> Big things left to do are list files, query metadata, and delete files
[18:23] <mterry> Let's start with listing
[18:23] <mterry> [18:23] <mterry> def get_children(path):
[18:23] <mterry>   import json
[18:23] <mterry>   import ubuntuone.couch.auth as auth
[18:23] <mterry>   import urllib
[18:23] <mterry>   # Request children metadata
[18:23] <mterry>   base = "https://one.ubuntu.com/api/file_storage/v1/~/"
[18:23] <mterry>   url = base + urllib.quote(path) + "?include_children=true"
[18:23] <mterry>   answer = auth.request(url)
[18:23] <mterry>   # Create file list out of json data
[18:23] <mterry>   filelist = []
[18:23] <mterry>   node = json.loads(answer[1])
[18:23] <mterry>   if node.get('has_children') == True:
[18:23] <mterry>     for child in node.get('children'):
[18:23] <mterry>       child_path = urllib.unquote(child.get('path')).lstrip('/')
[18:23] <mterry>       filelist += [child_path]
[18:23] <mterry>   print filelist
[18:23] <mterry> if sys.argv[1] == "list":
[18:23] <mterry>   login()
[18:23] <mterry>   get_children(sys.argv[2])
[18:23] <mterry> [18:23] <mterry> This is very similar to downloading a file
[18:23] <mterry> But we add "?include_children=true" to the end of the request URL
[18:24] <mterry> Then we grab the list of children from the JSON data returned
[18:25] <mterry> black_puppydog has noted that my ubuntuone-couch backport has a bug preventing it from working right
[18:25] <mterry> I will prepare a new package
[18:25] <mterry> But you can fix it by doing the following
[18:26] <mterry> sudo gedit /usr/share/pyshared/ubuntuone-couch/ubuntuone/couch/auth.py
[18:26] <mterry> Search for ", disable_ssl_certificate_validation=True" near the bottom
[18:26] <mterry> And remove it
[18:26] <mterry> Sorry, I really thought I had tested with that
[18:27] <mterry> I've uploaded a fixed package, but it will take a few minutes to build
[18:27] <mterry> So to download the complete file we've got so far...
[18:27] <mterry> grab it here: https://wiki.ubuntu.com/mterry/UbuntuOneFilesNotes11.10?action=AttachFile&do=view&target=6.py
[18:28] <mterry> I'll give everyone a few seconds to catch up
[18:28] <mterry> Save that 6.py file as u1file.py
[18:28] <mterry> And do the following commands to get to the same state:
[18:28] <mterry> python u1file.py login
[18:28] <mterry> python u1file.py create-volume testing
[18:29] <mterry> python u1file.py put u1file.py testing/u1file.py
[18:29] <mterry> python u1file.py get testing/u1file.py /tmp/u1file.py
[18:29] <mterry> python u1file.py list testing
[18:29] <mterry> Really sorry about that
[18:30] <mterry> Note that if you are working on a project that needs to work in 11.04 but you still want this functionality
[18:30] <mterry> You can just locally make a copy of ubuntuone-couch's auth.py file and use it in your project (as long as the license is compatible of course)
[18:31] <mterry> OK, I'm going to wait just a moment longer to let people catch up and re-read the file now that it will actually work when they run it
[18:31] <mterry> So when you run "python u1file.py list testing" you should get a list of all the files you put there
[18:32] <mterry> Which I expect will just be the one u1file.py file
[18:32] <mterry> So now, let's see if we can't get a bit more info about that file
[18:32] <mterry> Sometimes you'll want to query file metadata
[18:32] <mterry> This is very much like downloading
[18:32] <mterry> But without getting the actual contents
[18:32] <mterry> [18:32] <mterry> def query(path):
[18:32] <mterry>   import json
[18:32] <mterry>   import ubuntuone.couch.auth as auth
[18:32] <mterry>   import urllib
[18:32] <mterry>   # Request metadata
[18:32] <mterry>   base = "https://one.ubuntu.com/api/file_storage/v1/~/"
[18:32] <mterry>   url = base + urllib.quote(path)
[18:32] <mterry>   answer = auth.request(url)
[18:33] <mterry>   node = json.loads(answer[1])
[18:33] <mterry>   # Print interesting info
[18:33] <mterry>   print 'Size:', node.get('size')
[18:33] <mterry> if sys.argv[1] == "query":
[18:33] <mterry>   login()
[18:33] <mterry>   query(sys.argv[2])
[18:33] <mterry> [18:33] <mterry> Adding that to your file will let you call "python u1file.py query testing/u1file.py"
[18:33] <mterry> You should see the size in bytes
[18:33] <mterry> There is a bit more metadata available (try inserting a "print node" in there to see it all)
[18:33] <mterry> And the last big file operation we'll cover is the easiest
[18:34] <mterry> Deleting files
[18:34] <mterry> [18:34] <mterry> def delete(path):
[18:34] <mterry>   import ubuntuone.couch.auth as auth
[18:34] <mterry>   import urllib
[18:34] <mterry>   base = "https://one.ubuntu.com/api/file_storage/v1/~/"
[18:34] <mterry>   auth.request(base + urllib.quote(path), http_method="DELETE")
[18:34] <mterry> if sys.argv[1] == "delete":
[18:34] <mterry>   login()
[18:34] <mterry>   delete(sys.argv[2])
[18:34] <mterry> [18:34] <mterry> That's simple.  Merely an HTTP DELETE request to the metadata URL
[18:34] <mterry> This covers the basic file operations you'd want to do
[18:34] <mterry> I promised I'd talk about error handling
[18:34] <mterry> So behind the scenes, this is all done using HTTP
[18:35] <mterry> And the responses you get back from the server are all in HTTP
[18:35] <mterry> So it makes sense that to check what kind of response you got, you'd use HTTP status codes
[18:35] <mterry> You may be familiar with these
[18:35] <mterry> To look at a status code, with the above examples, you'd do something like:
[18:36] <mterry> answer = auth.request(...)
[18:36] <mterry> status = int(answer[0].get('status'))
[18:36] <mterry> answer is a tuple of 2
[18:36] <mterry> The first bit is HTTP headers
[18:36] <mterry> The second is the HTTP body
[18:36] <mterry> So we're asking for the 'status' HTTP header here
[18:36] <mterry> Any number in the 200s is an "operation succeeded" message
[18:36] <mterry> There are a few important status codes to be aware of
[18:36] <mterry> 400 is "permission denied"
[18:37] <mterry> 404 is "file not found"
[18:37] <mterry> 503 is "servers busy, please try again in a bit"
[18:37] <mterry> 507 is "out of space"
[18:37] <mterry> You may also just receive a boring old 500 status
[18:37] <mterry> This is like an "internal error" message
[18:37] <mterry> Which isn't very helpful, but usually you are also given an Oops ID to go with it
[18:38] <mterry> oops_id = answer[0].get('x-oops-id')
[18:38] <mterry> If you give this to the U1 server folks, they can tell you what happened and fix the bug
[18:38] <mterry> So if you're going to print a message for the user, include that so that when they report the bug, you'll have the Oops-ID to hand over
[18:39] <ClassBot> black_puppydog asked: how about checksums? this is needed for example in dejadup, right?
[18:40] <mterry> One piece of metadata is "hash"
[18:40] <mterry> That the server will give you
[18:40] <mterry> I actually have not used that, so I don't know what checksum algorithm it uses
[18:41] <mterry> But you can also just download the file and see (which is what Deja Dup does)
[18:41] <mterry> See https://one.ubuntu.com/developer/files/store_files/cloud/
[18:41] <mterry> For a list of other metadata pieces you can get from the server
[18:41] <mterry> That also has other useful info.  It's the official documentation for this stuff
[18:42] <mterry> If anyone is interested, the Deja Dup code is actually in duplicity, a command line tool that Deja Dup is a wrapper for
[18:42] <mterry> http://bazaar.launchpad.net/~duplicity-team/duplicity/0.6-series/view/head:/duplicity/backends/u1backend.py
[18:42] <mterry> That's real code in use right now
[18:43] <mterry> If you ever have a problem playing with this stuff, the folks in #ubuntuone are very helpful
[18:43] <mterry> With Oops that you run into or whatever
[18:44] <mterry> And that's all I have!  I'll hang around for questions if there are any
[18:46] <ClassBot> black_puppydog asked: this file you used here, shouldn't that be some sort of library?
[18:46] <mterry> black_puppydog, yeah, it very well could be
[18:46] <mterry> You mean, some sort of library supported by the U1 folks to make this all easier?
[18:46] <mterry> Well...  They've already provided a lot of the code around it.  I think their intention is to focus on providing the best generic API (the web HTTP one) that all sorts of devices and languages can use.
[18:47] <mterry> I think they'd be happy to see an awesome Python wrapper library, but I don't think they want to maintain and promote one such library at the expense of others
[18:47] <mterry> This is close, it would just need much better error handling and such
[18:47] <mterry> But I also don't want to maintain it  :)
[18:48] <mterry> But really, it's not *that* much code.  A bit boiler plate, true
[18:48] <mterry> ubuntuone-couch takes care of most of the icky parts that are hard to do well (OAuth authentication)
[18:49] <mterry> Most languages have REST and OAuth libraries that can be used in conjunction to talk to the servers
[18:50] <ClassBot> There are 10 minutes remaining in the current session.
[18:51] <mterry> black_puppydog makes a good point in the chat channel.  The duplicity code has better error handling than I've presented.  So it may be a better jumping-off point to just steal wholesale than the script we've built here
[18:52] <mterry> Note that it is licensed GPL-2+
[18:52] <mterry> So if that's not appropriate, maybe just whip something similar up yourself
[18:55] <ClassBot> There are 5 minutes remaining in the current session.
[19:00] <ClassBot> Logs for this session will be available at http://irclogs.ubuntu.com/2011/09/09/%23ubuntu-classroom.html following the conclusion of the session.
[19:05] <zoopster> Ok then...sorry for the delay...I thought I was being smart, but I wasn't!!!
[19:05] <zoopster> Hi everyone. My name is John Pugh. I'm responsible for business development for the Ubuntu Software Center.
[19:06] <zoopster> What does that mean? Well it means I'm trying to find new and interesting content, apps, games, etc for inclusion in the paid marketplace of the Ubuntu Software Center.
[19:06] <zoopster> We have several very cool games in there right now - Braid, SpaceChem, Uplink, Oil Rush, etc.
[19:07] <zoopster> As well as some magazine titles...Ubuntu User
[19:07] <zoopster> I'm working with some large 3D game engine companies, HTML5 application developers, several publishers, as well as trying to get every linux supported application, game, or content I can into the Software Center.
[19:08] <zoopster> Hopefully...this session will explain some of the workings from a business side...so throw out the questions if you have 'em and I'll answer as I go along
[19:08] <zoopster> Our goal is to have a large catalog of apps that are useful to those who use Ubuntu desktop.
[19:08] <zoopster> We've already heard from Anthony about MyApps on the Ubuntu Developer Portal
[19:08] <zoopster> https://wiki.ubuntu.com/MeetingLogs/appdevweek1109/PublishingAppsInSoftwareCenterMyApps
[19:09] <zoopster> achuni went over the details of submitting an app...it's quite straightforward
[19:09] <zoopster> and we heard from Stephane about ARB and it's use of the MyApps portal.
[19:09] <zoopster> https://wiki.ubuntu.com/MeetingLogs/appdevweek1109/PublishingAppsInSoftwareCenterARB
[19:10] <zoopster> stgraber talked about how the ARB can take advantage of the MyApps portal, too. So everyone can have a single place to submit new applications no matter how they are licensed.
[19:10] <zoopster> I want to talk about the "business side" of the MyApps portal.
[19:11] <zoopster> There really isn't much to it, but I can at least introduce the terms and what you can expect from us
[19:11] <zoopster> For edification, we have the https://developer.ubuntu.com portal and a application submission portal called MyApps at https://myapps.developer.ubuntu.com
[19:11] <zoopster> The Ubuntu Developer Portal is about to be refreshed with a new look and new content so keep your eye out for it. John Oxton and David Planella discussed it on Thursday.
[19:12] <zoopster> I didn't see a direct link to that session posted yet, but the link to it will be on the wiki shortly.
[19:12] <zoopster> For this session I want to talk about submitting paid applications that will show up in the "For Purchase" area of the Ubuntu Software Center.
[19:12] <zoopster> I'm fairly certain that Anthony covered it in his session, but generally the submission process is 5 steps.
[19:13] <zoopster> You have to register (if you don't have a Launchpad.net account already) and accept the terms of service, then you are able to submit a new package.
[19:13] <zoopster> Once submitted, the packaging team will package up your application or verify the packaging source if you submitted debian source with your package.
[19:14] <zoopster> I think jml talked about pkgme in one of the sessions earlier this week...that will eventually be part of the process to help automate some of the packaging needs.
[19:15] <zoopster> Currently packaging of application submissions is a manual process so it does take time to get your package through the system because of that manual setp
[19:15] <zoopster> or step even
[19:15] <zoopster> Once the app is packaged it goes through a QA and returns back to the submitter for them to make it visible on the software center.
[19:16] <zoopster> Ok enough of that...if you need more details on the application submission process...take a look at Anthony's session.
[19:16] <zoopster> The business terms are simple. We help package the app, we host the app, we provide the payment service via pay.ubuntu.com
[19:16] <zoopster> We will return 80% of the purchase price after any applicable taxes to the developer.
[19:17] <zoopster> Payments are processed quarterly at present and payments are via paypal
[19:17] <zoopster> The developer can change the price anytime they wish - the minimum price we can accept is US$2.99
[19:18] <zoopster> I'm cruising right along....any questions so far?
[19:19] <zoopster> We only allow non-DRM, non-licensed applications at present. Anthony mentioned the work his team is doing on licensing/activation keys
[19:20] <zoopster> The "For Purchase" section of the Ubuntu Software Center is a "blind channel".
[19:20] <zoopster> Due to the plethora of privacy rules/laws worldwide and Canonical's privacy policy we cannot release any user identifiable information to the developer.
[19:21] <zoopster> So it works very similarly to Apple's AppStore and the Android Marketplace at present
[19:22] <zoopster> We are able to consume nearly any content...and will allow apps with adverts or in-app purchases (which apple does not allow) and will allow "free" clients for online games (think minecraft)
[19:23] <zoopster> We can also accept HTML5 apps
[19:23] <zoopster> What we cannot accept currently are trial (try before you buy) apps
[19:24] <zoopster> and we cannot take demo's yet...but you can link to the demo and such via the text in the description
[19:25] <zoopster> Once the application is submitted you are able to "unpublish" the application at any time
[19:25] <zoopster> This will remove the visibility from the Software Center, but the application will continue to be available for those who have already purchased it
[19:26] <zoopster> Once published on the Ubuntu Software Center you can link to the app directly and that link will launch the Software Center desktop application and present the app to the user for purchase
[19:27] <zoopster> the url is simply http://apt.ubuntu.com/p/<pkgname>  - pretty slick.
[19:30] <zoopster> So mterry asked a question...there has been tepid interest in the program. It has only been "open beta" for about 3 weeks...previously it was not available unless we specifically added the dev to the beta
[19:31] <zoopster> mterry: prior to the myapps portal being opened...I was the primary "motivator" to get applications submitted
[19:31] <zoopster> mterry: now we are averaging a new app everyday and that is picking up.
[19:32] <zoopster> and the 3rd part mterry everyone so far has been very happy with it...they are getting good sales numbers, the process was simple and they have a whole new market available to them!
[19:34] <zoopster> mterry: might want to clarify your "2nd" question not sure what you mean there
[19:34] <zoopster> oh wait...mterry you mean seeing for purchase apps in the Oneiric beta for example
[19:36] <ClassBot> mterry asked: As a developer, always living in the development version, I'd like access to For Purchase apps earlier in the dev cycle (usually they are turned off until RC or whatever).  Is that possible?
[19:36] <zoopster> hah...nice
[19:37] <zoopster> mterry: right now we're smoke testing the apps...they are not specific to any release so it's possible that we can just make them visible as the dev's test the next release
[19:37] <ClassBot> mterry asked: How happy are users?  Obviously it hasn't been available long, and you can't give specific numbers, but are we seeing the same thing the humble bundles saw, that Linux users are willing to pay money for games/apps, contrary to popular thought?
[19:38] <zoopster> mterry: another good one...the user feedback so far has been good...they are keen to purchase the apps, but we're not seeing the numbers like the humble bundle overall
[19:38] <zoopster> mterry: we are seeing similar numbers to the linux side though...it correlates relatively well
[19:39] <ClassBot> paglia_s asked: what about updates? updates can be submitted always or they need to wait for the next cycle like for free apps?
[19:39] <zoopster> paglia_s: updates can be submitted anytime - once we package we can show or help with an update - the process is not as clean as submitting a new app, but we'll allow updates
[19:40] <zoopster> oil rush is a good example...it's in "beta" pre-order mode and we're about to finish up an update
[19:40] <zoopster> the user will receive notice as they do with other apps and can accept the update...new installs will get the updated app directly
[19:43] <zoopster> I hope we'll get a cleaner process to give "beta" users access earlier to the apps....we're working on it now so we'll shoot to have some if not all for purchase apps ready for the 12.04 beta
[19:43] <ClassBot> mterry asked: How is developer perception of Ubuntu vs other distros?  Is Ubuntu perceived as the top tier distro to support?
[19:44] <zoopster> ah
[19:45] <zoopster> mterry: when it comes to linux and desktop apps, we're top tier for sure
[19:45] <zoopster> mterry: you'd be surprised about that pie...it's growing quite rapidly...especially with android coming on scene...that has opened the eyes of dev's.
[19:46] <zoopster> mterry: while mobile rules right now, it's relatively simple for them to support a bigger screen with ubuntu once they have a android app done
[19:46] <ClassBot> mterry asked: Is it possible to talk about where we hope to be against other platforms?  Do we eventually want developers to think, "I have to support Win, Mac, Android, and Ubuntu?"
[19:47] <zoopster> mterry: the goal is to have a catalog of apps that are useful to the Ubuntu user base...we have some large targets for growth as you know
[19:47] <zoopster> mterry: we do want them to think at least Win, Mac, Android, Linux...Ubuntu would be the ideal situation, yes
[19:48] <zoopster> mterry: right now the focus is on any and all linux supported applications that we can support with what we currently have available
[19:49] <zoopster> mterry: however the goal is just that...prove that Linux and specifically Ubuntu is a platform of choice and have developers look at Ubuntu and linux as a necessary platform to support
[19:50] <ClassBot> mterry asked: Is there any idea that we might want to try to get "exclusives" for Ubuntu?
[19:51] <ClassBot> There are 10 minutes remaining in the current session.
[19:51] <zoopster> mterry: sure...would love to - we do have a few that are making some specific "extras" for ubuntu and we're getting some dev's running "specials" as well
[19:52] <zoopster> mterry: short answer is yes. I think it's a little early to attempt something like that, but for 12.04 you may see some interesting things along those lines
[19:53] <zoopster> Wow...great questions...
[19:53] <ClassBot> mterry asked: Are magazines and/or books much of a focus?  I see we have Ubuntu User.  Android recently upgraded their store for books as a top tier item.  Obviously books on Ubuntu devices is a bit different than Android...
[19:54] <zoopster> mterry: we have Ubuntu User right now and there are others that are interested
[19:55] <zoopster> mterry: so yes...that is a focus as well...stay tuned.
[19:55] <ClassBot> There are 5 minutes remaining in the current session.
[19:56] <zoopster> mterry: and yes...books are a different animal...and publishers are VERY protective of their copyrights so there's a bit of work to dothere
[19:56] <zoopster> Others questions?
[19:56] <ClassBot> paglia_s asked: there isn't a section for books, do you plan do add it?
[19:57] <zoopster> paglia_s: we've asked for it, but it won't be available in 11.10 as far as I know
[19:57] <zoopster> paglia_s: it's a topic for UDS though
[19:57] <ClassBot> black_puppydog asked: are there "subcategories" for books planned/implemented in the new softwarecenter? so for example ubuntuuser could get an own category
[19:58] <zoopster> answered that one...
[19:58] <ClassBot> mterry asked: Are you finding that we want to be doing more Software Center features to support new store capabilities(like special book support, support for marketing promotions, etc)  faster than the 6 month cycle can support?
[19:58] <zoopster> mterry: yes...but we are limited to the current cycles for the back end processes
[19:58] <zoopster> mterry: and the client side too
[19:59] <ClassBot> paglia_s asked: what about deals? are them publicized by something like "20% off for x time"?
[19:59] <zoopster> paglia_s: no...but they could by editing the description
[20:00] <zoopster> paglia_s: the dev can change the price anytime they wish...and edit the description anytime as well although the desc edit requires review (you can hit me up and I can change it)
[20:00] <ClassBot> Logs for this session will be available at http://irclogs.ubuntu.com/2011/09/09/%23ubuntu-classroom.html following the conclusion of the session.
[20:01] <niemeyer> Hello everyone!
[20:01] <niemeyer> Sorry for interrupting the amazing set of questions zoopster :)
[20:02] <niemeyer> So, I'm here to talk about how to develop apps in Go today
[20:02] <niemeyer> This is the Go language not the game board obviously
[20:02] <niemeyer> http://golang.org
[20:02] <niemeyer> I'll be using an Etherpad over the conversation to put content
[20:03] <niemeyer> Please follow up here:
[20:03] <niemeyer> http://pad.ubuntu.com/ep/pad/view/ro.vJnRUh5vWNI/latest
[20:03] <niemeyer> nigelb, pleia2: Can we have that in the topic?
[20:04] <niemeyer> Please feel free to ask any questions as I go.. I'll be happy to talk
[20:04] <niemeyer> I don't have a specific script in mind, so I'll be happier to solve doubts and to talk to you than to be in a monologue presenting the language
[20:04] <niemeyer> There's plenty of material online to follow, and I'll be providing some pointers
[20:04] <niemeyer> So let's make the most of our time here to exchange ideas and solve questions
[20:05] <niemeyer> So
[20:05] <niemeyer> My name is Gustavo Niemeyer, and I'm responsible for the area of Amazing And Unknown Projects at Canonical
[20:06] <niemeyer> Was pushing Landscape for several years, and then when it started to get well known I moved to the Ensemble project
[20:06] <niemeyer> https://landscape.canonical.com, https://ensemble.ubuntu.com
[20:06] <niemeyer> Now Ensemble is getting well known, but I think I'll stick for a little longer ;-)
[20:07] <niemeyer> Little more than a year and a half ago I started playing with Go in my personal time
[20:07] <niemeyer> Not really expecting much
[20:07] <niemeyer> I've been doing that with every other language for a while.. Haskell, Erlang, Java, etc
[20:07] <niemeyer> Python was the last language to hook me up for a long time as the "preferred whenever possible" choice
[20:08] <niemeyer> After I started some projects with Go, though, I started to really appreciate what was there
[20:08] <niemeyer> It didn't really hook me up for any _specific_ feature, though
[20:08] <niemeyer> What's most amazing about it is really the sum of the parts
[20:08] <niemeyer> In fact, it's a pretty simple language
[20:08] <niemeyer> So most aspects you'll see there will be a "Ah, but that's done elsewhere too" moment
[20:08] <niemeyer> and that's surely the case
[20:09] <niemeyer> The detail is precisely that it's putting all of those things in _one_ language
[20:09] <niemeyer> In a very nice to digest fashion
[20:09] <niemeyer> For instance,
[20:09] <niemeyer> Go is statically compiled, but has good reflection capabilities
[20:09] <niemeyer> It also has what I like to call "static duck typing"..
[20:10] <niemeyer> If you've used Python or similar languages before, you know what I mean by that..
[20:10] <niemeyer> In Python, you just provide a value to a function, say, and expect that the function will handle it correctly
[20:11] <niemeyer> So any method that has, say, a read( ) method, can be provided to a method that calls value.read()
[20:11] <niemeyer> In Go, there are interfaces
[20:11] <niemeyer> But they're not like interfaces in languages like Java, for instance
[20:11] <niemeyer> if you declare an interface like this:
[20:11] <niemeyer> type Validator interface { IsValid() bool }
[20:12] <niemeyer> You can use that to declare a function..
[20:12] <niemeyer> func MyF(v Validator) { println(v.IsValid()) }
[20:12] <niemeyer> and any objects at all that define such a method can be used
[20:12] <niemeyer> they don't have to _declare_ that they implement the Validator interface.. they simply do as long as they have the right methods
[20:13] <niemeyer> ...
[20:13] <niemeyer> Anyway
[20:13] <niemeyer> That's called structural typing, btw.. if you fancy CS
[20:13] <niemeyer> A few other factors of the top of my head..
[20:13] <niemeyer> Go is
[20:13] <niemeyer> - Garbage collected
[20:14] <niemeyer> - Capable of producing good tracebacks
[20:14] <niemeyer> - Debuggable with gdb
[20:14] <niemeyer> - Has an easy to use module/package system
[20:15] <niemeyer> Hmm.. lots of leavers :)
[20:15] <niemeyer> Maybe I should get into some action.
[20:15] <niemeyer> Let's get it working then.
[20:16] <niemeyer> First thing, you'll need to install the language
[20:16] <niemeyer> It's available in Oneiric
[20:16] <niemeyer> If you're using an older release, there is a PPA available
[20:16] <niemeyer> Supporting down to Lucid
[20:16] <niemeyer> sudo add-apt-repository ppa:gophers/go
[20:17] <niemeyer> sudo apt-get update
[20:17] <niemeyer> sudo apt-get install golang
[20:17] <niemeyer> Just the last command for Oneiric
[20:17] <niemeyer> Now, let's produce a hello world example..
[20:17] <niemeyer> I'll guide you towards having a sane environment that you can evolve from, rather than just a quick and dirty one
[20:18] <niemeyer> So, create a directory:
[20:18] <niemeyer> mkdir ~/gopath
[20:18] <niemeyer> and export the variable:
[20:18] <niemeyer> export GOPATH=~/gopath
[20:18] <niemeyer> This instructs goinstall that you want to be using that directory for installing programs and packages
[20:19] <niemeyer> Now, let's create a source directory for our program
[20:19] <niemeyer> mkdir -p ~/gopath/src/example.com/mycmd
[20:19] <niemeyer> cd ~/gopath/src/example.com/mycmd
[20:20] <niemeyer> When you host your source code, you'll have only that last bit withing the revision control, usually
[20:20] <niemeyer> the example.com is just namespacing the import.. it'll become more clear as we go on
[20:21] <niemeyer> So, you're now within that directory
[20:21] <niemeyer> Let's type the following on that file (on the fly! ugh)
[20:21] <niemeyer> package main
[20:21] <niemeyer> import "fmt"
[20:21] <niemeyer> func main() {
[20:21] <niemeyer>     fmt.Printf("Hello world\n")
[20:21] <niemeyer> }
[20:21] <niemeyer> Put that within a main.go file
[20:22] <niemeyer> The name is actually not important
[20:22] <niemeyer> Now, if you type this command:
[20:22] <niemeyer> goinstall example.com/mycmd
[20:22] <niemeyer> You should get mycmd within ~/gopath/bin/mycmd
[20:22] <niemeyer> Try to execute it
[20:23] <niemeyer> Congratulations!
[20:23] <niemeyer> You've just run your first Go program :-)
[20:23] <niemeyer> Let's go a bit further to understand what we're doing
[20:23] <niemeyer> Still within ~/gopath/example.com/mycmd
[20:24] <niemeyer> Let's create a second file called util.go
[20:24] <niemeyer> and type this within it:
[20:24] <niemeyer> package main
[20:24] <niemeyer> func Hello() {
[20:24] <niemeyer>     println("Hello there!")
[20:24] <niemeyer> }
[20:25] <niemeyer> Now, edit your main.go, and replace the Printf(...) with this:
[20:25] <niemeyer>     Hello()
[20:25] <niemeyer> and try the command again:
[20:25] <niemeyer> goinstall example.com/mycmd
[20:26] <niemeyer> You'll get an error regarding "fmt" not used.. simply remove the line import "fmt"..
[20:26] <niemeyer> and try again
[20:26] <niemeyer> Anyway.. the point I'm trying to make is this:
[20:26] <niemeyer> All files within a single package in Go are part of the same namespace
[20:27] <niemeyer> You can name files as you wish
[20:27] <niemeyer> They're just organizational
[20:27] <niemeyer> Now, let's get a bit fancier..
[20:27] <niemeyer> Let's introduce a package
[20:27] <niemeyer> Later we'll be playing with concurrency a bit too..
[20:28] <niemeyer> So.. let's create our package directory
[20:28] <niemeyer> mkdir -p ~/gopath/src/example.com/mypkg
[20:28] <niemeyer> cd ~/gopath/src/example.com/mypkg
[20:28] <niemeyer> Again, same idea
[20:28] <niemeyer> Files are just for organization..
[20:28] <niemeyer> Let's name ours as hello.go
[20:29] <niemeyer> Open hello.go within mypkg and put this content there:
[20:29] <niemeyer> package mypkg
[20:29] <niemeyer> func Hello(ch chan string) {
[20:30] <niemeyer>     println("Hello", <-ch)
[20:30] <niemeyer>     println("Hi as well", <-ch)
[20:30] <niemeyer> }
[20:30] <niemeyer> Let me copy & past locally to follow
[20:31] <niemeyer> Alright..
[20:31] <niemeyer> We have a package!
[20:31] <niemeyer> One very interesting detail:
[20:31] <niemeyer> This package is within its own namespace..
[20:32] <niemeyer> We can only access Hello() because it starts with a capital cased letter
[20:32] <niemeyer> This is the way Go differentiates private from exported variables, constants, functions, methods, etc
[20:32] <niemeyer> In all of those cases, capital case == exported
[20:32] <niemeyer> At first this feels a bit strange, but it has a pretty interesting consequence:
[20:33] <niemeyer> Every _usage_ of any of those things makes it clear if what's being used is public or not
[20:33] <niemeyer> So.. let's go..
[20:33] <niemeyer> Go back to our command
[20:33] <niemeyer> cd ~/gopath/example.com/mycmd
[20:33] <niemeyer> And type this new code in there:
[20:33] <niemeyer> Within file main.go, that is:
[20:33] <niemeyer> package main
[20:34] <niemeyer> import "example.com/mypkg"
[20:34] <niemeyer> func main() {
[20:34] <niemeyer>     ch := make(chan string)
[20:35] <niemeyer>      go mypkg.Hello(ch)
[20:35] <niemeyer>     ch <- "Joe"
[20:35] <niemeyer>     ch <- "Bob"
[20:35] <niemeyer> }
[20:35] <niemeyer> If I didn't screw up, you can run this now:
[20:36] <niemeyer> goinstall example.com/mycmd
[20:36] <niemeyer> and then
[20:36] <niemeyer> ~/gopath/bin/mycmd
[20:36] <niemeyer> Try that out
[20:37] <niemeyer> I made a mistake, which I'll mention in a bit, but first let's go over what just happened
[20:37] <niemeyer> We imported the package "example.com/mypkg"
[20:38] <niemeyer> This causes goinstall to look for this package, *download* it if necessary, compile, and install it
[20:38] <niemeyer> You can check that this is indeed the case by listing: ls ~/gopath/pkg/*/*
[20:39] <niemeyer> You should get a mypkg.a inside it
[20:39] <niemeyer> This is your package example.com/mypkg compiled into binary form
[20:40] <niemeyer> Then, we've called the Hello() function within it
[20:40] <niemeyer> But we haven't simply called it
[20:40] <niemeyer> The "go" keyword we've used ahead of the function call instructs the compiler that the function call should be put in the background
[20:40] <niemeyer> So mypkg.Hello() was running concurrently with your main() function
[20:41] <niemeyer> Then the statement
[20:41] <niemeyer>   println(..., <-ch)
[20:41] <niemeyer> Will block in the channel receive expression (<-ch) before a value is available for consumption
[20:41] <niemeyer> which is exactly what we did next in main()
[20:41] <niemeyer> ch <- "Joe" sends a value
[20:42] <niemeyer> The mistake I made in that case, is that the main() function is not waiting for the concurrently running function to terminate
[20:42] <niemeyer> There are tricks that may be used for this, such as
[20:42] <niemeyer> select{}
[20:43] <niemeyer> Then, in case you wanted to actually publish that code to be easily consumable,
[20:43] <niemeyer> you could put the code within Launchpad or Github
[20:43] <niemeyer> Exactly as it is
[20:43] <niemeyer> Just put the content of "mypkg" for instance within the trunk of a project launchpad.net/mypkg
[20:43] <niemeyer> And people will be able to "goinstall launchpad.net/mypkg"
[20:44] <niemeyer> So.. that's a quick introduction to the language..
[20:44] <niemeyer> But there's a _lot_ more to talk about it
[20:44] <niemeyer> unfortunately I cna't type that fast and we don't have as much time :)
[20:44] <niemeyer> So I'll provide some resources
[20:44] <niemeyer> And open to questions
[20:45] <niemeyer> The main site is the key documentation point:
[20:45] <niemeyer> http://golang.org
[20:45] <niemeyer> There's _very_ good introductory documentation there..
[20:45] <niemeyer> I recommend the Getting Started, Tutorial, and Effective Go, in that order
[20:45] <niemeyer> You should be quite proficient after that
[20:45] <niemeyer> Well.. or able to use it at least..
[20:46] <niemeyer> Proficiency comes with practice
[20:46] <niemeyer> I'm one of the external contributors of the language as well, so I'll be happy to answer deeper questions which I'm happy to be familiar with in case you have any
[20:46] <niemeyer> So, that was it.. please shoot the questions..
[20:47] <niemeyer> Or maybe not.. :-)
[20:49] <niemeyer> Alright.. I guess I'm alone in here. :-)
[20:49] <niemeyer> Thanks for attending, and please drop me a message if you have questions.
[20:50] <niemeyer> #go-nuts in this same server is also a good place to talk about it.
[20:50] <niemeyer> I'll be happy to answer questions in the -chat as well
[20:51] <ClassBot> There are 10 minutes remaining in the current session.
[20:55] <ClassBot> There are 5 minutes remaining in the current session.
[21:00] <ClassBot> Logs for this session will be available at http://irclogs.ubuntu.com/2011/09/09/%23ubuntu-classroom.html following the conclusion of the session.
[21:02] <donaldcarr_work> Good evening
[21:02] <donaldcarr_work> My name is Donald Carr, I am a PSO engineer with Nokia in Sunnyvale.
[21:02] <donaldcarr_work> I joined Trolltech in Oslo in 2005, and left for a brief sabbatical as a Qt customer working on a Skypesque client (for 2 years) in New York before relocating to the Bay Area office.
[21:03] <donaldcarr_work> I work on various embedded targets, track Qt's development across the breadth of the framework and am one of the parents of http://www.qtmediahub.com/ which is one of the largest QML projects I am aware of outside of Intel's tablet (1.2) UX stack.
[21:03] <donaldcarr_work> I work with many commercial customers who ship embedded Linux based products
[21:03] <donaldcarr_work> Embedded Linux is almost ubiquitous in embedded devices
[21:04] <donaldcarr_work> Qt is very strong in the Linux space, and the Webkit/DirectFB acceleration in particular have placed us primely in the STB (Set top box) market
[21:04] <donaldcarr_work> I am here today to discuss QtQuick
[21:04] <donaldcarr_work> This is a very laxly structured talk, and we can adjust the content to peoples needs
[21:04] <donaldcarr_work> basically I have hacked on and around QML a fair amount
[21:05] <donaldcarr_work> and rather than just showing people trivial examples
[21:05] <donaldcarr_work> will cover:
[21:05] <donaldcarr_work> What QtQuick is
[21:05] <donaldcarr_work> How you can get hacking on it
[21:05] <donaldcarr_work> How you can use it in your projects or large projects
[21:05] <donaldcarr_work> Gotchas/strengthes associated with it
[21:06] <donaldcarr_work> Forgive my shoddy spelling, I am cut and pasting half of the material, live tapping the rest of it
[21:06] <donaldcarr_work> Feel free to rain questions down on me
[21:06] <donaldcarr_work> My question queue is empty
[21:06] <donaldcarr_work> and I feel neglected :)
[21:07] <donaldcarr_work> I would suspect/hope that people would have questions out of the gate
[21:07] <donaldcarr_work> I have no clue how big my audience is
[21:07] <donaldcarr_work> and at what level to pitch my talk
[21:07] <donaldcarr_work> We can deal with hairy QML issues if you want, feel free to direct questions to me at any point
[21:07] <donaldcarr_work> I will start with introducing QtQuick to the unitiated
[21:07] <donaldcarr_work> uninitiated
[21:08] <donaldcarr_work> QtQuick is an umbrella term used to refer to QML and the associated tooling. QML is a declarative markup language with tight bindings to javascript which enables you to rapidly create animation rich pixmap orientated UIs.
[21:08] <donaldcarr_work> There has been a fair amount of controversy surrounding QML
[21:09] <donaldcarr_work> People seem to think it is too focused on mobile devices, and that it is less suitable for desktop usage
[21:09] <donaldcarr_work> I would contest this, and will hopefully have justified our hard emphasis on the usefulness of this tech by the end of this session
[21:10] <donaldcarr_work> In order to get you hacking on this, let me step you through getting this on Ubuntu
[21:10] <donaldcarr_work> qt-sdk is available in the Ubuntu 11.04 repos
[21:10] <donaldcarr_work> in order to grab it, all you have to do is run
[21:10] <donaldcarr_work> sudo apt-get install qt-sdk
[21:10] <donaldcarr_work> (It will pull in all dependencies, if you want just grab Qt Creator and the required subset of packages for Qt development)
[21:11] <donaldcarr_work> Please be advised, the SDK version shipped with 11.04 is a little long in the tooth at this point
[21:11] <donaldcarr_work> and you can grab binaries for 32/64bit Linux here http://qt.nokia.com/downloads/
[21:12] <donaldcarr_work> Our website will provide you with Qt SDK version 1.1.3 (Qt Creator 2.3.0, Qt 4.7.4) which includes its own updating mechanism and will have infinitely superior QML tooling.
[21:12] <donaldcarr_work> I was hoping this was going to be an interactive session
[21:12] <donaldcarr_work> I am one of the parents of: http://gitorious.org/qtmediahub
[21:13] <donaldcarr_work> This project is an attempt to recreate the look and feel and a subset of the functionality that XBMC provides
[21:13] <donaldcarr_work> The functionality we provide is basically everything Qt/QML gives us for free
[21:13] <donaldcarr_work> namely accelerated multimedia playback
[21:14] <donaldcarr_work> and a heavily pixmap centric layouting engine
[21:15] <donaldcarr_work> As mentioned, I am heavily involved with aiding people in using Qt in their set top boxes, and demonstrating its performance, readibility and high level accessibility is incredibly valueable
[21:15] <donaldcarr_work> You can check out the whole project
[21:15] <donaldcarr_work> or simply browse the QML code here:
[21:15] <donaldcarr_work> http://gitorious.org/qtmediahub/confluence/trees/master
[21:16] <donaldcarr_work> This code is for the primary skin
[21:16] <donaldcarr_work> and I can happily explain/walk you through any points of interest you may have
[21:17] <ClassBot> teemperor asked: ok, some question, is qml only a language/tool for guis or for whole applications?
[21:17] <donaldcarr_work> teemperor: There is no theoretical limitation which confines it to gui usage
[21:18] <donaldcarr_work> teemperor: Any person who starts a QML application will find themselves exposing a great set of Qt functionality to QML in order to use it, so there is certainly merit to dealing with non-gui elements in QML
[21:18] <donaldcarr_work> teemperor: It is not hard to imagine headless apps written using QML
[21:19] <donaldcarr_work> teemperor: That said, one of the nicest things about it is the way the bindings allow for relatively layouting
[21:19] <donaldcarr_work> If you look at XBMC skins
[21:19] <donaldcarr_work> You will see they are fundementally simple skins with a very limited number of relatively placed items
[21:20] <donaldcarr_work> They are far less like widget based applications and far more distance friendly
[21:20] <donaldcarr_work> (10 foot UIs?)
[21:21] <ClassBot> niemeyer asked: Is there any good path today/in the works to have QML handled by a C program?
[21:22] <donaldcarr_work> niemeyer: No, there is no straight forward way to do this
[21:22] <donaldcarr_work> niemeyer: I have no doubt a braver man than me could attempt it an succeed
[21:22] <donaldcarr_work> niemeyer: I am not that man
[21:23] <donaldcarr_work> gnomie just mentioned that Unity2D is QML based
[21:23] <donaldcarr_work> this is true, and it is really clean and small
[21:23] <donaldcarr_work> The Meego tablet UX is also QML based
[21:23] <donaldcarr_work> and is the broadest most ambitious of QML I have seen in a public project
[21:24] <donaldcarr_work> every single application is QML based
[21:24] <donaldcarr_work> and all launcher via a single engine
[21:24] <donaldcarr_work> I would love to see more QML applications in the desktop domain
[21:25] <donaldcarr_work> We tried to craft a compelling demo for CES 2009 using graphicsview
[21:25] <donaldcarr_work> And we found we were struggling against it every step of the way
[21:26] <donaldcarr_work> What we produced in a similar timeframe was completely uncomparable to the QML code we managed to churn out and demo
[21:26] <donaldcarr_work> The Confluence skin I have supplied a link to above is pretty big
[21:26] <donaldcarr_work> it is also vey ugly, but valuable in that it demonstrates various problems with large QML projects
[21:27] <donaldcarr_work> and should inspire you to contrain your QML to your own set of criteria
[21:27] <donaldcarr_work> basically
[21:27] <donaldcarr_work> the language does not constrain you, and allows you to use global variables and generate spagetthi code
[21:27] <donaldcarr_work> You have to be aware of this
[21:28] <donaldcarr_work> from the outset
[21:29] <donaldcarr_work> and set the appropriate coding conventions at the outset
[21:29] <donaldcarr_work> if you look at the Delphin skin:
[21:29] <donaldcarr_work> http://gitorious.org/qtmediahub/delphin/trees/master
[21:29] <donaldcarr_work> You will see it is infinitely cleaner
[21:30] <donaldcarr_work> I would urge any of your interested in QML to checkout out and build our project
[21:30] <donaldcarr_work> as it can get you hacking QML within minutes
[21:30] <donaldcarr_work> we are more than happy to field questions
[21:30] <donaldcarr_work> http://www.qtmediahub.com/
[21:30] <donaldcarr_work> gives information about the project
[21:30] <donaldcarr_work> and our respective email addresses
[21:30] <donaldcarr_work> where you can spam us to your hearts content
[21:31] <donaldcarr_work> I have to stress that we are pushing QML everywhere
[21:32] <donaldcarr_work> and experimenting with the extent to which it increases the accessibility of otherwise incredibly complex tasks
[21:32] <donaldcarr_work> This blog posting:
[21:32] <donaldcarr_work> http://labs.qt.nokia.com/2011/08/24/qt-quick-3d-tutorial-video/
[21:32] <donaldcarr_work> demonstrates the use of QML3D to render and interact with a 3D model of a car using QML
[21:33] <donaldcarr_work> If you are in the market for more formal training, or curious as to specifics, we have free training material for Qt Quick available here:
[21:33] <donaldcarr_work> http://qt.nokia.com/learning/online/training/materials/qt-quick-for-designers
[21:33] <donaldcarr_work> and here:
[21:33] <donaldcarr_work> http://qt.nokia.com/learning/online/training/materials/qt-essentials-qt-quick-edition
[21:35] <donaldcarr_work> As you would have gathered ARM based devices are gaining momentum in an increasing array of tasks
[21:35] <donaldcarr_work> my job involves dabbling with these devices on a daily basis
[21:35] <donaldcarr_work> Is there any remaining question about what QML is?
[21:35] <donaldcarr_work> Qt has historically had a painter algorithm paint engine
[21:36] <donaldcarr_work> a style api based on this, and widgets which render using this
[21:36] <donaldcarr_work> This resulted in an ungodly mapping of atomic painters algorithm calls resolving to GL calls and massive amount of overhead
[21:37] <donaldcarr_work> This will be resolved by the scenegraph work going into Qt 5
[21:37] <donaldcarr_work> As I mentioned earlier, there has been some public concern that Qt will become less applicable for desktop apps
[21:37] <donaldcarr_work> Our engineers have already blogged about:
[21:37] <donaldcarr_work> http://labs.qt.nokia.com/2011/03/10/qml-components-for-desktop/
[21:38] <donaldcarr_work> which demonstrates higher level native controls for usage from within QML
[21:38] <donaldcarr_work> These are still be actively researched and should mature over the coming months
[21:39] <donaldcarr_work> When QtQuick was first released, it provided very little higher level widget functionality and this caused something of a panic and pigeon holing of the tech as being undesktop like
[21:40] <donaldcarr_work> This was caused by the lag time in actually implementing and releases Components, which clearly could not be implemented in parallel to the core QML language itself
[21:40] <donaldcarr_work> We are having a session on this at this years Qt Developer Days (Munich 24-26th Oct, SF 29Nov-1Dec):
[21:40] <donaldcarr_work> http://qt.nokia.com/qtdevdays2011/qt-technical-sessions#qtquickcomponentsdesktop
[21:41] <donaldcarr_work> Does anyone have any questions of statements to make at this point
[21:41] <donaldcarr_work> ?
[21:41] <donaldcarr_work> If people want to use QML components now
[21:42] <donaldcarr_work> http://labs.qt.nokia.com/2011/07/06/ready-made-ui-building-blocks-at-your-service-qt-quick-components-for-symbian-and-meego-1-2-harmattan/
[21:42] <donaldcarr_work> documents the release of the Symbian component set which have no dependency outside of Qt so you can happily use them outside of Symbian development. (YMMV)
[21:43] <ClassBot> tomalan asked: gtk-applications tend to have a "foreign" look on OSX. How is that with QT?
[21:43] <donaldcarr_work> tomalan: I am going to assume you are asking this question in the QML context
[21:43] <donaldcarr_work> tomalan: Jens provides this screen shot in the comments section: http://labs.qt.nokia.com/wp-content/uploads/2011/03/mac2.png
[21:44] <donaldcarr_work> tomalan: I would assume the controls would look passably macish, although I clearly can't vouch for them until the surrounding work has matured
[21:45] <donaldcarr_work> tomalan: I also cant vouch for the feel (even though the look as clearly quite far along)
[21:45] <donaldcarr_work> tomalan: We are also exposing native theming functionality via other QML constructs
[21:45] <donaldcarr_work> as documented in this blog: http://labs.qt.nokia.com/2011/04/08/mac-toolbars-for-qt-quick/
[21:46] <donaldcarr_work> If you are interested in Qt development
[21:46] <donaldcarr_work> Please subscribe to the Qt developer mailing lists:
[21:46] <donaldcarr_work> http://lists.qt.nokia.com/mailman/listinfo
[21:46] <donaldcarr_work> where flame wars abound
[21:46] <donaldcarr_work> (but they have to be constructive flamewars)
[21:47] <donaldcarr_work> If in dabbling with QML you hit any issues please feel free to address questions to:
[21:47] <donaldcarr_work> Please direct any queries to qt-qml@qt.nokia.com where we can collectively address them.
[21:47] <donaldcarr_work> If there are no further questions
[21:48] <donaldcarr_work> I will show you interesting items from Qt Media Hub
[21:48] <donaldcarr_work> if you do not interject with questions
[21:48] <donaldcarr_work> the way the project is currently structured
[21:48] <donaldcarr_work> all models, model population, file system crawling, threading is done in c++
[21:49] <donaldcarr_work> the only thing the QML model cares about are structures which are explicitly exposed to it
[21:49] <donaldcarr_work> this is done in:
[21:49] <donaldcarr_work> http://gitorious.org/qtmediahub/qtmediahub-core/blobs/master/src/skinruntime.cpp
[21:50] <donaldcarr_work> starting on line 266
[21:50] <donaldcarr_work> You can see we create a property map
[21:50] <donaldcarr_work> and then attach it to the root context
[21:50] <ClassBot> There are 10 minutes remaining in the current session.
[21:51] <donaldcarr_work> this is a clear point where you can see the division of functionality between c++ and QML
[21:51] <donaldcarr_work> basically, media player skins are completely unconstrained other than the fact they have to use the APIs of these common exported contructs
[21:52] <donaldcarr_work> So where as XBMC has a fixed XML layout engine
[21:52] <donaldcarr_work> where the themes are bound to formatting/layout constraints imposed by this intermediate engine
[21:53] <donaldcarr_work> our QML skins can take any liberties extended by QML
[21:53] <donaldcarr_work> We currently have around 5 skins
[21:53] <donaldcarr_work> depending on the targets we are aiming them at: automotive, directfb hardware, GL based devices
[21:54] <donaldcarr_work> Your QML skin changes depending on the acceleration mechanisms available and the input mechanisms
[21:54] <donaldcarr_work> One of the trickiest things about QML (it has to be said) is the focus handling
[21:55] <donaldcarr_work> When using a keyboard to change focus between a hierarchy of controls, especially in a project where multiple people are hacking,  we found we had to adopt a procedual way of switching focus between components
[21:55] <ClassBot> There are 5 minutes remaining in the current session.
[21:56] <donaldcarr_work> We have 5 minutes remaining, does anyone have any questions?
[21:56] <donaldcarr_work> I hope some of you managed to grab the qt-sdk package
[21:56] <donaldcarr_work> and are curious enough to look further into it
[21:57] <donaldcarr_work> If any of you are interested in joining our project feel free to join us at #qtmediahub
[21:58] <donaldcarr_work> for QML related questions you can always hop on #qt-qml
[21:58] <donaldcarr_work> As you would have gathered for Qt 5, QML is the first class citizen, he has the key to the city and hence familiarizing yourself with this technology is very adviseable
[21:59] <donaldcarr_work> I for one am thoroughly convinced that the new model/architecture of Qt (for Qt 5) makes it very plausable for wide usage in the embedded (and desktop) space
[21:59] <donaldcarr_work> I think I am about to get booted
[21:59] <donaldcarr_work> thank you for your time
[21:59] <donaldcarr_work>  I hope that this has been useful
[22:00] <ClassBot> tomalan asked: When will QT5 be available?
[22:00] <donaldcarr_work> tomalan: I can't give explicit dates for clear reasons
[22:00] <ClassBot> Logs for this session will be available at http://irclogs.ubuntu.com/2011/09/09/%23ubuntu-classroom.html
[22:00] <donaldcarr_work> tomalan: I would expect it to be quite useable by the end of the year