Trolltech | Documentation | Qt Quarterly | « Implementing a Popup Calendar | Mini-Quiz Answers »

What's New in Qt 3.2
by Jasmin Blanchette with Harald Fernengel
Qt 3.2 is now available. This new version introduces many new classes and some features that you might want to use right away in your Qt applications. We will review many of these in this article. For a complete list of what has changed, see Changes 3.2.0.

Make a Splash

The new QSplashScreen class makes it easy to present the user with a splash screen at start-up. This class is similar to the SplashScreen class presented in Qt Quarterly issue 4. If you already use the SplashScreen class, you can replace it with QSplashScreen in your applications. When doing the conversion, you need to be aware of two things:

New Tool Box Widget

The tool box widget used by Qt Designer since Qt 3.1 is now available as a Qt widget for use in your own programs. The widget is called QToolBox.

Toolboxes

On Windows, the widget has the same look and feel as the native ToolBox widget. In Mac and Motif-based styles, the different pages that constitute the widget are shown as tabs.

QToolBox is fully supported by Qt Designer. To start using it, click ToolBox from the list of widgets (in Qt Designer's own toolbox) and click on your form to place it as usual. You can then populate its pages in the same way as you populate the pages of a QTabWidget or a QWidgetStack.

Thread-Local Storage

In multithreaded applications, static and global variables are shared by all threads. If you want each thread to have its own copy of the variables, you can use the new QThreadStorage template class. QThreadStorage provides thread-specific data storage, also known as thread-local storage (TLS). Before Qt 3.2, the only way to do this was to use platform-specific APIs. Here's how it works:

When is QThreadStorage useful? Basically, for every static or global variable that shouldn't be accessed from multiple threads simultaneously. One common example is a cache. By storing cached data for different threads as thread-local storage, we avoid the problems that would otherwise arise from having multiple threads accessing the same data simultaneously. (Another solution is to use a QMutex.)

Fine Control over QString's Memory Usage

QString often allocates more memory than it needs in case the string is extended repeatedly, as in the following example:

    while (...) {
        str += getChar();
    }
    

For most applications, QString's automatic behavior is sufficient. For the few applications where ultimate control is desired, QString now has three functions for controlling the memory allocated by a string:

These functions never affect the semantics of a QString. They only affect QString's speed and memory usage. Here's an example that uses reserve() to ensure that no memory reallocations take place in the while loop, then squeeze() to ensure that no memory is wasted:

    QString str;
    int len = 0;
    str.reserve(maxLen);
    while (...) {
        str[len++] = getChar();
    }
    str.squeeze();
    

QString with Multiple Args

The arg() mechanism used by QString has an obscure restriction that affects argument strings containing % characters. For example,

    QString("%1 - %2").arg(appName).arg(fileName)
    

will fail if appName contains %0, %1, or %2. Qt 3.2 provides a solution for this, through a set of arg() overloads that accept multiple strings at the same time. Here's how to rewrite the above code snippet to avoid any possible problem:

    QString("%1 - %2").arg(appName, fileName)
    

The new arg() overloads take up to four string arguments. If you have non-string arguments to pass as well (e.g. ints or floats), you can pass them first using the one-argument arg() versions and then use the multiple-argument arg() for the strings.

Mask Support for QLineEdit

The QLineEdit widget now supports input masks. A mask consists of special characters and separators. The special characters represent a set of valid characters that the user may type in, while the separators are always shown and cannot be edited. For example, in the mask 990.990.990.990, the 9s stand for optional digits, the 0s for mandatory digits, and the dots are separators. Such a mask could be used for entering IP addresses, such as 123. 54.129.255.

Masks

Masks complement validators. The main problem with validators is that they have no visual representation. A mask, on the other hand, usually contains separators to guide the user.

You can check at any time whether the user has entered an acceptable value using hasAcceptableInput(). While QLineEdit won't let the user enter a letter where a digit was expected, it is possible that the user hasn't entered all the mandatory information yet, and in that case hasAcceptableInput() returns false.

See the inputMask property's documentation for more information about the meaning of special characters in a mask and for more examples of masks. An easy way to experiment with masks is to set the inputMask property of a QLineEdit in Qt Designer and to preview the form (Ctrl+T).

Full Indic Script and Syriac Support

A lot of work went into Qt 3.0 to make it support right-to-left languages such as Arabic, Hebrew, and Persian. Now, with Qt 3.2, this support has been extended to cover the Indic scripts, including Bengali, Devanagari, and Tamil.

Scripts

Also, the Syriac script has now been added to Qt's list of supported right-to-left scripts. This is supported on Windows with Uniscribe installed and on X11 with XFT and OpenType fonts.

QTabWidget's Corner Widgets

Assistant-Tabs

The QTabWidget class now supports corner widgets on both sides of the tab bar. This feature is used in Qt Assistant, which uses a QTabWidget to present multiple pages and has a button in both corners, the left for adding a new tab, the right for removing the current tab.

Const Begin and Const End

The implicitly shared classes QMap<K, T>, QValueList<T>, and QValueVector<T> now provide constBegin() and constEnd() functions in addition to begin() and end(). When traversing containers using const iterators, the constBegin() and constEnd() functions are faster than their non-const counterparts, since they don't have to worry about whether the data is shared. In most applications, the speed difference is not great, but Qt's internals now use constBegin() and constEnd() whenever possible.

If you want to use these in your applications, look for all occurrences of const_iterator (or ConstIterator), for example:

    QValueVector<int>::const_iterator it = vec.begin();
    while (it != vec.end()) {
        ++it;
    }
    

And replace begin() and end() with constBegin() and constEnd():

    QValueVector<int>::const_iterator it = vec.constBegin();
    while (it != vec.constEnd()) {
        ++it;
    }
    

You might also check that you use const_iterator rather than iterator when you don't modify the data through the iterator.

QTextEdit::setMaxLogLines()

One of Qt 3.1's new features was the introduction of a "log" mode for QTextEdit. This mode is optimized for displaying large amounts of text. QTextEdit now provides a means of setting the maximum number of text lines in that mode. If more lines are appended than the maximum allowed, the top lines are automatically removed.

Support for Long Long

Qt now provides the Q_LLONG and Q_ULLONG typedefs as a portable means of specifying 64-bit integers in Qt applications. QVariant supports Q_LLONG and Q_ULLONG, eliminating the need to convert to or from a string when accessing 64-bit fields in an SQL database. QDataStream and QString have also had their interfaces extended to support the 64-bit integer types.

Changing Modality

A setModal(bool) function has been added to the QDialog class. This effectively renders the QDialog constructor's bool parameter unnecessary. When you write your own QDialog subclasses, you can simply provide a standard QWidget-style constructor like

    MyDialog(QWidget *parent = 0, const char *name = 0);
    

If you need to make the dialog modal, you can either call exec() (which always pops up a modal dialog) or you can call setModal(true) and then call show(). The latter approach gives a dialog that is sometimes referred to as "semi-modal".

About Qt

The QMessageBox::aboutQt() static function provides an About box for the Qt library. Qt 3.2 now provides a QApplication::aboutQt() slot, which can be used directly as the target of an "About Qt" action:

    aboutQtAct = new QAction(tr("About &Qt"), 0, this);
    connect(aboutQtAct, SIGNAL(activated()), qApp, SLOT(aboutQt()));
    

This is more convenient than having to define an aboutQt() slot yourself and calling the static function QMessageBox::aboutQt() from there.

Apart from showing that you use a quality tool, the "About Qt" box also shows which version of Qt the application is linked against, which may be useful for support and debugging.

Smarter Actions

The QAction class now has new constructors that don't require you to repeat the name of the action. Old-style:

    openAct = new QAction(tr("Open"), tr("&Open..."), tr("Ctrl+O"), this);
    

New-style:

    openAct = new QAction(tr("&Open..."), tr("Ctrl+O"), this);
    

QAction is smart enough to derive the tooltip text from the menu text, by removing the ampersand ('"&"') and the trailing dots ("..."). The new style won't work perfectly in some languages, such as Japanese; for these cases use setToolTip().

Executable Location

QApplication's applicationDirPath() and applicationFilePath() functions return the application executable's directory and file name (including the full path). This can be used for locating images or other files related to the application.
New Features in the SQL Module

The SQL module now includes an IBM DB2 driver (called QDB2), in addition to the Microsoft SQL Server, MySQL, Oracle, PostgreSQL, Sybase Adaptive Server, and ODBC drivers.

The QSqlError class now has a showMessage() function, which pops up a QMessageBox with the database and driver error texts.

The QSqlCursor class makes it easy to display the contents of a database table and to populate editable forms. Prior to Qt 3.2, if you wanted to display the results of a JOIN, you either had to subclass QSqlCursor or create a database view and tie the QSqlCursor to the view. The new QSqlSelectCursor class provides a more convenient solution. It can be used to display the results of any SQL SELECT statement, including those involving joins. By default the class provides read-only access to the result set, but it is possible to implement INSERT, UPDATE, and DELETE by reimplementing the appropriate virtual functions.

Compiling SQL Drivers into Applications

It is now possible to compile your own SQL drivers into your applications, rather than having to make them into plugins. Once the driver is compiled in, you must make it known to QSqlDatabase, since this class obtains its list of valid drivers by scanning the plugins paths for SQL drivers and knows nothing about compiled in drivers. Adding your driver is a simple one-liner:

    QSqlDatabase::registerSqlDriver( "MYDRIVER", new QSqlDriverCreator<MyDriver>());
    

Now MYDRIVER can be used like any other Qt database plugin.

Parameterized Queries

Qt 3.1 included support for IN parameters via its value-binding mechanism. Qt 3.2 completes the support for parameters by providing IN, OUT, and INOUT parameters. This is especially useful for use with stored procedures.

    QSqlQuery query;
    query.prepare("CALL GET_NEXT_SEQ_ID(:table, :seqid)");
    query.bindValue(":table", "INVOICES");
    query.bindValue(":seqid", 0, QSql::Out);
    query.exec();
    int sequenceNumber = query.boundValue(":seqid");
    

The :table variable is bound as a QSql::In variable (the default); bindValue() also supports QSql::InOut.

Secure SQL

Communication between client applications and the database server is unencrypted. To provide security it is necessary to ensure that the network traffic is routed via secure channels. Now it is also possible to make use of encrypted communications via SSL, for those databases that support it.

Encryption must be activated before the QSqlDatabase::open() call is made to establish the connection. Here's how we would create an encrypted connection using MySQL:

    QSqlDatabase* db = new QSqlDatabase("QMYSQL3");
    // Set username, host, password, etc.
    db->setConnectOptions("CLIENT_SSL");
    if (!db->open()) {
        // Fallback to unencrypted connection
        db->setConnectOptions("");
        db->open();
        if (!db->open())
            // Cannot connect at all
    }
    

Encryption is switched on by using the CLIENT_SSL MySQL connection option. Connection options are cleared by calling setConnectOptions(""). Unfortunately, these options are database-specific. For example, to achieve secure communication to a PostgreSQL server we must use a different option entirely:

    db->setConnectOptions("requiressl=1");
    

Multiple parameters must be concatenated with a semi-colon. For example, here's how to enable both encryption and compression for a MySQL server connection:

    db->setConnectOptions("CLIENT_SSL;CLIENT_COMPRESS");
    

An overview of the available connection options is given in QSqlDatabase's class documentation.


[1] The reason why show() isn't called from the QSplashScreen constructor is that it indirectly calls the virtual function drawContents(), which subclasses of QSplashScreen might reimplement. When we call a virtual function from a constructor in C++, the implementation that gets invoked is the class's own, not the subclass's. This happens because the object's virtual table isn't completely initialized yet.


This document is licensed under the Creative Commons Attribution-Share Alike 2.5 license.

Copyright © 2003 Trolltech Trademarks Mini-Quiz Answers »