Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

Tuesday, January 24, 2012

Coding: Aligning Widgets with GtkCheckButton Labels

As I mentioned in the previous post, I think I found a way to accurately align widgets with GtkCheckButton labels, though I have no idea if it's the easier way.


Basically, these are the GtkCheckButton style properties that define the left distance:


So all you need to do is set the margin-left of child widgets to:

focus-line-width + focus-padding + indicator-size + 3 * indicator-spacing

If you want to adapt immediately when the GTK theme changes, then you also need to connect to the style-updated signal appropriately. Here is a custom widget that gets the job done.

from gi.repository import GObject, Gtk

class CheckGroup(Gtk.VBox):
    def _on_checkbutton_style_updated(self, widget):
        value = GObject.Value()
        value.init(GObject.TYPE_INT)

        widget.style_get_property('focus-line-width', value)
        margin = value.get_int()

        widget.style_get_property('focus-padding', value)
        margin += value.get_int()

        widget.style_get_property('indicator-size', value)
        margin += value.get_int()

        widget.style_get_property('indicator-spacing', value)
        margin += 3 * value.get_int()

        self.group.set_margin_left(margin)

    def __init__(self, checkbutton):
        checkbutton.connect('style-updated', self._on_checkbutton_style_updated)

        self.group = Gtk.VBox(homogeneous=False, spacing=6)
        self.group.show()

        super(CheckGroup, self).__init__(homogeneous=False, spacing=6)
        self.pack_start(checkbutton, False, False, 0)
        self.pack_start(self.group, False, False, 0)
        self.show()

Sunday, January 8, 2012

Heads Up: Drag 'n' Drop bug with PyGObject 3.0.0 (Ubuntu 11.10)

The GTK handling of drag 'n' drop looks a little bit convoluted at first, but can be quite handy once you get the hang of it. Unfortunately, there is a bug in PyGObject versions below 3.0.3 that makes things a little bit harder than they should.

Long story short, when using PyGObject in Ubuntu 11.10 Oneiric Ocelot, you can't pass detailed parameters to widget.drag_dest_set and expect GTK to take care of everything. You need to handle the drag-motion, drag-leave, and drag-drop signals yourself.

Heads Up: Missing Pango text scale constants in PyGObject 3.0 (Ubuntu 11.10)

If you are using PyGI in Ubuntu 11.10 Oneiric Ocelot and desperately trying to find the introspected versions of the Pango text scale constants, don't bother. I have confirmed on the development IRC channel that those constants are not introspected yet.

Since those are all #defines, I suggest simply using the values directly until this is fixed upstream. It's not like those will change any time soon...

Saturday, January 7, 2012

Coding: Python proxy support, thread-safety, and SocksiPy

Depending on the task, HTTP requests in Polly can be made by four different modules: pycurl, python-httplib2, python-oauth2, and urllib. The first has built-in proxy support and to the others I add proxy support with a little bit of monkey-patching: I replace the standard socket.socket class by a proxy-aware subclass from the SocksiPy module.

This is massively thread-unsafe, of course, because there are different threads creating instances of socket.socket all the time. This issue is present in the current version of Polly because I admittedly implemented proxy support in a little bit of a rush. I decided to get rid of it now, during the rewritings and refactorings I'm doing for the GTK3 port.

Now all HTTP requests are wrapped by methods of a proxy singleton, responsible for ensuring thread-safety. This is done via a reader/writer lock instead of a simple lock to allow multiple requests simultaneously. I also use this wrapping to replace the plethora of different exceptions given by the different modules by simpler Polly exceptions.

At least for now, I'm happy with this approach. One of the things I like about it is that the syntax makes sense. Now instead of things like

    http_client = httplib2.Http()
    http_client.request(url)

I have things like

    proxy.http_request(url)

which makes syntactically clear that the request is being handled, and possibly rerouted, by a proxy manager.

Thoughts, everyone? Simpler alternatives?

Friday, January 6, 2012

Coding: Python, GNOME-Keyring, and GObject Introspection


Nowadays, the fragmentation of the free desktop is a much smaller issue for developers than it was some time ago. With specifications like XDG Base Directory and efforts like QGtkStyle, creating an application that does not behave or look alien in most big players like GNOME and KDE is relatively simple.

But when developing Polly, I found out that there was one field where fragmentation is still a major problem: storage of sensitive information. GNOME has GNOME-Keyring, KDE has KWallet, and those two don't speak the same language at all. Luckily, I came across a lovely library called python-keyring that takes care of all the dirty work by automatically detecting the environment and choosing a proper keyring. I even talked about it in my Ubuntu App Developer Week session

This approach is not without its problems, though. The library falls back to its own encrypting implementation when it does not detect any supported keyring, and requires the user to type an unlocking password via terminal in this case. This is obviously an usability fail for any user that does not use GNOME or KDE.

Furthermore, the library depends on the static Python bindings to libgnome-keyring, which in turn depend on the static bindings to GObject. This conflicts directly with my current work of porting Polly to GTK3 and therefore PyGI, because static and introspected bindings cannot be used together. Some googling revealed that the developers were aware of the issue but could not do much because libgnome-keyring is hard to introspect.

I really didn't want to postpone the GTK3 porting by another cycle, so my frustration grew to a point where I actually asked Matthew Paul Thomas whether requiring to type a password on every start would be a good idea. My idea was basically writing a graphical interface for the python-keyring fallback. His answer was no. Among other followers, it was more diverse: it varied between no, no, and no. Despite its overwhelming popularity, I decided to ditch the idea. After Lars offered me support on IRC, I started to consider the risky route of trying to introspect libgnome-keyring.

There was light at the end of the tunnel, though: for a long time now, GNOME-Keyring and KWallet developers have been drafting a DBus-based Secret Service specification. I didn't pay much attention to it first because it isn't final, but then I learned that the GNOME-Keyring daemon is already being backed by it. This was perfect: I could use DBus directly, bypassing the need for libgnome-keyring bindings.

Only one problem, though: I used to rely on dbus-python for DBus work, and this library had too the problem of depending on deprecated bindings. So I realized I had to face the introspected GDBus bindings and cringed at the non-pythonic syntax that was expecting me.

And then, after some more googling... a wild Martin Pitt appeared! Thanks to Martin, my GDBus experience in Python has been much more pleasant than I expected. That was really the last piece of the puzzle. Now I could access GNOME-Keyring via the DBus Secret Service API elegantly, and without ever leaving GObject Introspection.

I still have to support KWallet for a while, until KDE's migration to KSecretService is completed. But since the Qt4/KDE4 bindings do not cause any conflicts, this is no biggie. The main code is now as simple as

    if FDOKeyring.supported():
        keyring = FDOKeyring(UID)
    elif KDEKeyring.supported():
        keyring = KDEKeyring(UID)
    else:
        exit('No supported keyring seems to be accessible.')


Followed by occasional calls of

    keyring.get_password(username) 
    keyring.set_password(username, password, callback)

and so on. What was particularly nice about this whole experience was not only how much I learned, but also the warm and fuzzy feeling that I'm really standing on the shoulders of a lot of giants. Kudos to everyone involved in the libraries.