Will it Blend? Python Libraries for Desktop Integration - Instructors: conscioususer

   1 [20:00] <ClassBot> Logs for this session will be available at following the conclusion of the session.
   2 [20:01]  * conscioususer clears throat
   3 [20:01] <conscioususer> Hi folks!
   4 [20:01] <conscioususer> My name is Marcelo Hashimoto and I am the developer of Polly, a Twitter client designed for multiple columns of multiple accounts. (
   5 [20:01] <conscioususer> Polly is being written in Python, with the GTK+ toolkit for the graphical interface, and uses many libraries commonly present in Ubuntu applications.
   6 [20:02] <conscioususer> This session is not about Polly itself, but about some of those libraries and their underlying concepts.
   7 [20:02] <conscioususer> In particular, libraries that help you to integrate your application with the desktop.
   8 [20:02] <conscioususer> === DESKTOP INTEGRATION
   9 [20:02] <conscioususer> So what exactly do I mean by "desktop integration"?
  10 [20:03] <conscioususer> Informally, it is simply the noble attitude of "playing nice with others around you". :)
  11 [20:03] <conscioususer> When you develop an application, you must always remember that it will not be used in a completely independent way.
  12 [20:03] <conscioususer> It will be used as part of a big ecosystem.
  13 [20:03] <conscioususer> So it is important to blend well inside this ecosystem, to minimize the amount of different behaviors that the end user needs to learn.
  14 [20:03] <conscioususer> In Ubuntu this means striving for two things:
  15 === bUbu87 is now known as black_puppydog
  16 [20:04] <conscioususer> - consistency between different applications in a desktop environment
  17 [20:04] <conscioususer> - consistency across different desktop environments
  18 [20:04] <conscioususer> Ubuntu, by default, uses the GNOME environment with Unity. But alternatives like KDE and XFCE are one click away in the Software Center.
  19 [20:04] <conscioususer> So you should not forget the users who prefer these alternatives.
  20 [20:04] <conscioususer> 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.
  21 [20:05] <conscioususer> Everything will be clearer when I start giving concrete examples, so let's get on with it. :)
  22 [20:05] <conscioususer> === PRELIMINARIES
  23 [20:05] <conscioususer> Before starting, please download the tarball in
  24 [20:06] <conscioususer> (for those reading the transcript, I'll keep the tarball online, no worries)
  25 [20:06] <conscioususer> This tarball has some images I will reference here, and a text file with references that I will cite with [NUMBER].
  26 [20:06] <conscioususer> Those references are not meant to be read now, only later if you are interested in more information.
  27 [20:06] <conscioususer> 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.
  28 [20:06] <conscioususer> 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)
  29 [20:07] <conscioususer> Commands to be given to the shell will be prefixed by >>>
  30 [20:07] <conscioususer> === OVERVIEW
  31 [20:07] <conscioususer> The session is divided in three parts, each one answering a question that inevitably arises in many applications:
  32 [20:07] <conscioususer> 1 - "How do I send notifications to the user?"
  33 [20:07] <conscioususer> 2 - "Where do I place files read or written by my application?"
  34 [20:08] <conscioususer> 3 - "What do I use to store sensitive information?"
  35 [20:08] <conscioususer> and answering, of course, in a way that strives to blend well with the desktop that the user is currently using.
  36 [20:08] <conscioususer> Like I mentioned before, some of you might be surprised with those topics, because the word "integration" is usually associated with visuals.
  37 [20:08] <conscioususer> 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.
  38 [20:09] <conscioususer> If you open the images (warning: shameless self-promotion coming)
  39 [20:09] <conscioususer> polly-ubuntu-unity.png
  40 [20:09] <conscioususer> polly-ubuntu-shell.png
  41 [20:09] <conscioususer> polly-ubuntu-kubuntu.png
  42 [20:09] <conscioususer> polly-ubuntu-xubuntu.png
  43 [20:09] <conscioususer> you will see Polly visually integrated with four different environments (GNOME+Unity, GNOME-Shell, KDE and XFCE)
  44 [20:10] <conscioususer> I did not write a single line of code that had the specific goal of reaching this visual integration.
  45 [20:10] <conscioususer> Those four environments simply know what to do with GTK applications.
  46 [20:10] <conscioususer> So visuals will not be the main focus.
  47 [20:11] <conscioususer> All that said, the first part does have *some* visual elements involved.
  48 [20:11] <conscioususer> Any questions so far?
  49 [20:12] <conscioususer> Ok, so let's begin!
  50 [20:12] <conscioususer> === PART 1: HOW DO I SEND NOTIFICATIONS TO THE USER?
  51 [20:13] <conscioususer> I'm going to start with a quick hands-on example, and explain the concepts involved later.
  52 === narfnarf is now known as dingens
  53 [20:13] <conscioususer> For this part, you need to have the package gir1.2-notify-0.7 installed.
  54 [20:13] <conscioususer> This package comes in a default Natty/Oneiric install, actually.
  55 [20:13] <conscioususer> If you don't, do "sudo apt-get install gir1.2-notify-0.7"
  56 [20:13] <conscioususer> And those of you who attended Dmitry Shachnev's session yesterday are already familiar with the Notify library I'm going to use.
  57 [20:13] <conscioususer> With the package installed, please open the Python shell and enter:
  58 [20:13] <conscioususer> >>> from gi.repository import Notify
  59 [20:14] <conscioususer> This will load the library we will use, the Python bindings for libnotify.
  60 [20:14] <conscioususer> Before sending a notification, we should identify our application, for logging purposes:
  61 [20:14] <conscioususer> >>> Notify.init('test')
  62 [20:14] <conscioususer> You should've received a "True" in response to this command, meaning that the identification was accepted.
  63 [20:16] <conscioususer>  Now we are ready to build a notification:
  64 [20:16] <conscioususer> >>> notification ='Test', 'Hello World!', 'start-here')
  65 [20:16] <conscioususer> 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.
  66 [20:16] <conscioususer> You can change them at will.
  67 [20:16] <conscioususer> If I'm going too fast, for example if someone is still downloading a dependency, please let me know.
  68 [20:17] <conscioususer> Ok, moving on...
  69 [20:17] <conscioususer> The notification is now built, but it was not sent yet. Before sending it, we can set some details.
  70 [20:17] <conscioususer> For example, we can set the urgency level of this notification:
  71 [20:17] <conscioususer> >>> notification.set_urgency(Notify.Urgency.LOW)
  72 [20:17] <conscioususer> In Ubuntu, non-urgent notifications are not shown when you are seeing a fullscreen video, among other things.
  73 [20:18] <conscioususer> You could also set an arbitrary image to be an icon.
  74 [20:18] <conscioususer> But let's not waste too much time on details. :) If you are ready, then let's pop the notification already:
  75 [20:18] <conscioususer> >>>
  76 [20:18] <conscioususer> So, did you see a notification bubble popping up in your desktop?
  77 [20:18] <conscioususer> This notification is completely consistent with other notifications from Ubuntu, like network connection and instant messages.
  78 [20:19] <conscioususer> Not only on visuals, but also on behavior.
  79 [20:19] <conscioususer> 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!"
  80 [20:19] <conscioususer> And the environment took care of the rest.
  81 [20:19] <conscioususer> You can execute this code in other enviornments, and it will work similarly. See the image
  82 [20:20] <conscioususer> notify.png
  83 [20:20] <conscioususer> 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
  84 [20:21] <conscioususer> 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.
  85 [20:21] <conscioususer> What exactly this library does? Is it a huge pile of "ifs" and "elses", that does different things for each environment?
  86 [20:21] <conscioususer> 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.
  87 [20:21] <conscioususer> No, it's actually much more elegant than that, thanks to the concept of
  88 [20:22] <conscioususer> === SPECIFICATIONS
  89 [20:22] <conscioususer> A specification is basically a set of idioms and protocols, specifically designed to be environment-independent.
  90 [20:22] <conscioususer> In the case of notifications, this means a "common language" that is the only thing that notification senders and notifications receivers need to know.
  91 [20:22] <conscioususer> Specifications represent the foundation of a lot of integration you currently see in your system.
  92 [20:22] <conscioususer> 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.
  93 [20:23] <conscioususer> How this monolithic folder becomes neatly categorized menus in GNOME, KDE, XFCE, etc.?
  94 [20:23] <conscioususer> 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.
  95 [20:23] <conscioususer> Another example
  96 [20:23] <conscioususer> The library libdbusmenu provides a common language through which applications can send menus to each other.
  97 [20:23] <conscioususer> This library is what allows implementing the global menu you see in
  98 [20:23] <conscioususer> polly-ubuntu-unity.png
  99 [20:24] <conscioususer> polly-kubuntu.png
 100 [20:24] <conscioususer> without the need of Qt-specific code or special conditions inside Polly.
 101 [20:24] <conscioususer> A lot of those specifications are written by the community effort in [1], though other sources exist.
 102 [20:24] <conscioususer> If you are curious on knowing more, the specification for notifications used by libnotify can be seen in [4].
 103 [20:24] <conscioususer> libnotify represents an ideal situation for a specification
 104 [20:24] <conscioususer> 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.
 105 [20:25] <conscioususer> the library API is high-level, I mean
 106 [20:25] <conscioususer> It's like that old cliche from martial arts movies.
 107 [20:25] <conscioususer> You know that an specification has been mastered when you don't have to use it. :)
 108 [20:25] <conscioususer> But sometimes it's not so clean, even when a specification exists.
 109 [20:25] <conscioususer> Which brings us to the next topic.
 111 [20:26] <conscioususer> Before I continue, any questions?
 112 [20:26]  * conscioususer waits a bit...
 113 [20:27] <conscioususer> ok, let's move on
 114 === jrgifford_ is now known as jrgifford
 115 [20:27] <conscioususer> 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.
 116 [20:27] <conscioususer> Those can usually be categorized in three types: configuration files, data files, and cache files.
 117 [20:27] <conscioususer> The question is, where should you put them?
 118 [20:27] <conscioususer> A lot of applications simply create a .APPNAME folder in the user's home, but that's usually considered bad practice.
 119 [20:27] <conscioususer> First, because it clutters the home folder.
 120 [20:28] <conscioususer> Second, because separating files by type first can be more useful.
 121 [20:28] <conscioususer> 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.
 122 [20:28] <conscioususer> Also, file indexers can be programmed to ignore the entire folder if they want.
 123 [20:28] <conscioususer> But of course, you can only avoid this if environments follow the same guidelines for where placing those types.
 124 [20:29] <conscioususer> I guess you know the direction I'm going, right? :)
 125 [20:29] <conscioususer> Base Directory Specification [5]
 126 [20:29] <conscioususer> This specification establishes the environment variables that define where files of a certain type should be placed.
 127 [20:29] <conscioususer> You can check them right now.
 128 [20:29] <conscioususer> In the Python interpreter enter
 129 [20:29] <conscioususer> >>> import os
 130 [20:29] <conscioususer> >>> print os.environ['XDG_DATA_DIRS']
 131 [20:30] <conscioususer> This will print the content of the XDG_DATA_DIRS variable, which is a list of paths separated by colons.
 132 [20:30] <conscioususer> This list contains the paths where data files are expected to be, in order of priority.
 133 [20:31] <conscioususer> you can also try 'XDG_DATA_HOME', for example
 134 [20:31] <conscioususer> which is the path for the specific user
 135 [20:31] <conscioususer> Now, parsing this string is not particularly difficult, but it is annoying to reinvent this wheel for every application you write.
 136 [20:31] <conscioususer> So instead, you can use the PyXDG library.
 137 [20:31] <conscioususer> It also comes by default in Natty/Oneiric.
 138 [20:31] <conscioususer> If you don't have it, just do "sudo apt-get install python-xdg"
 139 [20:32] <conscioususer> Now, in the Python interpreter, enter
 140 [20:32] <conscioususer> >>> from xdg import BaseDirectory
 141 [20:32] <conscioususer> The BaseDirectory class takes care of all reading and parsing of the spec environment variables for you.
 142 [20:32] <conscioususer> For example, all you need to do to access data paths is to use the variable
 143 [20:33] <conscioususer> >>> BaseDirectory.xdg_data_dirs
 144 [20:33] <conscioususer> which has all paths in a neat Python list, ready to be used.
 145 [20:33] <conscioususer> If you want to know more details about using this library, I recommend you to read [5] and also entering in the interpreter
 146 [20:33] <conscioususer> >>> help(BaseDirectory)
 147 [20:33] <conscioususer> (type 'q' to leave help mode)
 148 [20:34] <conscioususer> 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.
 149 [20:34] <conscioususer> The reason is simple: what is done once you know the paths is highly application-dependent.
 150 [20:34] <conscioususer> Some use data folders to store icons, others to store databases.
 151 [20:34] <conscioususer> Some applications don't use caching at all.
 152 [20:34] <conscioususer> And so on.
 153 [20:34] <conscioususer> So higher-level interfaces wouldn't really help much.
 154 [20:34] <conscioususer> But the specification itself is very short and easy to understand.
 155 [20:35] <conscioususer> And the integration is worth the effort.
 156 [20:35] <conscioususer> Are there any questions about the usage of python-xdg?
 157 [20:35] <conscioususer> I can wait a bit. :)
 158 [20:36] <conscioususer> Ok
 159 [20:36] <conscioususer> Time to wrap part 2
 160 [20:36] <conscioususer> The only storage that the Base Directory specification does not cover is the storage of sensitive information.
 161 [20:37] <conscioususer> Which brings us to the third part.
 162 [20:37] <conscioususer> === WHAT DO I USE TO STORE SENSITIVE INFORMATION?
 163 [20:37] <conscioususer> If you application stores sensitive info like passwords, it is usually considered a security flaw to store them in plain text.
 164 [20:37] <conscioususer> (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)
 165 [20:37] <conscioususer> That's why desktop environments provide keyrings, which are encrypted storages unlocked by a master password or at login.
 166 [20:37] <conscioususer> For example,
 167 [20:37] <conscioususer> GNOME has GNOME-Keyring, while KDE has KWallet.
 168 [20:38] <conscioususer> G-K is widely used by GNOME apps, like Empathy, Evolution, networkmanager...
 169 [20:38] <conscioususer> But now here comes the bad news
 170 [20:38] <conscioususer> For the moment, there are no specifications on storage for sensitive information.
 171 [20:38] <conscioususer> has a draft, but is still in progress [6]
 172 [20:38] <conscioususer> So these two keyrings use different idioms.
 173 [20:38] <conscioususer> which is a very bad thing for developers who want cross-environment applications.
 174 [20:40] <conscioususer> Usually, the only way to ensure cross-environment in this case is implementing directly in your code
 175 [20:41] <conscioususer> And now here comes the good news: the python-keyring library
 176 [20:41] <conscioususer> Basically, an awesome developer has bad that for you!
 177 [20:41] <conscioususer> (sudo apt-get install python-keyring)
 178 [20:41] <conscioususer> (this one does *not* come in a default Ubuntu install)
 179 [20:42] <conscioususer> This library does all the dirty work of finding out which keyring should be used according to the environment you are on.
 180 [20:42] <conscioususer> It supports GNOME-Keyring, KWallet and also Windows and OSX keyrings (though I never tested it for those last two)
 181 [20:42] <conscioususer> And wraps this in a surprisingly elegant API.
 182 [20:42] <conscioususer> Really
 183 [20:42] <conscioususer> Let's go back to the interpreter
 184 [20:43] <conscioususer> Did you all install python-keyring already?
 185 [20:45] <conscioususer> Ops
 186 [20:46] <conscioususer> before I continue, I should make an observation
 187 [20:46] <conscioususer> it seems that XDG_DATA_HOME is not found in os.environ
 188 [20:47] <conscioususer> strangely, its entry works in xdg.BaseDirectory and it is on the spec
 189 [20:47] <conscioususer> I'll investigate this later, sorry about that
 190 [20:47] <conscioususer> Anyway
 191 [20:47] <conscioususer> Let's go back to the interpreter and load the library:
 192 [20:47] <conscioususer> >>> import keyring
 193 [20:48] <conscioususer> Now let's store a dummy password in the keyring
 194 [20:48] <conscioususer> >>> keyring.set_password('appname', 'myusername', 'mypassword')
 195 [20:48] <conscioususer> (I think the strings are all self-explanatory)
 196 [20:48] <conscioususer> 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
 197 [20:48] <conscioususer> It is labeled with the generic name of 'network password' (IIRC next installments of python-keyring will try to use a better naming scheme)
 198 [20:49] <conscioususer> Did you find it? :)
 199 [20:49] <conscioususer> Now let's retrieve it
 200 [20:50] <conscioususer> >>> print keyring.get_password('appname', 'myusername')
 201 [20:50] <conscioususer> Yep.
 202 [20:50] <conscioususer> It's *that* simple.
 203 [20:50] <ClassBot> There are 10 minutes remaining in the current session.
 204 [20:51] <conscioususer> That's not really much to talk about the library really, it is specifically designed to be easy to talk about. :)
 205 [20:51] <conscioususer> Of course, you lose some particular flexibility from GNOME-Keyring or KWallet, but for most applications those wouldn't be used.
 206 [20:52] <conscioususer> For simple account storage, python-keyring suffices and only occupies a couple of lines of code in your app.
 207 [20:52] <conscioususer> It's really convenient
 208 [20:52] <conscioususer> Well, I think it's time for me to wrap up.
 209 [20:52] <conscioususer> Hopefully this session was helpful for you to make the first steps into integrating your app in Ubuntu transparently
 210 [20:52] <conscioususer> Or, better saying, playing nice with others around you. :)
 211 [20:52] <conscioususer> Are there any questions?
 212 [20:53] <conscioususer> On the XDG_DATA_HOME issue
 213 [20:53] <conscioususer> I guess this precisely why using python-xdg is convenient. :)
 214 [20:53] <conscioususer> It works around this kind of problem.
 215 [20:55] <conscioususer> You can play with the files I gave in the tarball, and modify them to experiment with the libraries.
 216 [20:55] <conscioususer> 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.
 217 [20:55] <ClassBot> There are 5 minutes remaining in the current session.
 218 [20:57] <conscioususer> Ok, I guess that's pretty much it. :)
 219 [20:57] <conscioususer> Thank you very much for listening, and for attending today's sessions.
 220 [20:58] <conscioususer> Hope we have a nice last appdev day tomorrow. :)

MeetingLogs/appdevweek1109/PythonLibsDesktopIntegration (last edited 2011-09-09 18:32:38 by dpm)