Projet Informatique

Année universitaire 2002 - 2003




Utilisation de la bibliothèque Qt pour faire du graphisme en C


SAULNIER Gwénolé
PFLIEGER Guillaume
DESS Technologies et Stratégies Industrielles

Tuteur ULP : TRAU Patrick

SOMMAIRE

1 Introduction

2 Fonctions de base

2.1 La classe QApplication

2.2 La classe QPushButton

2.3 Créer une QApplication

2.4 Gérer des évènements

2.5 Exemple commenté

2.6 Compilation

3 Fonctions graphiques

3.1 Généralité sur le dessin

3.2 Quelques fonctions graphiques de bases

3.3 Exemple d'application

4 Problèmes rencontrés

5 Conclusion

6 Références bibliographiques


1Introduction

Le présent document a été établi à la suite du projet informatique effectué durant le DESS TSI. Le sujet porte sur l'utilisation de la bibliothèque Qt pour faire du graphisme en C. Ce document est un recueil des bases de la programmation avec Qt. Il présente quelques fonctions simples ainsi que leur mise en application sur des machines de l'IPST (système LINUX).

Qt est une bibliothèque graphique écrite en C++, c'est-à-dire un ensemble de classes permettant de développer l'interface graphique d'un programme C++. Elle est développée par la société norvégienne Troll Tech. Qt est portable (utilisable à la fois sur les systèmes UNIX et Windows) et est utilisée dans le développement de nombreuses applications. KDE (K Desktop Environment), l'environnement graphique de bureau utilisé en particulier sous Linux, a été développé avec Qt.

Une application Qt est une application graphique qui va interagir avec l'utilisateur de cette application. Le concept de base d'une application graphique est le widget. Un widget est l'entité de base d'une interface graphique qui réagit aux actions d'un utilisateur : manipulation de la souris, du clavier et tout autre évènement du système graphique. Les widgets sont de formes rectangulaires et sont organisés hiérarchiquement. Un widget particulier appelé widget principal ou widget racine se trouve au sommet de cette hiérarchie.

Dans ce document nous présenterons dans un premier temps les bases de Qt. Puis nous aborderons quelques fonctions simples et essentielles au dessin. Enfin, au travers d'un exemple commenté, nous mettrons en applications ces quelques fonctions.

2Fonctions de base

2.1La classe QApplication

Du point de vue du programmeur, une application Qt est un objet de la classe QApplication. C'est cet objet qui se charge de la gestion des évènements d'une application donnée : c'est la classe centrale de Qt qui reçoit de l'environnement graphique des évènements et qui les transmet à un objet graphique particulier (widget).

Chaque programme ne comporte qu'un objet de type QApplication et la variable globale qApplication fait référence à cet objet (objet défini par extern QApplication *qApplication).

Les widgets de Qt sont des objets de la classe QWidget.

Toute application Qt est constituée d'un objet de type QApplication. Le fichier qapplication.h contient les déclarations de la classe QApplication. On inclura dans les programmes utilisant Qt, le fichier de déclaration de la classe QApplication.

#include <qapplication.h>

2.2La classe QPushButton

Le fichier qapplication.h ne contient pas tous ce dont une application Qt a besoin. Il est souvent nécessaire d'inclure d'autres fichiers d'interface, selon les besoins de l'application que l'on développe. Par exemple, si l'on veut utiliser d'autres objets graphiques, un bouton par exemple, on devra inclure le fichier qpushbutton.h.

#include <qpushbutton.h>

Un bouton est un objet de la classe QPushButton et possède par défaut un aspect prédéfini. Il existe encore d’autres formes de bouton. Ces dernières ont été employées dans le grand exemple commenté vers la fin du rapport.

2.3Créer une QApplication

Une application Qt commence toujours par la création d'un objet de la classe QApplication de la manière QApplication nom_de_l'apli (argc, argv );

Exemple

On a un objet application (nommé "a"), un widget (nommé "hello"). Le widget est affecté à l'application, puis l'application exécutée.

#include <qapplication.h>
#include <qpushbutton.h>
int main( int argc, char **argv )
{
    QApplication a( argc, argv );
    QPushButton hello( "Hello DESS TSI", 0 );
    hello.resize( 300, 30 );  //taille de la fenêtre
    a.setMainWidget( &hello );
    hello.show();
    return a.exec();
}

2.4Gérer des évènements

Qt propose un système d'appels entre objets original. Chaque objet Qt peut émettre des signaux, et peut être doté de slots. Le lien entre un signal et un slot se fait à l'aide de la simple instruction connect.

Par exemple, l'objet standard QButton est doté d'un signal nommé clicked() qui est déclenché lorsque l'utilisateur clique sur le bouton (soit à l'aide de la souris, soit en le validant au clavier, peu importe). De même, la fonction quit() de l'objet standard QApplication est en fait un slot. Il nous suffit donc d'ajouter juste avant return a.exec() la ligne de code suivante :

QObject : :connect( &hello, SIGNAL(clicked()), &a, SLOT(quit()) ) ; 

Le système de communication établi en Qt est basée sur cette notion d'émission et réception de signaux. L'objet émetteur du signal n'a pas à se soucier de l'existence d'un objet susceptible de recevoir le signal émis. Ce qui permet une très bonne encapsulation et le développement totalement modulaire. Un signal peut être connecté à plusieurs slots et plusieurs signaux à un même slot.

2.5Exemple commenté

Prenons l'exemple d'un programme permettant de créer une fenêtre dans laquelle se trouve un bouton.

#include <qapplication.h>
#include <qpushbutton.h>
#include <qfont.h>
class MyWidget : public QWidget
{   public:
    MyWidget( QWidget *parent=0, const char *name=0 );   };
MyWidget::MyWidget( QWidget *parent, const char *name )
        : QWidget( parent, name )   {
    setMinimumSize( 200, 120 );
    setMaximumSize( 200, 120 );
    QPushButton *quit = new QPushButton( "Quit", this, "quit" );
    quit->setGeometry( 62, 40, 75, 30 );
//Géométrie du bouton définie dans la fenêtre suivant (x,y,w,h)

    quit->setFont( QFont( "Times", 18, QFont::Bold ) );
//définition de la police et taille du texte
    connect( quit, SIGNAL(clicked()), qApp, SLOT(quit()) );    }
//Effectue la fonction quit en cliquant sur le bouton "quit".
int main( int argc, char **argv )
{   QApplication a( argc, argv );
    MyWidget w;
    w.setGeometry( 100, 100, 200, 120 ); 
//Géométrie de la fenêtre définie dans l'écran suivant (x,y,w,h)

    a.setMainWidget( &w );
    w.show();
    return a.exec();
  }

2.6Compilation

Les fichiers se compilent avec la commande de base suivante :

g++ (nom du fichier).cpp –o (nom du fichier)

A ceci il faut encore ajouter le chemin d'accès à la bibliothèque Qt (-lqt) ainsi qu'à de nombreuses autres bibliothèques (-lX11, -lXt, …). Pour simplifier l'opération de compilation on a créé un fichier exécutable regroupant les accès à toutes les bibliothèques. Ce fichier est nommé "c" (ou « compiler-avec-QT.bat ») est regroupe la ligne de commande suivante :

g++ $1.cpp -I /usr/lib/qt3/include -lqt -L /usr/lib/qt3/lib -lX11 -lXt -lGL -L /usr/X11/lib -lXrender -lpng -ljpeg -lXft -lXinerama -o $1

Pour compiler il suffit alors de taper la commande suivante :

c (nom du fichier)

Certains de nos fichiers ont également nécessité l'utilisation du métacompilateur (moc). Cette commande va créer un fichier (nom du fichier).moc à partir du fichier (nom du fichier).cpp ou (nom du fichier).h. La commande à utiliser est :

moc (nom du fichier).cpp –o (nom du fichier).moc

Pour que la commande s'exécute il faut indiquer l'emplacement de certains fichiers. La commande complète est :

usr / lib / qt -3.0.5 / bin / moc (nom du fichier).cpp –o (nom du fichier).moc

3Fonctions graphiques

3.1Généralité sur le dessin

Les outils de bases pour dessiner sous Qt sont :

3.1.1Le pen

Le pen ou le stylo définit les contours des formes (rectangles, ellipse…). On peut soit modifier sa forme soit sa couleur. Au cours d'un programme il est possible d'utiliser des stylos préalablement déclarés ou en spécifier un par fonction.

3.1.2Le Brush

Le brush ou brosse permet de remplir une forme suivant un style ou une couleur différents.

3.2Quelques fonctions graphiques de bases

3.2.1void QPainter::drawPoint ( int x, int y )

Dessine un point à (x, y)

3.2.2void QPainter::drawLine ( int x1, int y1, int x2, int y2 )

Dessine une ligne de (x1, y1) à (x2, y2) place le pen à (x2, y2)

3.2.3void QPainter::drawLine ( const QPoint & p1, const QPoint & p2 )

Dessine une ligne de p1 à p2.

3.2.4void QPainter::lineTo ( int x, int y )

Dessine une ligne de la position initiale du pen à (x, y) et (x, y) devient la nouvelle position du pen.

3.2.5void QPainter::moveTo ( int x, int y )

Place le pen à la position (x, y)

3.2.6void QPainter::drawRect ( int x, int y, int w, int h )

Dessine un rectangle dont le coin supérieur gauche est à (x,y) de largeur w et de hauteur h

3.2.7void QPainter::drawRoundRect ( int x, int y, int w, int h, int xRnd = 25, int yRnd = 25 )

Dessine un rectangle aux coins arrondis à (x, y), de largeur w et de hauteur h.

xRnd et yRnd définissent les dimensions des arrondis des coins.

3.2.8void QPainter::eraseRect ( int x, int y, int w, int h )

Définit un rectangle où tout est effacé dans son aire définit par x, y, w, h.

3.2.9void QPainter::drawPie ( int x, int y, int w, int h, int a, int alen )

Dessine un camembert définit par le rectangle (x, y, w, h), l’angle d’ouverture alen et l’e décalage par rapport à l'horizontale a.

Les angles a et alen sont en 1/16ième de degré, c’est à dire qu’un cercle entier vaut 5760 (16*360). a, l’angle de positionnement de l’arc, peut être positif ou négatif. Si a est positif la part sera orientée dans le sens inverse des aiguilles d’une montre.

3.2.10void QPainter::drawArc ( int x, int y, int w, int h, int a, int alen )

Dessine un arc défini par un rectangle (x, y, w, h), l’angle d’ouverture alen et l’angle de positionnement a.

3.2.11void QPainter::drawEllipse ( int x, int y, int w, int h )

Dessine une ellipse avec un centre à (x + w/2, y + h/2) et de taille (w, h).

3.2.12void QPainter::drawPixmap ( int x, int y, const QPicture & pic )

Place l’image nommé pic à (x, y).

3.2.13void QPainter::drawPolygon ( nbpoints, x1,y1,x2,y2,x3,y3…)

nbpoints définit un nombre de points définissant un polygone, x1, y1, x2, y2…les coordonnées de tous les points.

3.2.14void QPainter::drawText ( x,y,w,h,Qt :: « texte à marquer »)

x,y,w,h, définit le rectangle où sera inscrit le texte.

Ces quelques fonctions de dessin sont issues de la documentation Troll Tech sur internet.

http://doc.trolltech.com/3.1/qpainter.html

3.3Exemple d'application

#include <qmessagebox.h>
#include <qfile.h>
#include <ctype.h>
#include <qwidget.h>
#include <qpainter.h>
#include <qprinter.h>
#include <qpushbutton.h>
#include <qradiobutton.h>
#include <qbuttongroup.h>
#include <qapplication.h>
#include <math.h>
#include <qpicture.h>
#include <qpixmap.h>

void drawFonts( QPainter *p ){
//définition des pinceaux :bleu, vert peu dense,transparent, quadrillé, jaune
    QBrush b1( Qt::blue );
    QBrush b2( Qt::green, Qt::Dense6Pattern );
    QBrush b3( Qt::NoBrush );
    QBrush b4( Qt::CrossPattern );
    QBrush b5( Qt::yellow );
//texte crayon rouge
    p->setPen( Qt::red );
    p->drawText( 10, 10, 150, 70, Qt::AlignCenter, "VOICI\nQUELQUES FORMES" );
//rectangle, crayon noir, fond bleu
    p->setBrush( b1 );
    p->setPen( Qt::black );
    p->drawRect( 150, 10, 150, 100);
//rectangle arrondi, crayon noir, fond vert peu dense
    p->setBrush( b2 );
    p->drawRoundRect( 350, 10, 200, 100, 50, 50 );
//polygone, intérieur jaune
    p->setBrush( b5 );
//définition d'un groupe de points
    QPointArray a;
    a.setPoints( 6, 10,200, 160,150, 310,200, 310,300,160,350, 10, 300 );
    p->drawPolygon( a );
//Rectangle vide dans l'hexagone
    p->eraseRect (20,225, 280,50);
// Ellipse, contour vert, transparent
    p->setPen( Qt::green );
    p->setBrush( b3 );
    p->drawEllipse( 350, 200, 200, 100 );
// Partie de camembert, contour noir, quadrillage noir
    p->setPen( Qt::black );
    p->setBrush( b4 );
    p->drawPie(550, 200, 150, 150, 45*16, 90*16 );
//point
    p->drawText( 10, 400, 150, 70, Qt::AlignCenter, "trois points" );
    p->drawPoint (150,435);
    p->drawPoint (200,435);
    p->drawPoint (250,435);
//Ligne
   p->setPen( Qt::red );
   p->drawText( 10, 465, 150, 70, Qt::AlignCenter, "une ligne" );
   p->drawLine (150,500,400,500);}

void drawShapes( QPainter *p ){
//arbres
    p->setBrush( Qt::darkYellow );
    p->drawRect( 37, 130, 25, 150 );
    p->drawRect( 110, 130, 25, 150 );
    p->drawRect( 185, 130, 25, 150 );
    p->drawRect( 265, 130, 25, 150 );
    p->drawRect( 335, 130, 25, 150 );
    p->drawRect( 410, 130, 25, 150 );
    p->setBrush( Qt::green );
    p->drawEllipse( 0, 75, 110, 100 );
    p->drawEllipse( 75, 75, 110, 100 );
    p->drawEllipse( 150, 75, 110, 100 );
    p->drawEllipse( 225, 75, 110, 100 );
    p->drawEllipse( 300, 75, 110, 100 );
    p->drawEllipse( 375, 75, 110, 100 );
//route
    p->setPen( Qt::black );
    p->drawLine (0,280,700,280);
    p->drawLine (0,550,700,550);
//définition du pinceau
    QBrush brush( Qt::red, Qt::SolidPattern );
    p->setBrush( brush );
//définition de la forme de la voiture
    QPointArray a;
    a.setPoints( 5, 50,200, 350,200, 450,270, 450,400, 50,400 );
    p->drawPolygon( a );
//police d'écriture
    QFont f( "courier", 20, QFont::Bold );
    p->setFont( f );
//Définition de la couleur bleu clair
    QColor windowColor( 120, 120, 255 );        
    brush.setColor( windowColor );             
    p->setBrush( brush );                       
//définition de la fenêtre
    p->drawRect( 80, 230, 250, 80 );
//définition du texte
    p->drawText( 200, 230, 150, 70, Qt::AlignCenter, "DESS\nTSI" );
// charge une image
    QPixmap pixmap;
    if ( pixmap.load("ulp.gif"))
    p->drawPixmap( 80, 220, pixmap );
    p->setBackgroundMode( Qt::OpaqueMode );     // set opaque mode
// définition des roues
    p->setBrush( Qt::black);
    p->drawEllipse( 310, 360, 80, 80 );
    p->drawEllipse( 90, 360, 80, 80 );
    p->setBrush( Qt::CrossPattern);
    p->drawEllipse( 320, 370, 60, 60 );
    p->drawEllipse( 100, 370, 60, 60 );
//Soleil
    p->setBrush( Qt::yellow );
    p->drawEllipse( 500, 50, 150, 150 );}

typedef void (*draw_func)(QPainter*);
struct DrawThing {
    draw_func    f;
    const char  *name;};
//affichage du menu
DrawThing ourDrawFunctions[] = {
    { drawFonts,        "Formes" },
    { drawShapes,       "Application" },
    { 0,                0 } };

class DrawView : public QWidget {
    Q_OBJECT
public:
    DrawView();
    ~DrawView();
public slots:
    void   updateIt( int );
    void   printIt();
protected:
    void   drawIt( QPainter * );
    void   paintEvent( QPaintEvent * );
    void   resizeEvent( QResizeEvent * );
private:
    QPrinter     *printer;
    QButtonGroup *bgroup;
    QPushButton  *print;
    int           drawindex;
    int           maxindex; };
DrawView::DrawView() {
 //titre de la fenêtre
    setCaption( "Applications Qt Saulnier Pflieger" );
    setBackgroundColor( white );
 // créer un groupe de bouton
    bgroup = new QButtonGroup( this );
    bgroup->resize( 200, 200 );
    connect( bgroup, SIGNAL(clicked(int)), SLOT(updateIt(int)) );
 // Taille des boutons
    int maxwidth = 80;
    int i;
    const char *n;
    QFontMetrics fm = bgroup->fontMetrics();
    for ( i=0; (n=ourDrawFunctions[i].name) != 0; i++ ) {
        int w = fm.width( n );
        maxwidth = QMAX(w,maxwidth);    }
    maxwidth = maxwidth + 20;             
    for ( i=0; (n=ourDrawFunctions[i].name) != 0; i++ ) {
        QRadioButton *rb = new QRadioButton( n, bgroup );
        rb->setGeometry( 10, i*30+10, maxwidth, 30 );
        if ( i == 0 )
            rb->setChecked( TRUE );    }
    drawindex = 0;                              
    maxindex  = i;
    maxwidth += 40;                             
    printer = new QPrinter;
    // bouton imprimer
    print = new QPushButton( "Print...", bgroup );
    print->resize( 80, 30 );
    print->move( maxwidth/2 - print->width()/2, maxindex*30+20 );
    connect( print, SIGNAL(clicked()), SLOT(printIt()) );
    bgroup->resize( maxwidth, print->y()+print->height()+10 );
    resize( 700,600 ); // taille de la fenêtre  }
DrawView::~DrawView()  {
#ifndef QT_NO_PRINTER
    delete printer;
#endif  }

void DrawView::updateIt( int index )   {
    if ( index < maxindex ) {
        drawindex = index;
        update();    }  }

void DrawView::drawIt( QPainter *p )
{ (*ourDrawFunctions[drawindex].f)(p);  }

void DrawView::printIt()   {
#ifndef QT_NO_PRINTER
    if ( printer->setup( this ) ) {
        QPainter paint;
        if ( !paint.begin( printer ) )
            return;
        drawIt( &paint );    }
#endif   }

void DrawView::paintEvent( QPaintEvent * )  {
    QPainter paint( this );
    drawIt( &paint );  }

void DrawView::resizeEvent( QResizeEvent * )   {
    bgroup->move( width()-bgroup->width(), 0 );    }

#include "demo.moc"
int main( int argc, char **argv )   {
    QApplication app( argc, argv );
    DrawView   draw;
    app.setMainWidget( &draw );
    draw.setCaption("Applications Qt Saulnier Pflieger");
    draw.show();
    return app.exec();   }

4Problèmes rencontrés

La compilation de certains programmes débouche sur des erreurs récurrentes. Ces dernières surviennent dans le cas où l'on fait appel à plusieurs programmes (.cpp) ainsi que (.h).

Nous avons effectué plusieurs recherches sur internet pour tenter de remédier au problème mais ce dernier persiste.

Une méthode décrite sur le site http://www.linuxfrench.net/article.php3?id_article=535 paraît tout de même intéressante. Pour notre part, nous n'avons plus eu le temps de l'étudier en profondeur. Les premiers tests ont également aboutis sur une liste d'erreur. Toutefois, cette piste n'est pas à négliger et permettrait peut être d'obtenir les résultats escomptés. La méthode est la suivante :

moc -o mybutton.moc.cpp mybutton.h

Dans notre cas la commande moc à utiliser est définie précédemment dans le rapport.

g++ -I /home/qt/qt-2.3.1/include/ -c -o mybutton.moc.o mybutton.moc.cpp

La compilation doit pouvoir se faire en utilisant notre fichier “c”. Quelques modifications seront à apporter toutefois.

g++ -I /home/qt/qt-2.3.1/include/ -c -o mybutton.o mybutton.cpp

g++ -I /home/qt/qt-2.3.1/include/ -c -o hello.o hello.cpp

g++ -I /home/qt/qt-2.3.1/include/ -L /usr/lib/qt2/lib -lqt -o hello hello.o mybutton.o mybutton.moc.o

5Conclusion

En quelques semaines d'utilisation des bibliothèques graphiques Qt, nous avons apprécié la grande diversité des applications possibles. Nous n'avons d'ailleurs pu qu'aborder et nous familiariser qu'avec les fonctions de bases (formes simples de dessins, gestion de fenêtres).

Les fonctions abordées dans ce rapport devraient apporter quelques premiers éléments à la reprogrammation du logiciel par élément finis Mailman en utilisant les fonctionnalités de QT. Toutefois, nous n'avons pu y joindre la gestion de la souris. Ce point reste inachevé en partie à cause des problèmes de compilation que l'on a rencontré avec les programmes d'exemples disponibles sur internet.

D'autres difficultés encore nous ont empêché de progresser à notre guise. Notamment des disparités entre les différentes versions des bibliothèques Qt (v2 et v3) ainsi que la recherche de tous les éléments de bibliothèque nécessaires à une compilation des exemples de base sans erreurs.

Ce projet a été intéressant, car il nous a permis de découvrir l'utilisation de Qt., dans un langage connu : C. Enfin, le projet avait l’avantage d’être concret et les différentes applications d’être visualisées graphiquement..

6Références bibliographiques


http://www.esil.univ-mrs.fr/~tourai/Qt/


http://doc.trolltech.com/3.0/


http://www.univ-pau.fr/~artouste/fr/composants/Qt/ManuelutilisateurQt.htm


http://www.linuxfrench.net/article.php?id_article=527

http://www.linuxfrench.net/article.php3?id_article=535

http://www.linuxfrench.net/article.php?id_article=545


http://www.eyrolles.com/php.informatique/Ouvrages/liste_ouvrages.php3?noeud_id=1431090&xd=a13578bbb7057baed3daaaea34769348 (littérature dédié à la programmation Qt)


fichiers sources à télécharger :