La librairie Qt - 5
Les zones défilables

Les layouts présentés le mois dernier vous permettent d’organiser simplement et efficacement plusieurs widgets. Ce mois-ci nous allons envisager la manipulation d’un widget de grande taille, ou bien d’un grand nombre de widgets, dont seule une petite partie est visible.

1. La classe du jour : QScrollView

2. Un grand widget

3. Quelques widgets

4. Beaucoup de widgets

5. Conclusion

6. Références

codes sources

Mais avant de commencer, une information : la hiérarchie complète des classes Qt est disponible au format Postscript [1], et depuis peu (depuis que votre serviteur l’a créé à partir du PostScript) au format PDF [2]. Ces graphiques sont destinés à être imprimés sur une feuille A3, et sont bien pratiques pour se repérer. Naviguez un peu à partir de ces URLs, vous trouverez pas mal d’autres documentations intéressantes. Mais revenons à nos moutons.

1. La classe du jour : QScrollView

Lorsque vous avez un grand nombre d’informations à afficher (comme une longue page HTML), ou un objet de grande taille (comme une image), il est inévitable d’utiliser les barres de défilement. Celles-ci existent bel et bien dans Qt, elles sont proposées par la classe QScrollBar, mais construire une zone n’affichant qu’une partie d’un ensemble plus grand à partir d’elles est assez pénible. Heureusement, Qt met à notre disposition la classe QScrollView, précisément dédiée à cela. Elle s’utilise de trois manières différentes :

  1. si vous voulez afficher un widget de grande taille, jusqu’à 4000 pixels dans les deux directions ;

  2. si vous avez quelques widgets perdus au milieu de nul part, dans un cadre pouvant dépasser les 4000 pixels ;

  3. si vous avez beaucoup de widgets, là aussi dans un cadre dépassant les 4000 pixels.

L’existence de ces trois modes est justifiée par la mise en oeuvre de diverses optimisations selon la situation. Notez qu’un QScrollView est (en principe) capable de traiter un cadre dépassant les limitations du système de fenêtrage sous-jacent, c’est-à-dire en général 32000 pixels dans les deux directions.

La structure d’un QScrollView est la suivante :

Il est possible d’accéder aux différents éléments qui le composent en utilisant les méthodes mentionnées sur le dessin. viewport(), clipper() et cornerWidget() renvoient un pointeur sur un QWidget, tandis que horizontalScrollBar() et verticalScrollBar() renvoient un pointeur sur un QScrollBar (qui dérive de QWidget et de QRangeControl, comme le QSlider présenté au deuxième chapitre). Il est ainsi possible de manipuler directement ces éléments. En particuliers, vous pouvez définir votre propre widget à afficher dans le coin inférieur droit, avec la méthode setCornerWidget(QWidget * corner ).

La zone centrale hachurée est la partie visible de ce que nous voulons afficher (qui est généralement beaucoup plus vaste). C’est un widget, que l’on peut obtenir par la méthode viewport() dans les deux premières utilisations, ou par clipper() dans la troisième.


2. Un grand widget

Obtenir un grand widget n’est pas difficile, il suffit de créer une instance de QWidget et d’utiliser la méthode resize(). Pour voir quelque chose dedans, nous pourrions lui ajouter un grand nombre d’autres widgets enfants, mais je vous propose une autre méthode : dessiner directement dans le widget en utilisant les fonctions de dessin de la classe QPainter. Nous étudierons celle-ci en détail plus tard. Voici le programme de démonstration :

01: #include <qapplication.h>
02: #include <qscrollview.h>
03: #include <qwidget.h>
04: #include <qpainter.h>
05: class MyWidget : public QWidget
06: { public:
07:     MyWidget(QWidget* parent = 0) : QWidget(parent)
08:     { resize(1024,1024) ; }
09:   protected:
10:     void paintEvent(QPaintEvent*)
11:     { QPainter p(this) ;
12:       p.setPen(QPen::NoPen) ;
13:       for (int i=0 ; i<256 ; i++)
14:         for (int j=0 ; j<256 ; j++)
15:         { p.setBrush(QColor(255,i,j)) ;
16:           p.drawRect(i*4,j*4,4,4) ;
17:         }
18:     }
19: } ;
20: int main (int argc, char* argv[])
21: { QApplication app(argc, argv) ;
22:   QScrollView sv(0) ;
23:   MyWidget w(sv.viewport()) ;
24:   sv.addChild(&w) ;
25:   sv.resize(200, 200) ;
26:   app.setMainWidget(&sv) ;
27:   sv.show() ;
28:   return app.exec() ;
29: }

Le reste du code est connu. Le résultat de ce programme :

Vous pouvez naturellement redimensionner la fenêtre pour voir une plus grande proportion de notre widget personnalisé. Les barres de défilement vous permettent de naviguer dans le dégradé, qui est bien du rouge vers le violet verticalement, du rouge vers le jaune horizontalement, et du rouge vers le blanc sur la diagonale.

3. Quelques widgets

Deuxième cas d’utilisation : vous avez quelques widgets, que vous voulez dispersés dans une vaste zone. Ce cas est peut-être le moins fréquemment utilisé. Voici un exemple :

01: #include <qapplication.h>
02: #include <qscrollview.h>
03: #include <qwidget.h>
04: #include <qpainter.h>
05: class MyWidget : public QWidget
06: { public:
07:     MyWidget(QColor col, int x, int y, QWidget* parent = 0)
08:     : QWidget(parent), mCol(col)
09:    { resize(x,y) ; }
10:   protected:
11:     void paintEvent(QPaintEvent*)
12:     { QPainter p(this) ;
13:       p.setPen(QPen::NoPen) ;
14:       p.setBrush(mCol) ;
15:       p.drawRect(0,0,width(),height()) ;
16:     }
17:     QColor mCol ;
18: } ;
19: int main (int argc, char* argv[])
20: { QApplication app(argc, argv) ;
21:   QScrollView sv(0) ;
22:   sv.resizeContents(500, 500) ;
23:   MyWidget w1(Qt::red, 30, 30, sv.viewport()) ;
24:   sv.addChild(&w1, 10, 10) ;
25:   MyWidget w2(Qt::blue, 10, 100, sv.viewport()) ;
26:   sv.addChild(&w2, 400, 20) ;
27:   MyWidget w3(Qt::green, 200, 100, sv.viewport()) ;
28:   sv.addChild(&w3, 200, 300) ;
29:   sv.resize(200, 200) ;
30:   app.setMainWidget(&sv) ;
31:   sv.show() ;
32:   return app.exec() ;
33: }

Voici le résultat :

Remarquez que le fond de la fenêtre est un gris sombre : c’est le fond par défaut des QScrollView.

4. Beaucoup de widgets

Ce sont les cas où ce que vous voulez afficher peut être de très grande taille et composé de nombreux éléments - une page web unique de tous les HOWTOs, ou la liste de vos milliers de contacts. Nous allons réutiliser le widget personnalisé précédent, pour créer 256 cadres de couleurs :

00001: #include <qapplication.h>
00002: #include <qscrollview.h>
00003: #include <qwidget.h>
00004: #include <qpainter.h>
00005: class MyWidget : public QWidget
00006: /* le même que précédemment */
00007: int main (int argc, char* argv[])
00008: { QApplication app(argc, argv) ;
00009:   QScrollView sv(0) ;
00010:   sv.resizeContents(200, 2600) ;
00011:   sv.enableClipper(true) ;
00012:   MyWidget * w = 0 ;
00013:   for (int i=0 ; i<256 ; i++)
00014:   { w = new MyWidget(QColor(0,120,i), 100, 10, sv.viewport()) ;
00015:     sv.addChild(w,0,10*i) ;
00016:   }
00017:   sv.resize(200, 200) ;
00018:   app.setMainWidget(&sv) ;
00019:   sv.show() ;
00020:   return app.exec() ;
00021: }

La seule différence notable est à la ligne 11, l’appel à la méthode enableClipper(). Nous aurions en fait obtenu sensiblement le même résultat sans l’utiliser, parceque notre zone n’est pas si étendu que ça (2600 pixels de haut, ligne 10). Par contre, si la zone est réellement vaste, activer le clipper() met en place dans le QScrollView un mécanisme de regroupement des widgets contenus dans la zone, ce qui permet d’optimiser le défilement et évite l’effet désagréable de clignotement (flicker).

Toujours pour des considérations d’optimisation, il est préférable d’utiliser cette méthode, plutôt que de créer un unique widget de grande taille pour contenir tous les éléments.

5. Conclusion

La classe QScrollView est bien utile en elle-même, mais elle est essentiellement conçue pour être dérivée. En particulier, il est possible de dessiner directement dans la zone en redéfinissant la méthode virtuelle drawContents() (c’est d’ailleurs à peu près le seul moyen de changer la couleur de fond). Je vous laisse consulter la documentation de cette classe pour découvrir ses aspects plus avancés.

Nous avons entr’aperçu ce mois-ci l’utilisation de QPainter pour dessiner, nous l’étudierons plus en détail le mois prochain.

6. Références

[1] Qt 3.0 class chart, PostScript : ftp://ftp.trolltech.com/qt/postscript/qt30-class-chart.ps.gz

[2] Qt 3.0 class chart, PDF : ftp://ftp.trolltech.com/qt/pdf/3.0/qt30-class-chart.pdf


Yves Bailly

http://www.kafka-fr.net



Article publié dans LinuxMagazine 41 de juillet 2002