Now that Qt 5.11 is released, it is finally time to upgrade the last Qt 1.0 applications out there… No, not really. 😉 I want to take a look at how well we have kept compatibility in Qt over the years since the first official release.
Qt guarantees source and binary compatibility between minor releases, and we take that seriously. Making sure that you don’t have to rewrite (or even recompile) your application when you upgrade to a newer version of Qt is important to us. However, there are times when we need to make bigger changes in order to keep Qt up to date. This is done in major releases. Since the release of Qt 1.0 in 1996 (almost twenty-two years ago), we have broken source compatibility four times: in 2.0, 3.0, 4.0 (some of you may remember that as a painful transition), and 5.0.
We do try to keep breakages to a minimum, even in the major releases, but the changes do add up. This raises the question: How hard would it be to port a Qt application from Qt 1.0 to 5.11?
To find an answer to this question, I took the tutorial example from the Qt 1.0 release, and tried to compile it against Qt 5. Since the Qt archives only go back to version 1.41, I actually had to retrieve it from ancient history which has been preserved through four different source code control systems …but I digress. Its name is t14 because it is the 14th and final chapter of the tutorial.
Here are the steps I needed to make to get it to build and run.
Import tutorial 14 from Qt 1.0 10 files changed, 798 insertions(+)
This is the state of the art: the official recommendation from Troll Tech (we weren’t Trolltech yet) on how to write programs with Qt 1.0, in 1996.
Translate to qmake 3 files changed, 2 insertions(+), 148 deletions(-)
Before qmake, there was tmake. tmake was written in Perl. The basic syntax was the same, except that qmake no longer allows embedding Perl code in the project files. This is probably for the best…
Also, tmake was not public, so we would generate Makefiles for the release packages. Having completely different internal and external build systems added excitement to the release process.
Fix include file names 4 files changed, 8 insertions(+), 8 deletions(-)
Back in the old days, Windows only allowed eight character file names. We did have proper include files on Unix, but if you wanted your code to be portable, you had to use wonderful names like
Add missing includes 1 file changed, 3 insertions(+)
Dependency on indirect includes was a problem back then as well.
Change TRUE/FALSE to true/false 1 file changed, 13 insertions(+), 13 deletions(-)
Kids these days. They don’t know how lucky they are. We had to make our own
Fix things moved to the Qt:: namespace 3 files changed, 15 insertions(+), 15 deletions(-)
The Qt namespace was introduced in 1998 … as a class, since we didn’t have those fancy namespaces back then.
Remove “name” argument 6 files changed, 26 insertions(+), 26 deletions(-)
All constructors of QObject subclasses used to take the object name as a parameter.
The QScrollBar API has changed 1 file changed, 5 insertions(+), 5 deletions(-)
Sometimes we have to get rid of old, bad APIs. Having individual setter functions is much better than a constructor that takes 7 arguments.
Use QString instead of const char * 2 files changed, 2 insertions(+), 2 deletions(-)
QString has been in Qt since 1994. It used to be 8 bit Latin1 with an automatic cast to const char*, so the API would use
const char *arguments. Unicode support was introduced in Qt 2.0.
warning() is called qWarning() now 1 file changed, 1 insertion(+), 1 deletion(-)
We avoid putting identifiers into the global namespace. Except for the letter ‘Q’. We own that.
Remove calls to old QApplication functions 1 file changed, 2 deletions(-)
Qt does the right thing automatically these days. In 1996, most displays used 8 bits per pixel. You had to tell Qt if you wanted to use other colors than the 256 standard ones.
Replace QAccel with QShortcut 1 file changed, 4 insertions(+), 3 deletions(-)
QShortcut is more powerful and easier to use, and the name is not an abbreviation. It was introduced in 2004.
Fix repaint logic 1 file changed, 7 insertions(+), 7 deletions(-)
In the ’90s, we could just paint directly onto the widgets whenever we wanted. Nowadays everything is buffered and composed, so we have to make an update request, and later repaint the widget when we get a go-ahead. Fortunately, this simplifies the logic.
QObject::killTimers() doesn’t exist anymore 2 files changed, 3 insertions(+), 2 deletions(-)
This function was just too dangerous. It would kill all timers belonging to an object, including those used by Qt internally. Now you have to kill timers individually.
QWMatrix is now QMatrix 1 file changed, 2 insertions(+), 2 deletions(-)
A simple name change.
QWidget::setBackgroundColor() is gone. 1 file changed, 3 insertions(+), 1 deletion(-)
Background color is no longer a separate concept: it’s wrapped inside QPalette together with all the other color roles. Also, child widgets are now transparent by default. We have to tell Qt to draw the background.
Can’t fill pixmap with contents of widget anymore 1 file changed, 1 insertion(+), 1 deletion(-)
We use a transparent pixmap instead, since Qt supports that now.
Rectangle painting has changed since Qt 1.0 1 file changed, 1 insertion(+), 1 deletion(-)
This is the worst incompatibility so far: In Qt 4.0 QPainter::drawRect() was changed so that “a stroked rectangle has a size of rectangle.size() plus the pen width”. Therefore, we need to subtract the pen width (i.e. 1) before passing the rectangle to QPainter.
Now we have a fully functional port of the tutorial example, and here is a screenshot:
Oops… Some of the text is clipped. It turns out that the example used hardcoded sizes and positions for most of the elements, and font sizes have changed a bit since 1996. The solution is to use QLayout, which was not available in Qt 1.0 (the first version was added in Qt 1.1, and it was completely rewritten for 2.0).
Use layouts instead of hardcoded sizes 4 files changed, 29 insertions(+), 24 deletions(-)
With this change, everything looks as it should:
So, what did we learn? It did not take that much effort to port tutorial 14 from Qt 1.0 to Qt 5.11. It probably took me longer to write this blog post. Most of the 1.0 API still lives on in recognizable form. The incompatible changes have improved the usability, readability and safety of Qt code.
This was just a small example, of course. Porting a full-sized application across multiple major versions would probably be more difficult. Fortunately, we have a vibrant Qt ecosystem with several companies offering consultancy services .