Sunday, February 13, 2011

Long live Qt!

I use Qt for desktop development and am not terribly interested in mobile apps at the moment (and web-apps are a better idea in the vertical market where I work, which is a different toolset) so Nokia's decision to get into bed with Microsoft doesn't concern me in the short term. I think the best thing that could happen is for someone else to make Nokia an offer for Qt ... Intel or Google perhaps, or for the ex-Trolltech guys to strike out on their own. Either way I feel that Qt is the best choice for cross-platform desktop development and long may it continue.


I will try to update this blog more often, especially in light of the recent Nokia announcement.

BTW, I spent 6 months working at Nokia in 2010 and they really had to do something about their glaring lack of a decent smartphone. Whether the decision to adopt WP7 was the right one or not I guess only history will tell but I'm not optimistic and think that Microsoft swallowing Nokia whole is a distinct possibility. Let's hope Qt survives and thrives.

Monday, May 10, 2010

Qt Creator 2.0 Beta First Impressions

Last week Nokia released a beta of Qt Creator 2.0 (and Qt 4.7 FWIW) and I have finally managed to spend some time playing with it today. Here's a quick list of likes and dislikes in no particular order.

Likes
  • The new Options dialog is much easier to use IMHO. It doesn't add much to the product but there's nothing wrong with a bit of polish and attention to detail.
  • Support for Mercurial (Hg). I have been using this today without any problems.
  • Revamped Projects view. I'm not a massive fan of anything that leads to a dependency on a .pro.user file but the new Projects page is much easier to use than previous releases.
  • The new Build/Target button is pretty smart and really useful when you have multiple projects loaded. Ctrl+T pops it up for those of us who are keyboard-centric.
  • You can now right-click on a project file and open it using the 'System Editor' (i.e. the application registered for the selected file extension) which I will be using a lot.
  • You can finally set the default projects directory.
Dislikes
  • On Windows, Ctrl+W no longer closes the current file - this has now been changed to the 'standard' Ctrl+F4 keystroke instead. I found this annoying and can hopefully restore Ctrl+W using the keyboard mappings editor.
  • The Output view has been removed. I am in two minds as to whether this was a good idea.
  • You cannot make a project active by right-clicking on it - you have to use the new Build/Run Target Selector.
  • I cannot get the Mercurial commit keystroke to work (Alt+H, Alt+C). Alt+H opens the Help menu. I must be missing something here.
  • When you are editing a .ui file, the new Design view is enabled. If you switch back to the Edit view, the XML .ui file is displayed in read-only mode. I'm not sure if this is desirable.
  • Shadow builds are enabled by default which confused me for a little while ("Where the hell's my bloody executable?"). I'm actually on the fence about shadow builds - I'm only doing desktop development so aren't sure if they are of much use to me. Plus I can't find a really good overview of the advantages of using them.
Overall I do prefer it to 1.3 and as I've come to expect from Nokia, it's rock solid.

I'll post a more detailed review when the final version ships (which I'm betting won't be long.)

Code to Generate a Version Number Header (2)

I have recently switched to using Mercurial (Hg) as my version control system of choice so have updated my little tool used to generate a version number header file that other projects can include. I have included both the local and global Hg revision numbers plus I've added an inline function to return the version number as a string. As before, the version number includes the major, minor and daily build numbers. I display the global Hg revision number in my About dialogs in the same way as Qt Creator does (the global Hg revision number is a string).

A typical version header now looks like this:

#ifndef VERSION_H
#define VERSION_H

namespace Version
{
const int MAJOR = 1;
const int MINOR = 6;
const int LOCAL_REVISION = 46;
const int BUILD = 16130;

inline const char* versionString()
{
return "1.6.46.16130";
}

inline const char* globalRevision()
{
return "81a3662fbc71";
}
}

#endif // VERSION_H

The code that produces the header looks like this (apologies for the formatting - the online source code formatter isn't working today):

#include <iostream>
#include <QProcess>
#include <QStringList>
#include <QFile>
#include <QTextStream>
#include <QDate>
#include <QTime>
#include <QFileInfo>
#include <QTemporaryFile>
#include <QtGlobal>
#include <cstdlib>

namespace
{
int getBuildNumber()
{
const QDate today(QDate::currentDate());
return ((today.year() - 1994) * 1000) + today.dayOfYear();
}

int getLocalHgRevision()
{
int revision = 0;
QProcess process;
process.start("hg", QStringList() << "id" << "-n");
if (process.waitForStarted() && process.waitForReadyRead())
{
revision = atoi(process.readAll().constData());
process.waitForFinished();
}
return revision;
}

QString getGlobalHgRevision()
{
QString revision = "";
QProcess process;
process.start("hg", QStringList() << "id" << "-i");
if (process.waitForStarted() && process.waitForReadyRead())
{
revision = QString(process.readAll().constData()).trimmed();
revision.remove('+');
process.waitForFinished();
}
return revision;
}

QByteArray readFile(const QString& fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
{
return QByteArray();
}
return file.readAll();
}

int writeFile(const QString& fileName, const int major, const int minor, const int localRevision,
const int buildNumber, const QString& globalRevision)
{
// Create a temp file containing the version info and
// only replace the existing one if they are different
QTemporaryFile tempFile;
if (tempFile.open())
{
QTextStream out(&tempFile);
out << "#ifndef VERSION_H\r\n";
out << "#define VERSION_H\r\n\r\n";
out << "namespace Version\r\n";
out << "{\r\n";
out << "\tconst int MAJOR = " << major << ";\r\n";
out << "\tconst int MINOR = " << minor << ";\r\n";
out << "\tconst int LOCAL_REVISION = " << localRevision << ";\r\n";
out << "\tconst int BUILD = " << buildNumber << ";\r\n\r\n";
out << "\tinline const char* versionString()\r\n";
out << "\t{\r\n";
out << "\t\treturn \"" << major << '.' << minor << '.' << localRevision << '.' << buildNumber << "\";\r\n";
out << "\t}\r\n\r\n";
out << "\tinline const char* globalRevision()\r\n";
out << "\t{\r\n";
out << "\t\treturn \"" << globalRevision << "\";\r\n";
out << "\t}\r\n";
out << "}\r\n\r\n";
out << "#endif // VERSION_H\r\n";

const QString tempFileName = tempFile.fileName();
tempFile.close();

if (!QFile::exists(fileName) || readFile(fileName) != readFile(tempFileName))
{
QFile::remove(fileName);
QFile::copy(tempFileName, fileName);
}

return 0;
}
else
{
std::cout << "Error creating temporary file!" << std::endl;
return 1;
}
}
}

int main(int argc, char *argv[])
{
if (argc != 4)
{
std::cout << "Usage: version major minor filename" << std::endl;
return 1;
}

const int major = atoi(argv[1]);
const int minor = atoi(argv[2]);
const int localRevision = getLocalHgRevision();
const QString globalRevision = getGlobalHgRevision();
const int buildNumber = getBuildNumber();

std::cout << major << '.' << minor << '.' << localRevision << '.' << buildNumber;
std::cout << ' ' << qPrintable(globalRevision) << std::endl;

return writeFile(argv[3], major, minor, localRevision, buildNumber, globalRevision);
}

Sunday, May 9, 2010

Hidden Features of Qt

OK, the title of this post is a bit of a misnomer as the Qt documentation is so good you'd be hard pushed to find something that is truly hidden ... but I came across a great list on stackoverflow and I thought I'd share the best bits here.
  • forever
    This is a macro that expands to for (;;). Very useful. You're already using foreach
    already right?
  • qChecksum
    This returns a CRC-16 checksum of a char* buffer.
  • qCompress / qUncompress
    These functions perform zlib compression/uncompression of a QByteArray. You can also specify the compression level.
  • qFuzzyCompare
    Use this to safely compare float/double values and avoid the dreaded floating point comparison trap.
  • qVersion
    Get the runtime version of Qt being used.
  • qPrintable
    Converts a QString to a const char* instead of writing QString.toLocal8Bit().constData(). Very useful - I could of done with this the other day when outputting some debug strings using...
  • qDebug
    Write to the debug window, in std::iostream style, e.g.:

    qDebug() << "String contents: " << qPrintable(myString);

  • QObject::deleteLater()
    This is used to force an object to delete itself (delete this) when a signal occurs. I have used this to delete a QTcpSocket object automatically when the socket is disconnected.
There are lots more if you follow the link.

Wednesday, April 28, 2010

Sample Multiple Unit Test Project

Long time no post...

Anyway, someone asked me if I could upload a working test project that shows my AutoTest header in use, so click here to download test.zip which includes two sample test classes and a main.cpp that shows two ways to start the tests running.

Open the test.pro file using Qt Creator, build it and let it rip and you should see output that looks something like this:

********* Start testing of Test2 *********
Config: Using QTest library 4.6.2, Qt 4.6.2
PASS : Test2::initTestCase()
PASS : Test2::test1()
FAIL! : Test2::test2() Compared strings are not the same
Actual ("Hello"): Hello
Expected ("World!"): World!
test2.cpp(14) : failure location
PASS : Test2::cleanupTestCase()
Totals: 3 passed, 1 failed, 0 skipped
********* Finished testing of Test2 *********
********* Start testing of Test1 *********
Config: Using QTest library 4.6.2, Qt 4.6.2
PASS : Test1::initTestCase()
PASS : Test1::test1()
FAIL! : Test1::test2() '1 == 0' returned FALSE. ()
test1.cpp(14) : failure location
PASS : Test1::cleanupTestCase()
Totals: 3 passed, 1 failed, 0 skipped
********* Finished testing of Test1 *********


I hope you find this useful - it has certainly made my life easier when using QTestLib.

Thursday, January 14, 2010

Code to Generate a Version Number Header

I've had a couple of requests to post the code I use to generate a version number header, so here it is. Create a Qt console-based application using the code below and pass it the major and minor version numbers and the name of the header file to create on the command-line, e.g.:

version.exe 1 2 ver.h

The code will create a header called ver.h using a major version number of 1 and a minor version number of 2. I include the Subversion revision number and a special build number that represents today's date, so the final version number might be something like:

1.2.2345.16001

Here is the version.cpp file:

#include <iostream> 
#include <QProcess>
#include <QStringList>
#include <QFile>
#include <QTextStream>
#include <QDate>
#include <QTime>
#include <QFileInfo>
#include <QTemporaryFile>
#include <cstdlib>

using namespace std;

static int getBuildNumber()
{
const QDate today(QDate::currentDate());
return ((today.year() - 1994) * 1000) + today.dayOfYear();
}

static int getSubversionRevision()
{
int revision = 0;
QProcess process;
process.start("svnversion", QStringList() << "." << "--no-newline");
if (process.waitForStarted() && process.waitForReadyRead())
{
const QString str(process.readAll().constData());
const int pos = str.indexOf(':');
if (pos != -1)
{
revision = atoi(str.mid(pos + 1).toAscii().constData());
}
else
{
revision = atoi(str.toAscii().constData());
}
process.waitForFinished();
}
return revision;
}

static QByteArray readFile(const QString& fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly))
{
return QByteArray();
}
return file.readAll();
}

static int writeFile(const QString& fileName, const int major, const int minor, const int revision, const int build)
{
// Create a temp file containing the version info and
// only replace the existing one if they are different
QTemporaryFile tempFile;
if (tempFile.open())
{
QTextStream out(&tempFile);
out << "#ifndef VERSION_H\r\n";
out << "#define VERSION_H\r\n\r\n";
out << "namespace Version\r\n";
out << "{\r\n";
out << "\tstatic const int MAJOR = " << major << ";\r\n";
out << "\tstatic const int MINOR = " << minor << ";\r\n";
out << "\tstatic const int REVISION = " << revision << ";\r\n";
out << "\tstatic const int BUILD = " << build << ";\r\n";
out << "}\r\n\r\n";
out << "#endif // VERSION_H\r\n";

const QString tempFileName = tempFile.fileName();
tempFile.close();

if (!QFile::exists(fileName) || readFile(fileName) != readFile(tempFileName))
{
QFile::remove(fileName);
QFile::copy(tempFileName, fileName);
}

return 0;
}
else
{
cout << "Error creating temporary file!" << endl;
return 1;
}
}

int main(int argc, char *argv[])
{
if (argc != 4)
{
cout << "Usage: version major minor filename" << endl;
return 1;
}

const int major = atoi(argv[1]);
const int minor = atoi(argv[2]);
const int revision = getSubversionRevision();
const int build = getBuildNumber();

cout << major << '.' << minor << '.' << revision << '.' << build << endl;

return writeFile(argv[3], major, minor, revision, build);
}

Tuesday, December 8, 2009

Registering Qt Creator as a Post-Mortem Debugger on Windows

When you install Qt Creator on Windows you are offered the chance to register it as a post-mortem debugger. This means, if your app should crash, you can launch Qt Creator to debug it. Very handy. However, if you forget to select this option when installing you can register it after the event from the command-line:

<CreatorDir>\bin\qtcdebugger -register

Note that you need to be an Admin user for this to work.

Thanks to Robert Löhning at Nokia for the top tip.

Qt Creator 1.3/Qt 4.6.0 Released

Yes, this post is a little late for the party, but you can get the new Qt SDK from here:


This version now ships with the MinGW 4.4 toolset which includes a faster compiler and generates better code.

Oh, and Qt have released a version of the 4.6.0 framework that is built using MS Visual Studio 2008. This means, if you have the relevant MS development tools installed, you can compile your apps using the MS compiler (which might generate even better, smaller code than MinGW but let's not get into that argument now...) This is useful as, previous to this release, you'd have to build an MS version of Qt yourself which was a PITA (it takes hours and gobbles disk space.)

All hail the Trolls!

Monday, November 2, 2009

Qt Creator Keyboard Reference Card

This is very, very useful.

http://kdab.net/qtcreator

Thanks to Nicolas Arnaud-Cormos who posted this on the Qt Creator mailing list.

Tuesday, October 27, 2009

Breakpoint Issue Fixed!

The breakpoint bug has been fixed in the October 27th nightly Windows build.

ftp://ftp.qtsoftware.com/qtcreator/snapshots/2009-10-27/qt-creator-win-opensource-1.2.93-200910270102-setup.exe

Sunday, October 25, 2009

Breakpoint Update

Further to my breakpoint issues, the latest snapshot is a little better - breakpoints don't work the second time you run your application, but they do the third time ... but not the fourth ... fifth is a charm though! You get the idea. :) So, the Trolls are getting there.

Unused Variables

Qt Creator will display unused variables in grey, which I absolutely love. For example:


Once the variable is referenced in the method, the colour will change:


It's little touches like this that make this product a joy to use.

Monday, October 19, 2009

Breakpoint Woes

I am having some serious breakpoint woes with Qt Creator 1.2.92 at the moment. Breakpoints work fine the first time I set them, but once the application has been terminated, attempting to add/remove breakpoints will fail and they are never triggered. This is driving me nuts and only a restart of Qt Creator seems to fix it.

Reproducing this problem is easy.
  1. Fire up Qt Creator and create a new Qt4 Console Application project.
  2. In main() set a breakpoint on the first line of code.
  3. Hit F5 to debug. The breakpoint is triggered.
  4. Quit the application, then set a new breakpoint, or remove the existing one and set it again.
  5. Hit F5 to debug. The breakpoint won't be triggered.
It looks like the second time gdb is started you can only set breakpoints once the application is being debugged. This is all well and good for a GUI app where you may have the opportunity to toggle the breakpoint as your app is running, but for applications such as unit test projects, you don't have time to do this before the application quits!

I find it hard to believe that something so fundamental is broken so if anyone has any hints, I'll be grateful. FWIW I am using the version of gdb that ships with Qt Creator 1.2.92. I have tried pointing this to the 2009.03 version instead, but it makes no difference at all.

Now, what I think is happening is this... when Qt Creator starts a debug session it should apply any breakpoints you've set by registering them with gdb. This explains why, until you debug your app, the breakpoint symbol in your code has a little wait cursor next to it:


When you debug your app the first time this icon changes as Qt Creator successfully applies the breakpoint:


Any new breakpoints you add stay displayed with the wait cursor so it looks like Qt Creator isn't applying them.

Stepping Through Build Errors/Search Results

Built your project and now you need to step through the build errors? Or done a 'Find in Files' and you don't want to use the mouse to cycle through the results? On Windows, hit F6 to display the next error/result or Shift+F6 to display the previous.

Friday, October 16, 2009

Blogger Source Code Formatting

OK, this is nothing to do with Qt Creator but I wanted to share this site that formats source code ready for using in Blogger posts:


The results are pretty sweet:

 #include <QtGui/QApplication>  
#include "mainwindow.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}

XP-style Common Dialogs

If you are using Qt to create Windows apps for XP then you might of noticed that the standard file dialogs (e.g. QFileDialog::getOpenFileName) are still using 'classic mode':


If you want XP mode versions then you'll have to add a resource file that contains a reference to a magic manifest file which specifies that your app using v6 of the MS common controls library. This is a similar process to adding an application icon as described in my previous post.

First create a file with a .manifest extension (e.g. MyApp.manifest) containing the following XML:

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<dependency>
<dependentAssembly>
<assemblyIdentity type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*' />
</dependentAssembly>
</dependency>
</assembly>

Then create (or edit) your applications resource file (e.g. MyApp.rc) and add the following line:

1 24 DISCARDABLE "MyApp.manifest"

If you haven't done already, add a win32 specific entry to your .pro file that points to your .rc file:

win32:RC_FILE = MyApp.rc

Build your project and you'll now have shiny XP common file dialogs:

Application Icon on Windows

So you've written a cool Windows app using Qt - but hey, it doesn't have an icon! Well, adding one is quite easy.

Simply create a special resource file that contains a reference to the .ico file that you want to use and then add a RC_FILE entry to your .pro file. The resource file usually has a .rc extension, and will look something like this (e.g. MyApp.rc):

IDI_ICON1 ICON DISCARDABLE "path/to/an/icon.ico"

The .pro entry that points to this file should be win32 specific:

win32:RC_FILE = MyApp.rc

Rebuild and hey presto! a nice Windows icon for your app.

You can also give your Linux/Mac applications an icon. More info on this can be found here:

Tuesday, October 13, 2009

Shipping Qt DLLs

If you're using Windows and you want to ship a Qt application then, unless you're statically linking, you'll need to ship some Qt DLL files. Be warned - there are two sets of these files installed with the Qt Creator SDK and only the ones found in the qt\bin folder will work. This set of DLLs have been built using the MinGW compiler, which is the one used by default when you build a project using Qt Creator. However, the set found in the bin folder (and Qt Creator itself) are built using MSVC and are essentially private to Qt Creator - shipping these instead will result in DLL Entry Point Not Found errors when you launch your application.

Now, this begs the question - why is Qt Creator built using MSVC? Isn't MinGW up to the task? Good question. The version of MinGW that ships with the Qt 4.5 SDK is quite an old release and the MSVC compiler generates much smaller files (and arguably generates better code.) I guess the Nokia team wanted to ensure that their flagship tools use the best compiler for each platform.

Whether the forthcoming MinGW update due to be shipped with Qt 4.6 later this year does a much better job or not remains to be seen. It will be interesting to see which compiler they decide to built their toolset with...

Monday, October 12, 2009

Generating Automatic Version Numbers

All of my projects use a special version numbering scheme - major.minor.revision.build - revision is the Subversion revision and build is a build number based on the current date. Whenever I build a project I want this version number to be updated automatically (if it has changed), but making this work using QMake isn't obvious ... but once you know how, it could be used for a variety of different uses.

I have a special Qt-based EXE project that generates the version number by creating a version.h file that looks something like this:

#ifndef VERSION_H
#define VERSION_H

namespace Version
{
static const int MAJOR = 1;
static const int MINOR = 2;
static const int REVISION = 2572;
static const int BUILD = 15280;
}

#endif // VERSION_H

I then #include this header where necessary. The EXE is passed the major/minor version number and the name of the output file (e.g. version.h) and it extracts the SVN revision and generates the special build number. I'll spare you the code - all you need to know is how to ensure this EXE is launched each time I build a project that requires it.

First, I use a subdirs project that contains the version-generator .pro file, ensuring I have a version.exe file to execute. Then, in my main apps .pro file I have something like this:

MAJOR = 1
MINOR = 2
VERSION_HEADER = ../version.h

versiontarget.target = $$VERSION_HEADER
versiontarget.commands = ../version/debug/version.exe $$MAJOR $$MINOR $$VERSION_HEADER
versiontarget.depends = FORCE

PRE_TARGETDEPS += $$VERSION_HEADER
QMAKE_EXTRA_TARGETS += versiontarget

The MAJOR and MINOR definitions are changed by hand when required, the rest is automatic. The key part here is the FORCE keyword - this is poorly (if at all?) documented and ensures that the QMAKE_EXTRA_TARGETS command works every time. Extending this scheme to run additional targets would be simple - create another 'target' type and add it to the list (for example, you may want to launch a shell script that generates some code, etc.)

Getting this to work involved a lot of StackOverflow/Qt-list posts - I hope you find it useful.