Qt5: Let’s Learn Some Theory

0
15376

QT
This article marks the beginning of a new column on Qt5 programming, which is a platform-independent application framework. It is widely used for developing application software that can be run on diverse hardware and software platforms with practically no change in the code base.

Qt (pronounced Cute) has a long and illustrious history. Originally developed by a company called Trolltech, Nokia took over the reins of this application framework before handing it over to Digia. Qt is no longer just a set of libraries for C++; it’s an entire application framework. It’s one of the very few pieces of software you’ll find that’s triple licensed—you have a choice between LGPL-2.1 and GPLv3 if you want to use the open source version of it, or you can get a Qt Commercial License if you want to use the proprietary version.

Here’s one of the greatest misconceptions about Qt— it’s not a GUI library. Well, it is, but GUIs are only part of what Qt can do. In fact, in the course of this series of articles, we’re going to develop a network-based application using Qt, and the server is going to be a CLI application. Qt is going to introduce you to a completely new programming paradigm, and once you’ve finished your first Qt application, you will not want to go back to using vanilla C++, ever.

A primer on Qt’s architecture
There’s a lot of difference between the code that you write when programming with Qt, and the code that actually gets compiled.
Qt’s primary language is C++. Bindings to other languages are available, but if you’re just starting out with Qt, it’s better to start with C++, because there are a bunch of concepts you’ll need to understand and it’ll be much easier to understand them if you’re using C++.
But Qt itself extends the entire C++ language and makes it much more flexible and powerful, thanks to the unique pre-processing and code-generation that it does before compiling any C++ code.
Let’s get started.
Part 1: Signals and slots
When you write standard C++ code, you have three access modifiers available for class members: there are private members, the visibility of which is restricted to other members of the class only; the public members, which can be seen from outside the object, and the protected members, which are hidden from public view but can be inherited.

Qt implements a programming paradigm called the Observer pattern. This is a fancy way of saying that an object maintains a list of sub-objects, which are dependent on the aforesaid master object, and that the sub-objects are automatically notified of any changes in the state, necessary for them to do their job. To achieve that effect, Qt uses a style that must be very familiar to developers of event-based systems—objects emit signals (Qt decided to add an emit keyword to C++ for this), which are then sent to slots that are listening for those signals.

For this, Qt adds three more access modifiers that you can (and must) use to define these additional pieces of functionality into your classes: signals, under which you define the signals that your object can emit, and public slots and private slots, under which you define the functions implemented by your object to handle signals that your object is listening for.

You don’t have to have both signals and slots in all your objects; indeed, you might have an object that only emits certain signals, or you might have objects that only listen for signals emitted by other objects. You might also have objects that do neither, but you’ll need to know about how signals and slots work because Qt’s bundled libraries use this mechanism extensively.

Both signals and slots are fully inheritable, and can be overloaded just like normal C++ functions. However, there are two factors to consider—one, neither signals nor slots can have a non-void return type, and two, while you have to both declare and define slots, you only need to declare the signal prototype in the class definition, not define it anywhere. In fact, you must not write the function body for the signals anywhere. That work is done somewhere else, as I will explain.

Part 2: QObject and the meta-object system
So how does all of this magic work? Let me explain with some code:

class TCPServer : public QObject
{
Q_OBJECT

private:

QTcpServer * server;

public:

explicit TCPServer(QObject *parent = 0);
~TCPServer();

signals:

void haveRequest(QTcpSocket *);
void haveResponse(QTcpSocket *);

public slots:

void newConnection();
void acceptError()
};

The above code snippet is a typical ‘Qt-fied’ class. What’s the first thing you notice about it?

The very first thing that a class must do, if it has to use all of the goodies provided by Qt, is to inherit from the QObject class. This sets up a bunch of internal tables and mechanisms that set up the class so that it’s usable with the signals and slots system.

The second thing that you must do, before you declare any other variables or methods inside the class, is to write the macro Q_OBJECT. Among other things, this tells the meta-object compiler to enable introspection on this class.
C++ is a compiled, static language. It doesn’t offer all the goodies of other more user-friendly languages like Python, where you can type dir(my_object) and see exactly what’s inside the object, or use methods like hasattr(my_object, “my_method”), which returns true if my_object has a member function called my_method. In other words, with C++, you can’t inspect the contents of the class during runtime and make decisions based on what you find (this is called introspection, by the way).

C++ doesn’t do introspection (this is not strictly true, since there is a mechanism called RTTI – Run Time Type Information – which provides a limited capability to infer type information about objects during runtime, but it requires special compiler support and… let’s just say people don’t like to use this feature). But the people who built Qt decided that if C++ did have a way of introspection for an object, the signals and slots mechanism could be implemented very elegantly. Indeed, during runtime, when objects keep getting created, and slots rapidly subscribe to and unsubscribe from signals, for the mechanism that marshals all these connections, introspection comes in useful.
So Qt’s designers decided to outfit C++ with an introspection mechanism, and the way they did it was pretty cool.

Just before the file is compiled, it’s passed through the Meta-Object Compiler or MOC. The MOC is like a pre-processor on steroids. It’s a full-fledged C++ code generator, and one of the things it does is take a look at the class contents and build a table inside the class with information on what’s in the class. That way, the signals and slots marshalling mechanism needs only to refer to this table to see the internal details of the class, and we have introspection.

The MOC does a lot of other things too. It sets up tables that are used to keep track of the connections between signals and slots. It sets up code that’s used to delay deletion of an object until all signal-slot connections that refer to the object are disconnected. This way, it manages to implement a rudimentary form of garbage collection. And most importantly, it generates the body of the signal functions.

Part 3: The basic data types
Now that you know Qt messes with C++ so much that it’s unfair to call it C++ any more, you might guess that Qt has its own set of basic and complex data types that you’re supposed to use instead of what’s in the standard library. And you’d be right. While Qt doesn’t break C++ (standards-compliant C++ code will always work in Qt), it does define its own data types that make programming with C++ so much easier. In particular, its types for strings and collections are a lot easier to use than the C++ standards library and the STL.

But let’s start with the very basics—the integers, if you will. You have qint8, qint16, qint32 and qint64 for signed 1-byte, 2-byte, 4-byte and 8-byte integers. You have quint8, quint16, quint32 and quint64 for unsigned 1-byte, 2-byte, 4-byte and 8-byte integers. You have qintptr for pointers, and qptrdiff for signed pointers, typically used for storing pointer deltas. You have a single qreal for floating point numbers, which is a typedef for double everywhere except on ARM, where it is a typedef to float for performance reasons. There’s no qbool, so you’ll be using the standard bool datatype for Boolean data.

Now there are the more complex data types. You’ll be dealing with QByteArray a lot, which is basically a data type for keeping generic data that grows and shrinks dynamically, i.e., for creating buffers. It’s very easy to change the endian-ness of data inside QByteArrays, so this is going to be invaluable in network programming.

The next data type you’ll deal with often is the QString. As suggested by the name, QStrings are used to store strings. It’s way better than the standard library strings, though, because it allows for very easy text-processing (if you’ve used Python’s split and join functions on strings to split a string on a certain character and/or join different strings with a certain character, QString allows that).
You’ll also need to know about the QStringLiteral macro. You’ll have to use this macro if you want to pass literal strings in function arguments that expect QString arguments, like:

someFunctionCall(QStringLiteral(“Long live Mr Potato!\n”));

Before we move on from the subject of strings, there’s a QStringList too. A QStringList is created when you split QStrings on a character, and it behaves exactly like a QList<QString>. As the name suggests, this is just a list of QStrings.
And finally, Qt has its own set of template collections. I’ve already mentioned the QList, which is just a list (an array that can grow infinitely) of objects, and there’s also a QVector, which again is very similar to the standards library’s std::vector. The other two types of collections you’re very likely to use is the QMap, which is basically just a name-value pair to be used as associative arrays and dictionaries, and the QSet, which is like a set in mathematics—a collection of unique objects.

Build systems and QtCreator
You can code in Qt with Vim (or Emacs, if you swing that way), and compile using CMake or whatever build system you prefer, but if you’re just getting started, you should simply install QtCreator.
There are a few reasons for this. First, QtCreator is great. The code-completion and automated refactoring capabilities rival those of Visual Studio. It’s very well designed, and devotes a lot of screen real estate to the actual text editor, instead of filling the screen with toolbars. And it looks good —the incredible features that it has are all hidden away but available with just a few mouse-clicks, so you don’t feel restrained by your own IDE. It’s also got in-built Git support.

Second, QtCreator takes care of the boilerplate for you, so that you don’t have to spend time writing build scripts and the like. It integrates with qmake, Qt’s own build tool, and for all the flak it receives, qmake is actually pretty awesome. So far, I’ve used QtCreator to create a piece of software and uploaded it to GitHub; and then written a PKGBUILD for Arch Linux that grabs the code off of GitHub, builds it and creates a complete package—one that installs binaries, and supports files in /share and configuration files inside /etc. Because QtCreator generates qmake project files, you don’t need it to build these files; you simply need to run qmake instead of ./configure to generate the make files and then just make && sudo make install as usual. You’ll need qmake to be installed if you have the Qt-Core development package installed, so it’s not an additional dependency (unlike CMake). And qmake project files are actually easy to read.

Due to space constraints, writing build scripts and CMakeFiles are out of the scope of this article (because of the MOC and other steps you need to take to set up the build environment, it’s pretty complicated). I’m going to explain how to use QtCreator only, and build stuff with qmake. Don’t worry, it’s easy and professionals do use QtCreator. You’re not sacrificing the powerful way for the easy way.

In the next article
Now that you know a little bit about the theory behind Qt’s awesome architecture, you’re ready to write some code. In the next article, we’ll write a network server application that sends out a random fortune cookie to anyone who connects to it. And as you will see, it is going to be too easy for words.

LEAVE A REPLY

Please enter your comment!
Please enter your name here