Wednesday 30 June 2010

Session Managers and PyQt Applications

X11 window managers commonly have an option to "remember" applications which are running when a session ends.

Here's the gnome-checkbox (System -> Preferences -> Startup Applications)

but for this to work, the applications themselves need to comply. Qt does this with the QSessionManager class.

The class is not used directly, but is passed as an argument into functions of qApp, namely the saveState and commitData functions.
qApp therefore needs to be subclassed, and these two functions overwritten.

here's a working example. Note the simplicity of the functions!

#! /usr/bin/env python

from PyQt4 import QtGui, QtCore
import sys

class RestorableApplication(QtGui.QApplication):
    def __init__(self):
        super(RestorableApplication, self).__init__(sys.argv)

    def commitData(self, session):
        '''re-implement this method, called on quit'''

    def saveState(self, session):
        '''re-implement this method, called on run'''

app = RestorableApplication()

mw = QtGui.QMainWindow()
mw.setWindowTitle("I live on after a logout!")

label = QtGui.QLabel(
'''Leave me running and log off
I will survive! (on X11 systems)
Window size and position will be restored
... cool eh?''', mw)




Tuesday 22 June 2010


Making the pluggable pyqt app has been more fun than I thought, and I believe the application may actually turn out to be half decent contacts manager.

it accepts plugins now (a zipped folder, with a config file within) and I am still finialising the plugin API, but I am certain I am on the right lines. The "zipimport" module did all that I hoped for and more.

It's running great on linux and windows. Plugins can simply be dropped into the applications plugins folder which reside here.
linux = ~/.qt-o-fax/plugins
windows = C:\Documents and Settings\USER\.qt-o-fax\
Config file is simple format, heavily influenced by the gedit method of plugin config
[qt-o-fax Plugin]
# module is where the entry point of the plugin is
# it should contain a class Plugin, with a method run()
# for this example, it is


Name=Hello World
Name[fr]=bonjour toulemande
Description=The simplest possible plugin
Description[fr]=Un plugin moins sofisticate

Authors=Neil Wallace 
Copyright=Copyright © 2010 Neil Wallace


Hello World Plugin

This plugin simply displays a hello world message when activated.
It connects to no signals, nor alters the database.

So what plugins could be created?? I think the sky is the limit. Add custom fields to the database, send SMS messages, create word processor documents with addresses embedded, christmas card lists.... contact sharing..

I've stuck it up on launchpad


Wednesday 16 June 2010

Making a pyqt4 application which accepts "plugins".

So I decided that openmolar needed to be broken up into bits. I did this last week, Client, Server and language packs are now seperate packages, meaning that I can update one part of it independently of the other bits. All well and good.

However, I REALLY want "plugins" (similar to firefox functionality) to provide some of the functionality that folks are going to want (partly because it will make it possible for them to code such things themselves)

So, as ever, 1st stage is to make a trivial app, and see if I can get some form of plugin system going.

Here's a video of that application (In making it, I learned a lot about QToolbar class... something I haven't used much before).
(if you have an HTML5 compatible browser, that should appear by magic below.. simply hit play)

If you want to see the code for this little application, I stuck it on launchpad - get it this way.

~$ bzr branch lp:~rowinggolfer/+junk/pluggable-pyqt-app

the vid was taken with the files at revision 1.

Now for the difficult bit.. designing and loading the plugins.

My idea is that plugins should be zipped folders. Signed by the author ideally, and containing a config file which describes it, gives version numbers, and installation instructions which the parent app can use.

Python can import modules from zipped folders easily thanks to the zipimport module.

I think I am on the right lines for this... I'll let you know (via this blog) how I get on