Traduction par Jean-luc Biord, du Site de la communauté
Qt francophone.
English TOC.
Le module de canevas, par exemple QCanvas, QCanvasView et QCanvasItem et les classes héritées, sont utilisées pour afficher les graphiques 2D. Les graphiques peuvent aller du classique jeu en 2D au graphique commercial. La documentation officielle dit ceci :
"Le canevas est optimisé pour un grand nombre d'éléments, particulièrement lorsqu'un petit pourcentage des éléments change en une seule fois. Si l'affichage entier change vraiment régulièrement, vous pouvez songer à utiliser votre propre classe héritée de QScrollView."
Les classes du module canevas construisent un modèle classique d'affichage, le modèle - Vue. La classe QCanvas est un document accueillant un nombre de QCanvasItems. Chaque canevas peut-être vu avec un ou plusieurs QCanvasViews. Cela signifie que le même canevas peut être affiché avec des vues vraiment indépendantes avec différents niveaux de zoom, rotations, etc.
Qt offre une panoplie d'éléments de canevas. Une démonstration de plusieurs de ces éléments est faite dans l'exemple 12-1 montré ci-dessous. Coller simplement le code dans un fichier.cpp dans un répertoire vide et lancez qmake -project && qmake && make pour générer un exécutable. La fenêtre du résultat est montrée schéma 12-1.
#include <qapplication.h>
#include <qcanvas.h>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QCanvas *c = new QCanvas( 210, 75 );
QCanvasView *cv = new QCanvasView( c );
QCanvasRectangle *rect = new QCanvasRectangle( 10, 10, 40,
40, c );
rect->setPen( Qt::black );
rect->setBrush( Qt::red );
rect->show();
QCanvasText *t = new QCanvasText( "Rect", c );
t->setX( 30 );
t->setY( 55 );
t->setTextFlags( Qt::AlignHCenter );
t->show();
QPointArray points( 3 );
points.setPoint( 0, 20, 0 );
points.setPoint( 1, 0, 40 );
points.setPoint( 2, 40, 40 );
QCanvasPolygon *poly = new QCanvasPolygon( c );
poly->setPoints( points );
poly->setX( 60 );
poly->setY( 10 );
poly->setBrush( Qt::blue );
poly->show();
t = new QCanvasText( "Poly", c );
t->setX( 80 );
t->setY( 55 );
t->setTextFlags( Qt::AlignHCenter );
t->show();
QCanvasLine *line = new QCanvasLine( c );
line->setPoints( 110, 10, 150, 50 );
line->setPen( QPen( Qt::green, 4 ) );
line->show();
t = new QCanvasText( "Line", c );
t->setX( 130 );
t->setY( 55 );
t->setTextFlags( Qt::AlignHCenter );
t->show();
QCanvasEllipse *elli = new QCanvasEllipse( 40, 40, 45*16,
225*16, c );
elli->setX( 180 );
elli->setY( 30 );
elli->setBrush( Qt::cyan );
elli->show();
t = new QCanvasText( "Elli", c );
t->setX( 180 );
t->setY( 55 );
t->setTextFlags( Qt::AlignHCenter );
t->show();
c->update();
a.setMainWidget( cv );
cv->show();
return a.exec();
}
Les classes utilisées dans cet exemple sont QCanvasText, QCanvasRectangle, QCanvasPolygon, QCanvasLine et QCanvasEllipse. Les quatre dernières sont héritées de QCanvasPolygonalItem qui est la classe d'élément de canevas la meilleure pour créer de nombreux éléments personnalisés.
En manipulant le polygone, notez que la forme obtenue doit être positionné en utilisant les méthodes setX et setY. Ces méthodes transposent les points, ainsi le point (20, 0) devient (20+60, 0+10) = (80, 10).
En créant l'ellipse les angles sont spécifiés en 16ème de degrés, d'où le ...*16 dans le constructeur. Les coordonnées indiquent le centre de l'ellipse.
Avant de continuer avec l'exemple suivant et de créer un élément personnalisé, une rapide consultation du tableau 12-1 est recommandée. L'élément polygone introduit les propriétés stylo (pen) et brosse (brush). Elles ne sont pas utilisées par toutes les classes héritées. C'est résumé dans le tableau ci-dessous, mais c'est également exposé dans la description détaillée de chaque élément dans la documentation officielle.
Classe | Utilise Brush | Utilise Pen |
QCanvasRectangle | Oui |
Oui |
QCanvasPolygon | Oui | Non |
QCanvasLine | Non | Oui |
QCanvasEllipse | Oui | Non |
Le deuxième exemple introduit un élément personnalisé de canevas, MyCanvasItem. L'exemple 12-2 montre la classe implémentée MyCanvasItem et l'exemple 12-3 montre les changements dans la fonction main depuis l'exemple précédent.
class MyCanvasItem : public QCanvasPolygonalItem
{
public:
MyCanvasItem( int width, int height, QCanvas *c ) :
QCanvasPolygonalItem( c )
{
m_width = width;
m_height = height;
}
~MyCanvasItem()
{
hide(); // Required to
avoid segfault - see docs
}
QPointArray areaPoints() const
{
QPointArray res(8);
res.setPoint( 0, QPoint( (int)x() + m_width/4,
(int)y() ) );
res.setPoint( 1, QPoint( (int)x() +
3*m_width/4, (int)y() ) );
res.setPoint( 2, QPoint( (int)x() + m_width,
(int)y() + 3*m_height/8 ) );
res.setPoint( 3, QPoint( (int)x() + m_width,
(int)y() + 5*m_height/8 ) );
res.setPoint( 4, QPoint( (int)x() +
3*m_width/4, (int)y() + m_height ) );
res.setPoint( 5, QPoint( (int)x() + m_width/4,
(int)y() + m_height ) );
res.setPoint( 6, QPoint( (int)x(),
(int)y() + 5*m_height/8 ) );
res.setPoint( 7, QPoint( (int)x(),
(int)y() + 3*m_height/8 ) );
return res;
}
protected:
void drawShape( QPainter &p )
{
p.drawEllipse( (int)x()+m_width/4, (int)y(),
m_width/2, m_height );
p.drawRect( (int)x(), (int)y()+3*m_height/8,
m_width, m_height/4 );
}
private:
int m_width;
int m_height;
};
Le main est modifié à deux endroits. Premièrement, la taille du canevas est différente, deuxièmement un certain nombre de lignes a été ajouté pour créer, positionner et nommer l'élément de canevas.
...
QCanvas *c = new QCanvas( 260, 75 );
...
// Custom canvas item
MyCanvasItem *my = new MyCanvasItem( 40, 40, c );
my->setX( 210 );
my->setY( 10 );
my->setBrush( Qt::yellow );
my->setPen( Qt::black );
my->show();
t = new QCanvasText( "Custom", c );
t->setX( 230 );
t->setY( 55 );
t->setTextFlags( Qt::AlignHCenter );
t->show();
...
L'implémentation de l'élément personnalisé consiste en cinq parties. Premièrement, le constructeur, qui est simple. Les paramètres commencent avec ceux spécifiques à la classe puis sont suivis des paramètres transmis à la classe de base. Le destructeur est également simple. La documentation officielle pour QCanvasPolygonalItem indique que chaque destructeur de classe héritée doit appeler hide.
La troisième partie est l'implémentation de areaPoints. La zone contenue à l'intérieur des points renvoyés doit contenir la forme entière tout en étant aussi petite que possible. La zone renvoyée depuis cette implémentation est montrée dans le schéma 12-2 comme ensemble bleu avec la forme réelle en jaune.
La quatrième partie est le dessin réel qui est effectué dans la fonction membre drawShape. Le painter passé en paramètre est configuré avec le stylo et la brosse donnés et est translaté de sorte que les coordonnées fournies en utilisant les méthodes setX et setY soit localisées en (0, 0).
La partie finale comprend les variables privées m_width et m_height.Dans une implémentation appropriée la largeur devrait être modifiable en utilisant une méthode setWidth et accessible par une méthode width et serait déclarée comme propriété en utilisant la macro Q_PROPERTY. La même chose naturellement s'applique à la hauteur.
La fenêtre du résultat du deuxième exemple est montrée sur le schéma 12-3.
Dans le troisième exemple, la vue du canevas sera changée. Une version faite sur mesure sera créée en sous-classant et la classe résultante permettra de déplacer les formes autour en utilisant un dispositif de pointage.
L'exemple 12-4 montre le code pour l'implémentation de la vue faite sur mesure.
class MyCanvasView : public QCanvasView
{
public:
MyCanvasView( QCanvas *c, QWidget *parent=0, const char
*name=0, WFlags f=0 ) : QCanvasView( c, parent, name, f )
{
dragging = 0;
}
protected:
void contentsMousePressEvent( QMouseEvent *e )
{
QCanvasItemList il = canvas()->collisions(
e->pos() );
for( QCanvasItemList::Iterator it=il.begin();
it!=il.end(); ++it )
{
if( (*it)->rtti() !=
QCanvasText::RTTI )
{
dragging = (*it);
xoffset =
(int)(e->x() - dragging->x());
yoffset =
(int)(e->y() - dragging->y());
return;
}
}
}
void contentsMouseReleaseEvent( QMouseEvent *e )
{
if( dragging )
{
dragging->setX( e->x() -
xoffset );
dragging->setY( e->y() -
yoffset );
dragging = 0;
canvas()->update();
}
}
void contentsMouseMoveEvent( QMouseEvent *e )
{
if( dragging )
{
dragging->setX( e->x() -
xoffset );
dragging->setY( e->y() -
yoffset );
canvas()->update();
}
}
private:
QCanvasItem *dragging;
int xoffset, yoffset;
};
Le constructeur positionne simplement le pointeur dragging à null. Ceci indique qu'aucun élément n'est déplacé actuellement.
contentsMousePressEvent détermine si un clic est le début d'une opération de déplacement ou pas en identifiant tout élément sous le pointeur de la souris et en vérifiant que l'élément ne soit pas un QCanvasText. Le but de cette limitation est de montrer comment la méthode membre rtti peut-être employée pour déterminer ce qui doit être déplacé. Si un élément valide est trouvé dragging est définit pour pointer sur lui et les variables xoffset et yoffset reçoivent la différence entre la position de l'élément (c.-à-d. le point que x et y indiquent) et l'endroit de la souris.
Quand la souris est déplacée, la position de l'élément, le cas échéant, est mise à jour. Notez l'appel à canvas()->update() qui est nécessaire pour mettre à jour la vue réelle. Quand le bouton de souris est relâché la position des éléments est ajustée de nouveau et le membre dragging est repositionné à null. Cela termine l'opération de mouvement.
Pour utiliser cette vue depuis la routine main il est nécessaire de changer la ligne créant la vue de canevas pour créer la version personnalisée à la place.
Le code du dernier chapitre peut être téléchargé ici.
Qt Quarterly à un article sur l'implémentation des éléments de canevas groupés.
This is a part of digitalfanatics.org and is valid XHTML.
Copyright (c) 2002-2004 by Johan Thelin (e8johan -at- digitalfanatics.org). This material may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 (local copy) or later (the latest version is presently available at http://www.opencontent.org/openpub/). Distribution of substantively modified versions of this document is prohibited without the explicit permission of the copyright holder. Distribution of the work or derivative of the work in any standard (paper) book form is prohibited unless prior permission is obtained from the copyright holder.