Application Development with Qt Creator(Second Edition)
上QQ阅读APP看书,第一时间看更新

Linking against our sample library

Now, let's make an application that depends on our library. Our application will call the factorial function in the library, statically linking to the library in order to access the factorial function. To accomplish this, you need to perform the following steps:

  1. Select Close All Projects and Editors from the File menu.
  2. Choose New File or Project… from the File menu and create a new Qt console application called MathFunctionsTest using the wizard.
  3. Right-click on MathFunctionsTest in the Project pane and click on Add Library.... You can now choose a library in your build tree, a library outside your build tree, an external library on your system such as the Unix math library, fftmpeg, or another library that you've created. Select External Library and click on Next.
  4. Browse the library file that was built in the previous section by clicking on Browse, next to the line labelled Library file. It'll be in a folder named something such as build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug in your project's folder. Select the MathFunctions library in either the release or debug folders—it doesn't matter which. The dialog box should look something similar to the following screenshot:
  5. Browse the include files for your library by clicking on Browse next to Include path; this is the directory where you put the headers for your library.
  6. Choose static linking.
  7. Leave the other values set to their default values, click on Next, and then click on Finish.

Qt Creator will work its magic with your .pro file, adding a LIBS variable that includes the output of your library's build and an include path to your library's header files.

We can now call our factorial function. Edit main.cpp to read the following code:

#include <QCoreApplication>
#include "mathfunctions.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    qDebug("6! is %lu", MathFunctions::factorial(6));
    return a.exec();
}

This code first includes our library header file. Note that if you compile the application after adding just the #include declaration, you'll get autosuggest help for every element of the MathFunctions library. This code uses qDebug instead of the C standard library to process its console output.

Tip

qDebug() actually has a stream-savvy implementation too. I could have written the qDebug line as follows:

qDebug() << "6! is" << MathFunctions::factorial(6);

The code would have generated the same output. To do this, you'll need to be sure to include the line #include <QDebug>.

Build and run the application now in the Debug mode; you should see a console window with the text 6! is 720. Now, try to build and run the library in the Release mode… wait, why is the debugging output from qDebug still there?

qDebug isn't really a debugging log; it's an output stream for debugging information regardless of build levels. If you want to turn off its output in release builds, you'll need to edit the .pro file. Double-click on your .pro file and add the following line:

CONFIG(release, debug|release): DEFINES += QT_NO_DEBUG_OUTPUT

This is another scope; it says that if your build configuration is release, add the QT_NO_DEBUG_OUTPUT preprocessor definition to the list of preprocessor definitions for the project.

Now, if you rebuild (you don't just choose build, but actually choose rebuild because you want a clean build through the entire system) and run in the release mode, you won't see any output.

Tip

Qt actually defines four output streams, one for debugging messages and one for bonafide warnings. Use qDebug for regular logging and qWarning to output messages of a higher priority. There's also qCritical and qFatal for high-priority log messages that will indicate critical failures or failures that cause the application to terminate. You can also turn off warnings in release builds in the same way; simply add the following to your .pro file:

CONFIG(release, debug|release): DEFINES += QT_NO_WARNING_OUTPUT

What will you do if you want to add files to your project? You can either do this by manually editing the .pro file—which can be faster if you're a good typist, but it is also error-prone and can result in weird build problems if you mess up—or by right-clicking on your project and selecting either Add New… or Add Existing Files…. The Add New… option opens up a short wizard with choices such as these:

  • C++ header and source files
  • Qt Designer forms, which we'll talk about in the next chapter
  • Qt resource files, which we'll talk about in the next chapter
  • Qt Meta-object Language (QML) files
  • JavaScript files (which can contain the code implementing the logic of a Qt Quick application)
  • OpenGL shaders for fragments or vertices in either full OpenGL or OpenGL/ES
  • Text files (such as a Readme file for your project) or a scratch file to use as a place to stash temporary clipboard items until you're done with an editing session

Before we move on to the important topic of debugging, let's take a look at one more .pro file: the .pro file for our application:

#-------------------------------------------------
#
# Project created by QtCreator 2013-07-23T20:43:19
#
#-------------------------------------------------

QT       += core

QT       -= gui

CONFIG(release, debug|release):
DEFINES += QT_NO_DEBUG_OUTPUT

TARGET = MathFunctionsTest
CONFIG   += console
CONFIG   -= app_bundle

TEMPLATE = app

SOURCES += main.cpp
win32:CONFIG(release, debug|release): LIBS += -L$$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/release/ -lMathFunctions
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/debug/ -lMathFunctions
else:unix: LIBS += -L$$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/ -lMathFunctions

INCLUDEPATH += $$PWD/../MathFunctions
DEPENDPATH += $$PWD/../MathFunctions

win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/release/libMathFunctions.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/debug/libMathFunctions.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/release/MathFunctions.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/debug/MathFunctions.lib
else:unix: PRE_TARGETDEPS += $$PWD/../build-MathFunctions-Desktop_Qt_5_3_0_MinGW_32bit-Debug/libMathFunctions.a

Phew! That's pretty dense. Let's see if we can unravel it. It begins by telling the build system that we use QtCore, but not QtGui. Next up is the instruction to disable the qDebug messages in release builds, which won't happen by default. The TARGET, CONFIG, and TEMPLATE options together say that we're building a console application with the name MathFunctionsTest. The next line indicates that we have one source file, main.cpp.

The next set of scopes indicate the path to our library and handle the fact that our libraries are in different directories on Windows for release and debug—this is different from Unix systems, where there is only one build variant of the library. After this come the INCLUDEPATH and DEPENDPATH variables, which indicate that there are library headers in the MathFunctions directory and that the application depends on those headers; so, if the timestamps on the headers change, the binary should be rebuilt.

The final scope specifies the same dependency on the output library itself; if the library changes, the application executables have to be rebuilt. This is especially important because that way, we can run multiple copies of Qt Creator, edit our library and application files separately, and rebuild the bits we need either after they change, as well as all the dependencies get figured out and the right bits get built automatically.