An introduction into Qt

published at 16.07.2013 14:53 by Jens Weller
Save to Instapaper Pocket

The last 3 weeks I've been busy with teaching Qt. Each monday I was doing an one on one introductory course, from Qt Core to Qt XML. So, for a few weeks I was very busy with Qt, preparing for Mondays but also looking into things, how should this or that be done. With this course, my concept of teaching Qt has become reality, but I'd like to share my views on Qt with you, and give you an overview over Qt.

Getting Started

First things first, so incase you haven't installed the Qt SDK yet, you might should give it a try, especially if you'd like to try a few of the example Codes. Also, the qt-project.org website is a excellent place to get started (or lost) with Qt. Qt brings a bunch of examples with it, and also there are some great slides for teaching by digia. Qt5.1 is the newest version, I'm not sure if they updated also the QtCreator to 2.8 later, as this was released a week later. So you might also want to install the newest version of this great IDE. QtCreator 2.7.2, which I did get with Qt5.1 is fine too for the start. Currently Qt is licensed under LGPL, and digia also is offering a commercial license with support. Commercial Users have to choose between one of these licences.

Qt

With only 3 days I had to make a few decisions. First, it was not desired, to be a Qt5 training, so the focus is on the parts in Qt5 that are also shared with 4.x. Also, I will not cover QML or QtQuick for now, for this reason, and as Qt is really big, it actually doesn't fit in 3 days fully. Also, on the first day, I mostly start with a short introduction into C++, to refresh the knowlegde needed to work with C++, Qt and Object Orientation in general. Qt it self is split into modules, like core, gui, widgets, sql, network and many more. Also there are some Add-ons available (f.e. Printing), which enhance Qt further.

Qt & C++

Before I start with Qt Core Overview, I'd like to point at a few things, that are unique to Qt. First, there is that thing called Moc, Qts Meta Object Compiler Preprocessor. Its part of the concept of QObject and an important base of Qt. The Moc adds a couple of informations to your QObject Class, it allows introspection at runtime and few other things as the Signals/Slot Mechanism used by Qt. It will also generate classes and cpp/header files from .ui files if you use Widgets based Applications. Qt defines a few keywords, but can be used with out using or needing them, as they can be replaced by macros. In practice, the Moc does not limit you at all, Qt is very well usable with boost, poco or other C++ Libraries and the STL. The only thing that annoys me from time to time is, that Qt uses a lot of int, where size_t or unsigned int would be better. int size() in Qts Container classes and using int as index in models isn't really nice afaik.

Qt Core

The core module provides the basic classes of qt, and it also can be used in non UI environments. Writing servers or services in Qt is done by many of its users. A lot of classes in Qt are based on QObject, the base class of Qts Meta Object System. QObject provides an object tree capable of holding other QObject classes as children. Also having such a central base class might be seen today as an anti pattern, QObject provides its uses to Qt, and isn't really overused. Its main purpose IMHO is to the useful ness of SIGNAL/SLOT (which could be done without a baseclass, see boost::signals2), and the Object tree functionality. Plus the Q_OBJECT Macro, which the Moc will turn in a to a few lines of special code and methods. Such as letting the Class know its name and methods at runtime.

In this first part, I will mostly stay within Qt Core, and just handle its classes. First I'd like to handle memory allocation and such in Qt and C++. A very important topic in C++ have smart pointers become, Qt provides a set of smartpointers. As its hopefully known to you, you should allocate memory (e.g. variables) either on the stack or with new. The first one is preferred, as its faster, and automatically destroyed with the end of the stacks/scopes lifetime. When ever an object is either to big for the stack, or needs to live longer, one should allocate it with new, and carefully guard the pointer to this new object. Later, to free the object, you'll need to call delete of course. And you don't see a lot of deletes in Qt, so whats happening? QObjects need to be created with new, IF they have a parent, which makes them part of an object tree in Qt. This is used to put widgets and layouts together. In such an object tree, the children are deleted, when the parent is deleted. So, the root of the tree can be on the stack, while all its children need to be on the heap. Also, as QObjects can receive signals, one might now want to delete them, as some signals might get lost. With QObject::deleteLater it is ensured, that the deletion happens after the handling of still pending signals. But, how to deal with pointers, if they are not part of an object tree in Qt?

Qt has for this QSharedData, a class that is a base class for classes handled in QSharedDataPointer, so you can implement the value data in a QSharedData class, f.e. name or number of an Employee Class. The EmployeeData Class will manage the value data, while Employee is the identity. Employee then holds a QSharedDataPointer to EmployeeData. Fortunately this is not the only way Qt can deal with Smart Pointers, the classes QSharedPointer/QWeakPointer and QScopedPointer also exist. QSharedPointer has the semantics of sharing a Pointer to a resource through instance counting. QScopedPointer will selfdestruct and free the pointer, once its scope ends. Also, as Qt plays quite well with boost or the C++ Standard, you could use the smart pointer classes from boost or std::shared_ptr/weak_ptr/unique_ptr.

And there is QPointer<T>, which requires T to be derived from QObject (std::is_base_of<T, QObject>::value == true). QPointer is a bit like a weak_ptr for QObject, but without the need to convert it to a fully functional shared_ptr. Instead, you simply test with if(mypointer) for its validness, and then can access the object beneith it. If in the mean time that object gets destroyed, you might be out of luck...

Qt basic types

Qt core also provides a bunch of basic types used in Qt. QString is Qts string class, and its a quite useful implementation for strings imho. It provides far more convinience methods then std::string, and is convertible to a lot of other formats, such as UTF8, std::string or const char*. Then there is a set of date and time classes in Qt, QTime and QDate have quite easy interfaces, and QDateTime provides you with time and date in one class. QDateTime::currentDateTime() will give you now(). I'd like to have a now() method in QDateTime. Qt also has a variant class, QVariant is often used when working with Qts Database support or using its model/view concepts.

Another important part of Qt core are its containers, which are implemented as templates. QList<T> is not a linked list class, its implementation is more std::vector like. There is QLinkedList for linked list fans. A short comparison of Qt Containers to the STL ones:

Qt class STL type
QList<T> std::vector<T>
QStringList (derived from QList<QString> std::vector<QString/std::string>
QVector<T> std::vector<T>
QLinkedList<T> std::list<T> / std::slist<T>
QStack<T> std::stack<T>
QQueue<T> std::queue<T>
QSet<T> std::set<T>
QMap<Key,Value> std::map<Key,Value>
QMultiMap<Key,Value> std::multimap<Key,Value>
QHash<Key,Value> std::unordered_map<Key,Value>
QMultiHash<Key,Value> std::unordered_multimap<Key,Value>

Both, STL and Qt Containers opt for non virtual constructors, still QStringList is a prime example of deriving a class of such a base class. There is an interesting discussion why this is, and why its not fixed with Qt5. Still, there is an important difference between Qt containers and STL containers: Qt containers have value semantics, and will only perform copy on write, while a std container will copy its full contents when copied. This behavoir accounts for most of Qt base classes, that they will only create a new instance for data, when needed. This implicit sharing of resources is a very important concept to understand when dealing with Qt and its containers.

I often use the STL containers(and I do prefer them), just as I try to keep some of my code to be 100% standard C++, STL Containers mix well with Qt, but have of course a different copy semantic. The Qt Containers also do offer two different interfaces, one Javalike, and one STL like. One pitfall is, that the Qt Container function returns int instead of size_t. Actually this is another major difference, that all sizes for containers in Qt are int and not size_t or unsigned int. So when dealing with large datasets, that exceed std::numeric_max<int>, STL containers are your only option. Also this leads to funny code like QVector::resize(-1220), well, don't do this ;)

Then, Qt provides QOIDevice classes such as QFile or QSerialPort (Qt5.1) for accessing io, reading a csv file can be quite easy:

QString path = QFileDialog::getOpenFileName(this,tr("Select file"));
QFile file(path);
if(!file.open(QFile::ReadOnly|QIODevice::Text))
    return;

QTextStream stream(&file);

QString line = stream.readLine();
QString del(",");
if(line.contains(";"))
    del = ";";
else if(line.contains("\t"))
    del = "\t";
while(!stream.atEnd())
{
    line = stream.readLine();
    QStringList items = line.split(del);
    handle_items(line);
}

Reading the contents of a file via a QTextStream has some advantages, instead of using QFile directly, in this case, QTextStream will handle german umlauts in a csv correctly, while QFile will not deal nicely with them. The code also assumes that the first line is a header line, also containing the correct delimeter. It handles ',' by default, but also will deal with ';' and tabulators. Of course it will not deal with quoted strings (e.g. "this is one;datablock".

Qt Core also provides Threading classes, and contains Qts atomic classes. Qt has a QThreadPool class, which executes its tasks QRunnable derived classes, so when dealing with multithreading in Qt, you can implement either your own Thread by letting a function or Method run inside a QThread. Qt has also Mutexes, Futures or Semaphores in its Threading part. With QMutexLocker it offers support for locking Mutexes in Stack Objects, and guaranteeing in this way, that the unlock method is called. Also there is a Qt Concurrent Add-on, which is worth a look, if you have more extensive Multithreading needs with Qt.

But Qt Core offers more, there is JSON Support. With Qt5, JSON is supported in Qt, and QJsonDocument will offer you access to data stored in JSON files. Earlier, with Qt 4.x already Qt did get a few other frameworks, of which are the Animation Framework and the Statemachine Framework part of Qt core. Regarding the Statemachines, you also could use boost::msm with Qt. Also Qt core contains the Plugin System for Qt, and its QEvents Event System.

And before we warp into the TL;DR lands, I will close Part I here. Its covering most of Qt core, the next part will take a look at QGUI, QWidgets and Model/View.

Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!