Journal Articles

CVu Journal Vol 17, #2 - Apr 2005 + Programming Topics
Browse in : All > Journals > CVu > 172 (12)
All > Topics > Programming (877)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: Using Qt's Non-GUI Classes

Author: Administrator

Date: 03 April 2005 13:16:11 +01:00 or Sun, 03 April 2005 13:16:11 +01:00

Summary: 

In the fifth installment of our series on cross-platform programming with the Qt 3 C++ toolkit, we are going to review Qt's non-GUI classes.

Body: 

In the fifth installment of our series on cross-platform programming with the Qt 3 C++ toolkit, we are going to review Qt's non-GUI classes. Although Qt is fundamentally a GUI toolkit, it contains many non-GUI-related classes that are useful when writing portable applications. Here we'll focus on Qt's support for networking, database access, inter-process communication, XML handling and multithreading.

Network Classes

Qt provides a set of classes for writing cross-platform TCP/IP clients and servers. The most important classes are QHttp, QFtp, QSocket, QSocketServer and QSocketDevice.

QHttp and QFtp implement the client side of the HTTP and FTP protocols. HTTP (Hypertext Transfer Protocol) is an application-level network protocol used mainly for downloading HTML and XML files, but it is also used as a high-level transport protocol for other types of data. For example, HTTP is often used for transferring purchase orders over the Internet. In contrast, FTP (File Transfer Protocol) is a protocol used almost exclusively for browsing remote directories and transferring files. (see Figure 1.)

HTTP and FTP protocol stacks

Figure 1. HTTP and FTP protocol stacks

Since the two protocols are used to solve similar problems, the QHttp and QFtp classes have many features in common:

Non-blocking behaviour.

QHttp and QFtp are asynchronous. You can schedule a series of commands (also called "requests" for HTTP). The commands are executed later, when control goes back to Qt's event loop. (Blocking behaviour is usually unacceptable in a GUI application, because it can freeze the user interface for some time.)

Command IDs.

Each command has a unique ID number that you can use to follow the execution of the command. For example, QFtp emits the commandStarted() and commandFinished() signal with the command ID for each command that is executed. QHttp has requestStarted() and requestFinished() signals that work the same way.

Data transfer progress indicators.

QHttp and QFtp emit signals whenever data is transferred. You can connect these signals to a progress bar's setProgress() slot.

For example, let's assume we have a class called Downloader that has a QHttp data member called http and a QFile member called outFile. To keep the example simple, we'll do everything in the constructor and in a slot called httpDone().

Downloader::Downloader() {
  outFile.setName("bookreviews.html");
  if(!outFile.open(IO_WriteOnly))
    return;
  connect(&http, SIGNAL(done(bool)),
          this, SLOT(httpDone(bool)));
  http.setHost("www.accu.org");
  http.get("/bookreviews/public/", &outFile);
  http.closeConnection();
}

void Downloader::httpDone(bool error) {
  if (error)
    ...
  outFile.close();
}

In the constructor, we open the output file. Then we connect QHttp's done() signal to our httpDone() slot. Finally we schedule three HTTP requests: "set host", "get", and "close connection". These requests will be executed at some point in the future, when control returns to Qt. When all three requests have been processed, the httpDone() slot is called to close the file.

If you need TCP-based protocols other than HTTP and FTP (e.g., POP3, SMTP, NNTP or any proprietary protocol), you can use the QSocket class to implement your own protocol. TCP (Transmission Control Protocol) is a low-level network protocol used by most Internet protocols, including HTTP and FTP, for data transfer. It is a reliable, stream-oriented, connection-oriented transport protocol. It is especially well suited for continuous transmission of data.

An alternative to TCP is UDP (User Datagram Protocol). UDP is a lightweight, unreliable, datagram-oriented, connectionless protocol. It can be used when reliability isn't important. For example, a server that reports the time of day could choose UDP. If a datagram with the time of day is lost, the client can simply make another request. UDP is supported through the QSocketDevice class.

TCP and UDP protocols

Figure 2. TCP and UDP protocols

Database Classes

The Qt database classes provide a multiplatform interface for accessing SQL databases. Qt includes native drivers for Oracle, Microsoft SQL Server, Sybase Adaptive Server, PostgreSQL, MySQL, ODBC and SQLite. The drivers work on all platforms supported by Qt and for which client libraries are available. Programs can access multiple databases using multiple drivers simultaneously.

Programmers can easily execute any SQL statements. Qt also provides a high-level C++ interface that programmers can use to generate the appropriate SQL statements automatically.

The QSqlQuery class is used to directly execute any SQL statement. It is also used to navigate the result sets produced by SELECT statements.

In the example below, a query is executed, and the result set navigated using QSqlQuery::next():

QSqlQuery query("SELECT id, surname FROM staff");
while(query.next()) {
  cout << "id: " << query.value(0).toInt()
       << " surname: " << query.value(1).toString()
       << endl;
}

Field values are indexed in the order they appear in the SELECT statement. QSqlQuery also provides the first(), prev(), last() and seek() navigation functions.

INSERT, UPDATE and DELETE are equally simple. Below is an UPDATE example:

QSqlQuery query("UPDATE staff SET salary = salary"
           " * 1.10 WHERE id > 1155 AND id < 8155");
if(query.isActive()) {
  cout << "Pay rise given to "
       << query.numRowsAffected()
       << " staff" << endl;
}

Qt's SQL module also supports value binding and prepared queries, for example:

QSqlQuery query;
query.prepare("INSERT INTO staff (id, surname,
                                  salary)"
              " VALUES (:id, :surname, :salary)"

query.bindValue(":id", 8120);
query.bindValue(":surname", "Bean");
query.bindValue(":salary", 29960.5);
query.exec();

Value binding can be achieved using named binding and named placeholders (as above), or using positional binding with named or positional placeholders. Qt's binding syntax works with all supported databases, either using the underlying database support or by emulation.

For programmers who are not comfortable writing raw SQL, the QSqlCursor class provides a high-level interface for browsing and editing records in SQL tables or views without the need to write SQL statements. For example:

QSqlCursor cur("staff");
while(cur.next()) {
  cout << "id: " << cur.value("id").toInt()
       << " surname: "
       << cur.value("surname").toString() << endl;
}

QSqlCursor also supports the ordering and filtering that are achieved using the ORDER BY and WHERE clauses in SQL statements.

Database drivers usually supply data as strings, regardless of the actual datatype. Qt handles such data seamlessly using the QVariant class. Database drivers can be asked about the features they support, including query-size reporting and transactions. The transaction(), commit() and rollback() functions can be used if the database supports transactions.

Qt also provides classes that make it easy to present data from the database to the user. One of them is QDataTable, a table widget that displays records from a result set (see Figure 3). QDataTable supports in-place editing. Records can be updated and deleted without writing any code. Insertions require some code since most database designs expect new records to be created with a unique key.

QDataTable

Figure 3. QDataTable

Qt also includes QDataBrowser and QDataView to display records as forms (see Figure 4), typically with one or perhaps a few records shown at a time. These classes provide buttons with ready-made connections for navigating through the records. QDataView is used for read-only data. QDataBrowser is used for editing, and can provide ready-made insert, update and delete buttons.

QDataBrowser

Figure 4. QDataBrowser

Inter-Process Communication

The QProcess class is used to start external programs, and to communicate with them from a Qt application in a platform-independent way. Communication is achieved by writing to the external program's standard input stream and by reading its standard output and standard error.

QProcess works asynchronously, reporting the availability of data by emitting Qt signals. We can connect to the signals to retrieve and process the data, and optionally respond by sending data back to the external program.

XML Classes

Qt's XML module provides a SAX parser and a DOM parser, both of which read well-formed XML and are non-validating. The SAX (Simple API for XML) implementation follows the design of the SAX2 Java implementation, with adapted naming conventions. The DOM (Document Object Model) Level 2 implementation follows the W3C recommendation and includes namespace support.

Many Qt applications use XML format to store their persistent data. The SAX parser is used for reading data incrementally and is especially suitable both for simple parsing requirements and for very large files. The DOM parser reads the entire file into a tree structure in memory that can be traversed at will.

Multithreaded Programming

GUI applications often use multiple threads: one thread to keep the user interface responsive, and one or many other threads to perform time-consuming activities such as reading large files and performing complex calculations. Qt can be configured to support multithreading, and provides four central threading classes: QThread, QMutex, QSemaphore and QWaitCondition.

To create a thread, subclass QThread and reimplement its run() function. For example:

class MyThread : public QThread {
protected:
  void run();
};

void MyThread::run() {
  ... // thread code goes here
}

Then, create an instance of the thread object and call QThread::start(). The code that appears in the run() reimplementation will then be executed in a separate thread.

The QMutex class provides a means of protecting a variable or a piece of code so that only one thread can access it at a time, preventing memory corruption, crashes or other race conditions. The class provides a lock() function that locks the mutex. If the mutex is unlocked, the current thread seizes it immediately and locks it; otherwise, the current thread is blocked until the thread that holds the mutex unlocks it. Either way, when the call to lock() returns, the current thread holds the mutex until it calls unlock(). For example:

QString globalString;
QMutex mutex;
...
mutex.lock();
globalString += "More text\n";
mutex.unlock();

QSemaphore and QWaitCondition are two other classes that make it possible to synchronize threads.

This article presented a short overview of some of the most interesting non-GUI classes offered by Qt. There are many more classes than we presented here. For example, Qt has a Unicode 16-bit QString data type, classes for performing text and binary I/O, classes for supporting internationalisation, and more.

Notes: 

More fields may be available via dynamicdata ..