Dropping things on app icons with Qt

I’ve spent the last year or so attempting to improve the users experience of the application I spend my (working) life developing and maintaining. This has involved many new and exciting changes, but the biggest one is the ability to just drop data and files onto the applications icon and windows rather than using dialogs to locate and import things. To do this with a Mac OSX application developed with Qt requires two steps:

  1. Subclass QApplication and reimplement the event() method
  2. Update the applications Info.plist to register associated file types

So, step 1 is relatively simple:

application.h

#ifndef APPLICATION_H
#define APPLICATION_H

#include <QApplication>

class Application : public QApplication
{
    Q_OBJECT
public:
    Application(int &argc, char** argv);
    ~Application();
protected:
    bool event(QEvent *);
private:
    void loadFile(const QString &fileName);

};

#endif // APPLICATION_H

application.cpp

#include "application.h"

#include <QFileOpenEvent>
#include <QDebug>

Application::Application(int &argc, char **argv) : QApplication(argc,argv){}
Application::~Application(){}

bool Application::event(QEvent *event){
    switch(event->type()){
    case QEvent::FileOpen:
        loadFile(static_cast<QFileOpenEvent *>(event)->file());
        return true;
    default:
        return QApplication::event(event);
    }
}

void Application::loadFile(const QString &fileName){
    qDebug() << "File Name:" << fileName;
}

You can then use this in place of QApplication, obviously after doing something more useful in the loadFile() method.

Step 2 simply involves inserting the following somewhere in your Info.plist file:

...
    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeExtensions</key>
            <array>
                <string>txt</string>
            </array>
            <key>CFBundleTypeIconFile</key>
            <string>application.icns</string>
            <key>CFBundleTypeMIMETypes</key>
            <array>
                <string>text/plain</string>
            </array>
            <key>CFBundleTypeName</key>
            <string>Text</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSIsAppleDefaultForType</key>
            <false/>
        </dict>
    </array>
...

And you’re done! To automatically update the Info.plist file after the application has built rather than (forgetting to) update by hand, see my previous post about running scripts from Qt project files.

Running a script post Qt build

Just sometimes you want/need to run a script after building your application to modify the output in some way (copy resources into a bundle for example). Qt of course has a way of doing this which I just discovered. I need to modify a Mac OSX bundle’s Info.plist file after compilation (I’ll explain why in my next post) and if I don’t automate it, I’ll just forget and spend hours trying to work out what I’ve (not) done! So, add the following to your Qt project file:

QMAKE_POST_LINK += $$quote($${PWD}/fix_plist.sh $${OUT_PWD} $${TARGET})

All fields that start with $$ are macros that Qt will replace with the relevant information. The $$quote bit just makes sure the command is appropriately quoted for the target OS. $${PWD} is the projects working directory where the application’s source (and my script fix_plist.sh lives), $${OUT_PWD} is the build output directory where the output bundle will live and $${TARGET} is the name of the application. The last two are passed to my script as command line arguments so I can find the app bundle and thus its Info.plist by relative path.

Python Enums as Flags

I’m a big fan of enumerated types. As someone who started there HLL programming life with C++ (my first love 🙂 ) I’ve always enjoyed how we can safely limit a variable to a range of known, named values. No more magic numbers! Also, particularly as a Qt devotee, I love how we an use enumerations like flags and create masks from them which we can then test using bitwise operators. A good example is Qt’s Alignment flags for positioning elements on screen, which might be implemented and used as follows:

#include <iostream>
using namespace std;

namespace Qt {
    enum Alignment {
        AlignLeft     = 0x0001,
        AlignRight    = 0x0002,
        AlignHCenter  = 0x0004,
        AlignJustify  = 0x0008,
        AlignTop      = 0x0020,
        AlignBottom   = 0x0040,
        AlignVCenter  = 0x0080,
        AlignBaseline = 0x0100,
        AlignCenter   = AlignVCenter|AlignHCenter
    };
    inline Alignment operator|(Alignment a, Alignment b){
        return static_cast<Alignment>(static_cast<int>(a)|static_cast<int>(b));
    }
}

int main(int argc, char *argv[]){
    Qt::Alignment align=Qt::AlignLeft;
    align=Qt::AlignTop|Qt::AlignHCenter;

    if(align&Qt::AlignTop) cout << "Align Top" << endl;

    return 0;
}

As a Python evangelist it had long been a disappointment to me that the language had no concept of enumerated types. So much so that I actually went away and designed my own! Thankfully, in Python 3.4 we now have the enum package! Yay! It’s even been back ported to Python<=3.4 as enum34! Unfortunately, this doesn’t provide support for bitwise operations, however we can easily add them. The following example shows how to implement the Alignment flag C++ example from above in Python:

from enum import Enum as _Enum

class EnumMask(object):
    def __init__(self, enum, value):
        self._enum=enum
        self._value=value

    def __and__(self, other):
        assert isinstance(other,self._enum)
        return self._value&other.bwv

    def __or__(self, other):
        assert isinstance(other,self._enum)
        return EnumMask(self._enum, self._value|other.bwv)

    def __repr__(self):
        return "<{} for {}: {}>".format(
            self.__class__.__name__,
            self._enum,
            self._value
        )

class Enum(_Enum):
    @property
    def bwv(self):
        cls=self.__class__
        idx=list(cls.__members__.values()).index(self)
        return 2**idx

    def __or__(self, other):
        return EnumMask(self.__class__, self.bwv|other.bwv)

    def __and__(self, other):
        if isinstance(other, self.__class__):
            return self.bwv&other.bwv
        elif isinstance(other, EnumMask):
            return other&self
        else: raise

if __name__=="__main__":
    class Qt(Enum):
        AlignLeft     = 0x0001
        AlignRight    = 0x0002
        AlignHCenter  = 0x0004
        AlignJustify  = 0x0008
        AlignTop      = 0x0020
        AlignBottom   = 0x0040
        AlignVCenter  = 0x0080
        AlignBaseline = 0x0100
        AlignCenter   = AlignVCenter|AlignHCenter

    align=Qt.AlignLeft
    align=Qt.AlignTop|Qt.AlignHCenter
    if align&Qt.AlignTop: print("Align Top")

This takes advantage of the fact that the enumerations are stored in order (using OrderedDict) so we can always get a unique bitwise value (bwv) for each enumeration element based on its position. EnumMask simply acts a wrapper for combinatorial values of Enums.

Hope this helps out someone else, please feel to ask questions or for more detailed explanations etc.