== Will it Blend? Python Libraries for Desktop Integration - Instructors: conscioususer == {{{#!irc [20:00] Logs for this session will be available at http://irclogs.ubuntu.com/2011/09/08/%23ubuntu-classroom.html following the conclusion of the session. [20:01] * conscioususer clears throat [20:01] Hi folks! [20:01] My name is Marcelo Hashimoto and I am the developer of Polly, a Twitter client designed for multiple columns of multiple accounts. (https://launchpad.net/polly) [20:01] Polly is being written in Python, with the GTK+ toolkit for the graphical interface, and uses many libraries commonly present in Ubuntu applications. [20:02] This session is not about Polly itself, but about some of those libraries and their underlying concepts. [20:02] In particular, libraries that help you to integrate your application with the desktop. [20:02] === DESKTOP INTEGRATION [20:02] So what exactly do I mean by "desktop integration"? [20:03] Informally, it is simply the noble attitude of "playing nice with others around you". :) [20:03] When you develop an application, you must always remember that it will not be used in a completely independent way. [20:03] It will be used as part of a big ecosystem. [20:03] So it is important to blend well inside this ecosystem, to minimize the amount of different behaviors that the end user needs to learn. [20:03] In Ubuntu this means striving for two things: === bUbu87 is now known as black_puppydog [20:04] - consistency between different applications in a desktop environment [20:04] - consistency across different desktop environments [20:04] Ubuntu, by default, uses the GNOME environment with Unity. But alternatives like KDE and XFCE are one click away in the Software Center. [20:04] So you should not forget the users who prefer these alternatives. [20:04] And it's important to emphasize that, when I talk about consistency, I'm *not* only talking about visuals! In fact, visuals are only a small part of this presentation. [20:05] Everything will be clearer when I start giving concrete examples, so let's get on with it. :) [20:05] === PRELIMINARIES [20:05] Before starting, please download the tarball in http://ubuntuone.com/p/1Gzy/ [20:06] (for those reading the transcript, I'll keep the tarball online, no worries) [20:06] This tarball has some images I will reference here, and a text file with references that I will cite with [NUMBER]. [20:06] Those references are not meant to be read now, only later if you are interested in more information. [20:06] There are also some Python files that will not be used directly, but are there for you to play and modify as you want after the session. [20:06] The hands-on during the session will be on the Python interactive shell (simply execute "python" in the terminal and you will be on it) [20:07] Commands to be given to the shell will be prefixed by >>> [20:07] === OVERVIEW [20:07] The session is divided in three parts, each one answering a question that inevitably arises in many applications: [20:07] 1 - "How do I send notifications to the user?" [20:07] 2 - "Where do I place files read or written by my application?" [20:08] 3 - "What do I use to store sensitive information?" [20:08] and answering, of course, in a way that strives to blend well with the desktop that the user is currently using. [20:08] Like I mentioned before, some of you might be surprised with those topics, because the word "integration" is usually associated with visuals. [20:08] But the truth is, if you use one of the most widely used toolkits, like Qt and GTK, visuals are almost a non-issue nowadays thanks to the efforts of the developers of those toolkits. [20:09] If you open the images (warning: shameless self-promotion coming) [20:09] polly-ubuntu-unity.png [20:09] polly-ubuntu-shell.png [20:09] polly-ubuntu-kubuntu.png [20:09] polly-ubuntu-xubuntu.png [20:09] you will see Polly visually integrated with four different environments (GNOME+Unity, GNOME-Shell, KDE and XFCE) [20:10] I did not write a single line of code that had the specific goal of reaching this visual integration. [20:10] Those four environments simply know what to do with GTK applications. [20:10] So visuals will not be the main focus. [20:11] All that said, the first part does have *some* visual elements involved. [20:11] Any questions so far? [20:12] Ok, so let's begin! [20:12] === PART 1: HOW DO I SEND NOTIFICATIONS TO THE USER? [20:13] I'm going to start with a quick hands-on example, and explain the concepts involved later. === narfnarf is now known as dingens [20:13] For this part, you need to have the package gir1.2-notify-0.7 installed. [20:13] This package comes in a default Natty/Oneiric install, actually. [20:13] If you don't, do "sudo apt-get install gir1.2-notify-0.7" [20:13] And those of you who attended Dmitry Shachnev's session yesterday are already familiar with the Notify library I'm going to use. [20:13] With the package installed, please open the Python shell and enter: [20:13] >>> from gi.repository import Notify [20:14] This will load the library we will use, the Python bindings for libnotify. [20:14] Before sending a notification, we should identify our application, for logging purposes: [20:14] >>> Notify.init('test') [20:14] You should've received a "True" in response to this command, meaning that the identification was accepted. [20:16] Now we are ready to build a notification: [20:16] >>> notification = Notify.Notification.new('Test', 'Hello World!', 'start-here') [20:16] The first parameter is the title of the notification, the second is the body text, and the third is the name of the icon you want the notification to use. [20:16] You can change them at will. [20:16] If I'm going too fast, for example if someone is still downloading a dependency, please let me know. [20:17] Ok, moving on... [20:17] The notification is now built, but it was not sent yet. Before sending it, we can set some details. [20:17] For example, we can set the urgency level of this notification: [20:17] >>> notification.set_urgency(Notify.Urgency.LOW) [20:17] In Ubuntu, non-urgent notifications are not shown when you are seeing a fullscreen video, among other things. [20:18] You could also set an arbitrary image to be an icon. [20:18] But let's not waste too much time on details. :) If you are ready, then let's pop the notification already: [20:18] >>> notification.show() [20:18] So, did you see a notification bubble popping up in your desktop? [20:18] This notification is completely consistent with other notifications from Ubuntu, like network connection and instant messages. [20:19] Not only on visuals, but also on behavior. [20:19] You didn't have to explicitly code this consistency, all the code did was say "hey, desktop environment, whichever you are, please show this notification here!" [20:19] And the environment took care of the rest. [20:19] You can execute this code in other enviornments, and it will work similarly. See the image [20:20] notify.png [20:20] It will obey the guidelines of those environments. For example, in XFCE you can click to close, while in Ubuntu+Unity you can't by design [20:21] Now that we are warmed up, I will dive a little bit into a very important question that is under the hood of what we just did. [20:21] What exactly this library does? Is it a huge pile of "ifs" and "elses", that does different things for each environment? [20:21] Thank goodness no, because that would mean the library depends on core libraries of all those environments, greatly increasing the dependencies of your app if you wanted to use it. [20:21] No, it's actually much more elegant than that, thanks to the concept of [20:22] === SPECIFICATIONS [20:22] A specification is basically a set of idioms and protocols, specifically designed to be environment-independent. [20:22] In the case of notifications, this means a "common language" that is the only thing that notification senders and notifications receivers need to know. [20:22] Specifications represent the foundation of a lot of integration you currently see in your system. [20:22] For example, if you peek /usr/share/applications in your system, you will see a monolothic folder with .desktop files for all the applications installed. [20:23] How this monolithic folder becomes neatly categorized menus in GNOME, KDE, XFCE, etc.? [20:23] It's thanks to the Desktop Entry Specification [2] and Desktop Menu Specification [3] that specify how .desktop files have to be written, and their contents mean wrt categorization. [20:23] Another example [20:23] The library libdbusmenu provides a common language through which applications can send menus to each other. [20:23] This library is what allows implementing the global menu you see in [20:23] polly-ubuntu-unity.png [20:24] polly-kubuntu.png [20:24] without the need of Qt-specific code or special conditions inside Polly. [20:24] A lot of those specifications are written by the community effort in freedesktop.org [1], though other sources exist. [20:24] If you are curious on knowing more, the specification for notifications used by libnotify can be seen in [4]. [20:24] libnotify represents an ideal situation for a specification [20:24] It has been adopted by the most popular environments and is so high-level that app developers don't even need to know that the specification exists. [20:25] the library API is high-level, I mean [20:25] It's like that old cliche from martial arts movies. [20:25] You know that an specification has been mastered when you don't have to use it. :) [20:25] But sometimes it's not so clean, even when a specification exists. [20:25] Which brings us to the next topic. [20:26] === PART 2: WHERE DO I PLACE FILES READ OR WRITTEN BY MY APPLICATION? [20:26] Before I continue, any questions? [20:26] * conscioususer waits a bit... [20:27] ok, let's move on === jrgifford_ is now known as jrgifford [20:27] When your application starts to become a little more complex than helloworlding, it is highly possible that at some point you will need to read and write files. [20:27] Those can usually be categorized in three types: configuration files, data files, and cache files. [20:27] The question is, where should you put them? [20:27] A lot of applications simply create a .APPNAME folder in the user's home, but that's usually considered bad practice. [20:27] First, because it clutters the home folder. [20:28] Second, because separating files by type first can be more useful. [20:28] For example, if all cache files of all applications are in the same folder, desktop cleaners know they can safely delete this folder for a full app-independent cache cleanup. [20:28] Also, file indexers can be programmed to ignore the entire folder if they want. [20:28] But of course, you can only avoid this if environments follow the same guidelines for where placing those types. [20:29] I guess you know the direction I'm going, right? :) [20:29] Base Directory Specification [5] [20:29] This specification establishes the environment variables that define where files of a certain type should be placed. [20:29] You can check them right now. [20:29] In the Python interpreter enter [20:29] >>> import os [20:29] >>> print os.environ['XDG_DATA_DIRS'] [20:30] This will print the content of the XDG_DATA_DIRS variable, which is a list of paths separated by colons. [20:30] This list contains the paths where data files are expected to be, in order of priority. [20:31] you can also try 'XDG_DATA_HOME', for example [20:31] which is the path for the specific user [20:31] Now, parsing this string is not particularly difficult, but it is annoying to reinvent this wheel for every application you write. [20:31] So instead, you can use the PyXDG library. [20:31] It also comes by default in Natty/Oneiric. [20:31] If you don't have it, just do "sudo apt-get install python-xdg" [20:32] Now, in the Python interpreter, enter [20:32] >>> from xdg import BaseDirectory [20:32] The BaseDirectory class takes care of all reading and parsing of the spec environment variables for you. [20:32] For example, all you need to do to access data paths is to use the variable [20:33] >>> BaseDirectory.xdg_data_dirs [20:33] which has all paths in a neat Python list, ready to be used. [20:33] If you want to know more details about using this library, I recommend you to read [5] and also entering in the interpreter [20:33] >>> help(BaseDirectory) [20:33] (type 'q' to leave help mode) [20:34] As you can see, in this case the app developer is much closer to the metal than on the notifications case. He actually needs to know some details about the specification. [20:34] The reason is simple: what is done once you know the paths is highly application-dependent. [20:34] Some use data folders to store icons, others to store databases. [20:34] Some applications don't use caching at all. [20:34] And so on. [20:34] So higher-level interfaces wouldn't really help much. [20:34] But the specification itself is very short and easy to understand. [20:35] And the integration is worth the effort. [20:35] Are there any questions about the usage of python-xdg? [20:35] I can wait a bit. :) [20:36] Ok [20:36] Time to wrap part 2 [20:36] The only storage that the Base Directory specification does not cover is the storage of sensitive information. [20:37] Which brings us to the third part. [20:37] === WHAT DO I USE TO STORE SENSITIVE INFORMATION? [20:37] If you application stores sensitive info like passwords, it is usually considered a security flaw to store them in plain text. [20:37] (I say "usually" because it's not that much of a big deal if you live alone and use a computer that is never connected to the internet, for example) [20:37] That's why desktop environments provide keyrings, which are encrypted storages unlocked by a master password or at login. [20:37] For example, [20:37] GNOME has GNOME-Keyring, while KDE has KWallet. [20:38] G-K is widely used by GNOME apps, like Empathy, Evolution, networkmanager... [20:38] But now here comes the bad news [20:38] For the moment, there are no specifications on storage for sensitive information. [20:38] freedesktop.org has a draft, but is still in progress [6] [20:38] So these two keyrings use different idioms. [20:38] which is a very bad thing for developers who want cross-environment applications. [20:40] Usually, the only way to ensure cross-environment in this case is implementing directly in your code [20:41] And now here comes the good news: the python-keyring library [20:41] Basically, an awesome developer has bad that for you! [20:41] (sudo apt-get install python-keyring) [20:41] (this one does *not* come in a default Ubuntu install) [20:42] This library does all the dirty work of finding out which keyring should be used according to the environment you are on. [20:42] It supports GNOME-Keyring, KWallet and also Windows and OSX keyrings (though I never tested it for those last two) [20:42] And wraps this in a surprisingly elegant API. [20:42] Really [20:42] Let's go back to the interpreter [20:43] Did you all install python-keyring already? [20:45] Ops [20:46] before I continue, I should make an observation [20:46] it seems that XDG_DATA_HOME is not found in os.environ [20:47] strangely, its entry works in xdg.BaseDirectory and it is on the spec [20:47] I'll investigate this later, sorry about that [20:47] Anyway [20:47] Let's go back to the interpreter and load the library: [20:47] >>> import keyring [20:48] Now let's store a dummy password in the keyring [20:48] >>> keyring.set_password('appname', 'myusername', 'mypassword') [20:48] (I think the strings are all self-explanatory) [20:48] If you are using GNOME, you can see the password stored in Seahorse, type "seahorse" in the terminal or look in the applications for System > Passwords & Encryption [20:48] It is labeled with the generic name of 'network password' (IIRC next installments of python-keyring will try to use a better naming scheme) [20:49] Did you find it? :) [20:49] Now let's retrieve it [20:50] >>> print keyring.get_password('appname', 'myusername') [20:50] Yep. [20:50] It's *that* simple. [20:50] There are 10 minutes remaining in the current session. [20:51] That's not really much to talk about the library really, it is specifically designed to be easy to talk about. :) [20:51] Of course, you lose some particular flexibility from GNOME-Keyring or KWallet, but for most applications those wouldn't be used. [20:52] For simple account storage, python-keyring suffices and only occupies a couple of lines of code in your app. [20:52] It's really convenient [20:52] Well, I think it's time for me to wrap up. [20:52] Hopefully this session was helpful for you to make the first steps into integrating your app in Ubuntu transparently [20:52] Or, better saying, playing nice with others around you. :) [20:52] Are there any questions? [20:53] On the XDG_DATA_HOME issue [20:53] I guess this precisely why using python-xdg is convenient. :) [20:53] It works around this kind of problem. [20:55] You can play with the files I gave in the tarball, and modify them to experiment with the libraries. [20:55] I purposefully chose some libraries with technically simple APIs, as I wanted to dedicate part of this session to talk about the concept of specifications themselves. [20:55] There are 5 minutes remaining in the current session. [20:57] Ok, I guess that's pretty much it. :) [20:57] Thank you very much for listening, and for attending today's sessions. [20:58] Hope we have a nice last appdev day tomorrow. :) }}}