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++; its an entire application framework. Its one of the very few pieces of software youll find thats triple licensedyou 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.
Heres one of the greatest misconceptions about Qt its 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, were 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 youve finished your first Qt application, you will not want to go back to using vanilla C++, ever.
A primer on Qts architecture
Theres a lot of difference between the code that you write when programming with Qt, and the code that actually gets compiled.
Qts primary language is C++. Bindings to other languages are available, but if youre just starting out with Qt, its better to start with C++, because there are a bunch of concepts youll need to understand and itll be much easier to understand them if youre 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.
Lets 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 systemsobjects 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 dont 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 youll need to know about how signals and slots work because Qts 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 considerone, 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. Whats 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 its 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 doesnt offer all the goodies of other more user-friendly languages like Python, where you can type dir(my_object) and see exactly whats 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 cant inspect the contents of the class during runtime and make decisions based on what you find (this is called introspection, by the way).
C++ doesnt 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
lets just say people dont 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 Qts designers decided to outfit C++ with an introspection mechanism, and the way they did it was pretty cool.
Just before the file is compiled, its passed through the Meta-Object Compiler or MOC. The MOC is like a pre-processor on steroids. Its 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 whats 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 thats 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 its unfair to call it C++ any more, you might guess that Qt has its own set of basic and complex data types that youre supposed to use instead of whats in the standard library. And youd be right. While Qt doesnt 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 lets start with the very basicsthe 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. Theres no qbool, so youll be using the standard bool datatype for Boolean data.
Now there are the more complex data types. Youll 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. Its 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 youll deal with often is the QString. As suggested by the name, QStrings are used to store strings. Its way better than the standard library strings, though, because it allows for very easy text-processing (if youve used Pythons 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).
Youll also need to know about the QStringLiteral macro. Youll 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, theres 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. Ive already mentioned the QList, which is just a list (an array that can grow infinitely) of objects, and theres also a QVector, which again is very similar to the standards librarys std::vector. The other two types of collections youre 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 mathematicsa 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 youre 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. Its 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 dont feel restrained by your own IDE. Its also got in-built Git support.
Second, QtCreator takes care of the boilerplate for you, so that you dont have to spend time writing build scripts and the like. It integrates with qmake, Qts own build tool, and for all the flak it receives, qmake is actually pretty awesome. So far, Ive 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 packageone that installs binaries, and supports files in /share and configuration files inside /etc. Because QtCreator generates qmake project files, you dont 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. Youll need qmake to be installed if you have the Qt-Core development package installed, so its 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, its pretty complicated). Im going to explain how to use QtCreator only, and build stuff with qmake. Dont worry, its easy and professionals do use QtCreator. Youre not sacrificing the powerful way for the easy way.
In the next article
Now that you know a little bit about the theory behind Qts awesome architecture, youre ready to write some code. In the next article, well 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.