Walkthrough: A simple QFont demonstration

Qt 3.0.5

Home | All Classes | Main Classes | Annotated | Grouped Classes | Functions

Walkthrough: A simple QFont demonstration

The following walkthrough will show you how to make use of the font setting and manipulation techniques provided by QFont.

In addition it will show some aspects of widget layout -- if you prefer leaving this job to Qt Designer simply skip the relevant paragraphs. Moreover, if you have asked yourself how to add strings to a QStringList and how to step through its members, you will know after reading this walkthrough, or skipping to the relevant explanations in Viewer::showFontInfo() and Viewer::setFontSubstitutions().

To get the most out of the walkthrough you should at least be familiar with signals and slots.

The example program consists of a widget containing two QTextViews side by side. The one on the left shows greetings in English, Russian, and Hebrew. The one on the right shows some information about the fonts used to draw these greetings. Three push buttons in the bottom of the main window change the font used to display the greetings.

Note that the fonts and font characteristics in the example have been chosen for demonstration purposes only -- in a real world application they would rather count for bad design.

The API of the custom widget

The widget used in this example is a custom widget named Viewer.

    #include <qwidget.h>
    #include <qfont.h>

As we derive it from QWidget we include the relevant header file. Additionally we use a QFont object as a function argument, and therefore include the QFont class.

    class QTextView;
    class QPushButton;

Furthermore we declare the use of the QTextView and the QPushButton classes for class variables (we don't need to include them at this stage yet because we only use pointers to these classes).

    class Viewer : public QWidget
    {
    Q_OBJECT

The Viewer widget will have slots, so don't forget to add the Q_OBJECT macro.

    public:
        Viewer();

As we won't use more than one instance of this class there is no need for any complex constructors, a simple one without any arguments should be sufficient.

    private slots:
        void setDefault();

As previously mentioned we're going to have three push buttons. When the user clicks on them, something should happen. Thus we define one slot that sets the font in the greeting window back to the default, ...

        void setSansSerif();

... one that switches to a sans serif font, and ...

        void setItalics();

... one that shows the greetings in italics.

    private:
        void setFontSubstitutions();

Will will write the greetings using different alphabets. For users who don't have Unicode fonts installed we want to tell the application to try to exchange missing characters in one font with appropriate characters from other fonts. QFont does such font substitutions on its own but with this helper function we can define our preferred substitution pattern.

        void layout();

The task of putting the buttons and text views together we will put into a separate layout() function. This will make the code easier to understand and read.

        void showFontInfo( QFont & );

The last private function reveals font information in the text view on the right.

        QTextView * greetings;
        QTextView * fontInfo;

        QPushButton * defaultButton;
        QPushButton * sansSerifButton;
        QPushButton * italicsButton;
    };

Last but not least we define the elements of our GUI as private class variables.

The implementation of the Viewer widget

Now we will implement the Viewer class.

    #include "viewer.h"
    #include <qstring.h>
    #include <qstringlist.h>
    #include <qtextview.h>
    #include <qpushbutton.h>
    #include <qlayout.h>

First we include the relevant header files -- obviously the header of the Viewer class itself, of the QPushButton and QTextView widgets, and of the QString and QStringList classes. qlayout.h provides classes for horizontal and vertical layout and will be used in the layout() function.

    Viewer::Viewer()
           :QWidget()
    {

As already mentioned the finger print of the Viewer constructor is as simple as possible, without any arguments, derived from the QWidget default constructor.

        setFontSubstitutions();

First we define the font substitutions -- for clarity reasons we do this in a separate function.

        QString greeting_heb = QString::fromUtf8( "\327\251\327\234\327\225\327\235" );
        QString greeting_ru = QString::fromUtf8( "\320\227\320\264\321\200\320\260\320\262\321\201\321\202\320\262\321\203\320\271\321\202\320\265" );

The Hebrew and the Russian greeting we have readily available as UTF8 encoded strings. To use them in a QString we "import" them with QString::fromUtf8().

        QString greeting_en( "Hello" );

Dor the English greeting we use a simple QString.

        greetings = new QTextView( this, "textview" );

Now we create the first widget as a child of this widget, the QTextView with the identity name textview that shows the greetings.

        greetings->setText( greeting_en + "\n" +
                           greeting_ru + "\n" +
                           greeting_heb );

Now we set the text shown by greetings to the three greetings.

        fontInfo = new QTextView( this, "fontinfo" );

The second text view we call fontinfo and create it as a child of this Viewer widget.

        setDefault();

Using the setDefault() function we apply the initial font to the greetings greetings and fill the fontInfo textview with information about the font used.

        defaultButton = new QPushButton( "Default", this,
                                                       "pushbutton1" );

Now we create the first of the three push buttons -- the one that changes the font to the initial one -- with the label Default.

        defaultButton->setFont( QFont( "times" ) );

The label should be printed in a member font of the Times family. In the unlikely case that the user does not have installed a matching font, QFont is responsible in finding a replacement. Note that case-sensitivity is no issue when specifying the font family.

As we don't explicitly request a font size or weight, QFont tries to find a default 12 pt font with normal boldness.

        connect( defaultButton, SIGNAL( clicked() ),
                 this, SLOT( setDefault() ) );

In order to make something happening when the user clicks the defaultButton, we connect the QPushButton::clicked() signals issued from it to the Viewer's setDefault() slot.

        sansSerifButton = new QPushButton( "Sans Serif", this,
                                                         "pushbutton2" );
        sansSerifButton->setFont( QFont( "Helvetica", 12 ) );

The newly created second button is labelled Sans Serif in a 12 pt Helvetica font. Again if this is not possible because the requested font is not available on the system, QFont deals with it and finds a replacement.

        connect( sansSerifButton, SIGNAL( clicked() ),
                 this, SLOT( setSansSerif() ) );

We connect the clicked() signal of the sansSerifButton to the setSansSerif() slot.

        italicsButton = new QPushButton( "Italics", this,
                                                       "pushbutton3" );
        italicsButton->setFont( QFont( "lucida", 12, QFont::Bold, TRUE ) );

italicsButton, the last push button, is labelled Italics. This time we specify even more characteristics of the label font. We wish it to be a 12 pt bold member of the Lucida family. Also it should be in italics, indicated by the fourth QFont argument being TRUE.

        connect( italicsButton, SIGNAL( clicked() ),
                 this, SLOT( setItalics() ) );

Again, the italicsButton's clicked() signal is connected to a slot of this Viewer object setItalics().

        layout();
    }

Finally we arrange all five child widgets of this main window nicely using layout().

    void Viewer::setDefault()
    {
        QFont font( "Bavaria" );

For demonstration purposes on how the QFont substitution works we use a non-existant font family, Bavaria, as the default font for the greetings.

        font.setPointSize( 24 );

This font should have a size of 24 points, ...

        font.setWeight( QFont::Bold );

... it should be bold, ...

        font.setUnderline( TRUE );

... and the text written should be underlined.

        greetings->setFont( font );

Now we ask the greetings widget to use the font font.

As a member of the Bavaria font family is unlikely to be installed on your machine, run the program and observe how QFont finds a substitute. Later on we will define custom substitutions for Bavaria in the setFontSubstitutions() function.

        showFontInfo( font );
    }

Finally we use the function showFontInfo() to display appropriate information about the current font and how it maybe differs from the one requested.

    void Viewer::setSansSerif()
    {
        QFont font( "Newyork", 18 );

The slot to change the greeting font to sans serif is quite similar to setDefault(). Here we save a line of code and define the (non-existant) font family (NewYork) and size (18 points) at once.

        font.setStyleHint( QFont::SansSerif );

We use a style hint to ask QFont for a sans serif font (SansSerif is a member of the QFont::StyleHint enumeration).

As a member of the NewYork family is quite unlikely to be installed on your computer, QFont will try to follow the style hint and the font size and use this information to find a replacement font.

        greetings->setFont( font );

Finally we apply the requested font to the content of the greetings textview ...

        showFontInfo( font );
    }

... and display the appropriate font information in the fontInfo textview.

    void Viewer::setItalics()
    {
        QFont font( "Tokyo" );
        font.setPointSize( 32 );
        font.setWeight( QFont::Bold );
        font.setItalic( TRUE );

The setItalics() slot changes the greetings' font to a 32 pt bold and italic member of the (again non-existant) Tokyo family. Note that setFontSubstitutions() defines a substitution family for Tokyo.

        greetings->setFont( font );

We set the font of the greetings textview to font, and ...

        showFontInfo( font );
    }

... display the appropriate font information in the fontInfo textview.

    void Viewer::showFontInfo( QFont & font )
    {

Now, how do we show the font information?

        QFontInfo info( font );

First we obtain information about the font that is actually used when the font font is required, and store it in info.

        QString messageText;
        messageText = "Font requested: \"" +
                      font.family() + "\" " +

Then we start compiling the message that we want to show in the fontInfo textview. First, we print out the requested font family name. As we want to frame the family name with quotation marks, we have to escape the " character so that it is not confused with the C++ quotation marks used to terminate strings.

                      QString::number( font.pointSize() ) + "pt<BR>" +

We obtain the requested font size in points and convert it to a QString using QString::number(). Using <BR>; we add a rich-text linebreak to the messageText string.

                      "Font used: \"" +

After we have displayed information about the required font we want to contrast it with the one actually used. This is stored in the QFontInfo info variable.

                      info.family() + "\" " +

First we display the font family, ...

                      QString::number( info.pointSize() ) + "pt<P>";

... and then we append the actual font size, converted to a QString, to the message string. The unit abbreviation and a rich-text paragraph (<P>) follow.

If custom substitutions are available for the requested font, we're going to show them as well:

        QStringList substitutions = QFont::substitutes( font.family() );

First we store the entire list of substitutes in a string list.

        if ( ! substitutions.isEmpty() ){

If it contains at least one substitute ...

            messageText += "The following substitutions exist for " + \
                           font.family() + ":<UL>";

... we say so in the messageText, ...

            QStringList::Iterator i = substitutions.begin();

... and prepare ourselves to step through the list. For this purpose we set the list iterator i to the first list member of the substitutions string list.

            while ( i != substitutions.end() ){

As long as we haven't reached the last list member ...

                messageText += "<LI>\"" + (* i) + "\"";

we add a bullet list entry (<LI>) of the current list member (i.e. the font family name of the substitute), ...

                i++;
            }

... and move the iterator one step further.

             messageText += "</UL>";

Finally we add the end-of-bullet-list rich-text tag to the messageText string.

        } else {
            messageText += "No substitutions exist for " + \
                           font.family() + ".";
        }

If the substitution list was empty, we make a note about it in the messageText string.

        fontInfo->setText( messageText );
    }

Now that we have the messageText string ready we enter it into the fontInfo textview.

    void Viewer::setFontSubstitutions()
    {

With this function we finally reveal the secret of how to define custom substitutions for a font family.

        QStringList substitutes;

All we need is a string list.

        substitutes.append( "Times" );
        substitutes +=  "Mincho",
        substitutes << "Arabic Newspaper" << "crox";

In a real world application you will probably stick to one of the above methods to add strings to a string list. Here all possible ones are outlined to give you an overview.

After these append operations substitutes consists of four members: Times, Mincho, Arabic Newspaper, and Crox in this order. These are the font families that in the first place are searched for characters the base font does not provide.

        QFont::insertSubstitutions( "Bavaria", substitutes );

In Viewer objects, these four families provide a fallback for the Bavaria font family requested by the setDefault() slot.

        QFont::insertSubstitution( "Tokyo", "Lucida" );
    }

For the Tokyo family used in setItalics() we provide only one substitute family, Lucida. Because it is only one and not many as for Bavaria, we use QFont::insertSubstitution() instead of QFont::insertSubstitutions().

If you usually create your GUIs using Qt Designer this walkthrough has already come to an end. If this is one of your first encounters with Qt you might however continue with the explanation of the very simple main() function.

    void Viewer::layout()
    {

This last member function of the Viewer class does not cover any more QFont details. All it does is creating a nice automatic layout for the three push buttons and the two text views.

The best solution for this task is to have the two QTextViews lined up horizontally. The same applies to the QPushButtons. Finally both of these layouts are placed together into a vertical layout container. Qt takes care of the proportions so that everything looks nice.

        QHBoxLayout * textViewContainer = new QHBoxLayout();

Let's create the first layout that aligns its members horizontally, ...

        textViewContainer->addWidget( greetings );

... and add the QTextView with the greetings, ...

        textViewContainer->addWidget( fontInfo );

... as well as the text view with the font information. fontInfo appears to the right of greetings because it was added later.

        QHBoxLayout * buttonContainer = new QHBoxLayout();

Now we create the second layout for the push buttons.

        buttonContainer->addWidget( defaultButton );

defaultButton is placed on the left hand side of the layout, ...

        buttonContainer->addWidget( sansSerifButton );

... sansSerifButton in the middle, ...

        buttonContainer->addWidget( italicsButton );

... and italicsButton on the right hand side.

Unfortunately we face a tiny problem: remember that (a highly unusual thing to do in a real world application) the labels of the three buttons are drawn in different fonts. Whilst the automatic layout accounts for the fact all three buttons have the same width, the uncommon occurrence of different character heights leads to different button heights.

To make the application window look nice we have to help it a little.

        int maxButtonHeight = defaultButton->height();
        if ( sansSerifButton->height() > maxButtonHeight )
            maxButtonHeight = sansSerifButton->height();
        if ( italicsButton->height() > maxButtonHeight )
            maxButtonHeight = italicsButton->height();

By comparing the three button heights we find the largest one and store it in maxButtonHeight.

        defaultButton->setFixedHeight( maxButtonHeight );
        sansSerifButton->setFixedHeight( maxButtonHeight );
        italicsButton->setFixedHeight( maxButtonHeight );

Now we set the height of each button to this maximum value and make sure that the automatic layout does not change it.

This was the hardest part of the entire layout process. There is one task left:

        QVBoxLayout * container = new QVBoxLayout( this );

We create a layout that arranges its members vertically.

        container->addLayout( textViewContainer );

This container layout contains the text views on top, ...

        container->addLayout( buttonContainer );

... and the button row below.

        resize( 700, 250 );
    }

Finally we set the size of the entire main window to a width of 700 pixels and a height of 250 pixels.

The main program

There is not much to say about the main program.

    #include "viewer.h"
    #include <qapplication.h>

    int main( int argc, char **argv )
    {
        QApplication app( argc, argv );
        Viewer * textViewer = new Viewer();

We create an instance of the Viewer class, ...

        app.setMainWidget( textViewer );

... make it the main widget of the application object app, ...

        textViewer->show();

... display it to the user ...

        return app.exec();
    }

... and enter the application loop. Well done, that was all for today ...

See also Step-by-step Examples.


Copyright © 2002 TrolltechTrademarks
Qt version 3.0.5