Tracés de courbes
Cette rubrique présente les diverses possibilités de la librairie pour tracer des courbes sur des repères orthonormés. La librairie exporte deux classes instantiables et une classe abstraite permettant ces opérations. Ces classes sont :
La classe TGraph est abstraite. Vous ne pouvez pas créer d'objet de cette classe. La raison étant que selon le fonctionnement, certaines de ses fonctions doivent être obligatoirement surchargées par les classes TXYGraph et TYGraph. Cependant c'est elle qui exporte la presque totalité des fonctions de tracés et de manipulations des tracés, comme les opérations de zoom.
La classe TXYGraph est une classe générique qui permet de tracer des courbes Y=f(X). Les fonctions de tracés reçoivent un tableau pour les X, un tableau pour les Y. La classe TYGraph peut être vue comme une simplification de la classe TXYGraph en ce sens que les valeurs en X sont définies par l'indice des éléments du tableau de Y.
Ces 3 classes s'appuient également sur des classes utilitaires :
- TGraphAxis : Qui est instanciée pour chaque axe du graphique.
- TAxisGrid : Qui représente la grille de chaque axe du tracé.
- TCurve : Qui représente une courbe du tracé.
- TXYData : Qui représente un couple XY d'un point d'une courbe.
Cette rubrique propose un certain nombre d'exemples d'utilisation de ces classes de difficultés croissantes :
- Tracé d'une courbe en fonction des indices de son tableau
- Tracé de plusieurs courbes en fonction des indices de leurs tableaux
- Modifier les propriétés des courbes
- Supprimer des courbes d'un tracé
- Gérer et manipuler les courbes (The hard way...)
- Tracer une courbe en fonction du temps
- Tracé de plusieurs courbes Y=f(X)
- Fonctions de zoom directes à la souris
- Fonctions de zoom à l'aide de curseurs
Générateur de fonctions
Pour tracer des courbes, il faut des données à tracer ! En phase d'apprentissage, il n'est pas souvent aisée de disposer de données à tracer. Pour cette raison, la librairie, via sa classe TUtilities expose une fonction nommée "CreateWave" qui permet de générer des courbes à tracer : Des sinus, des cosinus, des signaux carrés, des triangles, des dents de scie, du bruit aléatoire, des rampes, etc... Tous les exemples donnés dans cette rubrique utilisent cette fonction pour générer le ou les signaux à tracer.
Tracé d'une courbe en fonction des indices de son tableau
On dispose d'un tableau de points de type réel (double) que l'on souhaite tracer en fonction des indices du tableau. C'est à dire que le point à l'indice 0 du tableau a pour coordonnée X la valeur 0, le point à l'indice 1 a pour coordonnée X la valeur 1, etc... Pour cela on utilise un objet de la classe TYGraph et l'une de ses méthodes YAutoPlot ou YDataPlot. La différence entre ces deux fonctions étant que la fonction YAutoPlot recalcule les valeurs minimales et maximales des axes, alors que la fonction YDataPlot ne modifie pas les axes et utilise ceux déjà présents. Il faut également se rappeler que les graduations des axes sont calculées selon les directives données à la rubrique Gestion des axes.
Ci dessous un exemple d'utilisation de la fonction YAutoPlot :

// Génération d'une sinusoide de 2 alternances de 200 points par alternance
// Soit 400 points au total
// avec un gain de 5 pour une amplitude de -5 à +5
double* pData = new double[400];
TUtilities::CreateWave(pData,
TXS::Waveform_Sinus,
5,
0,
0,
2,
200);
// Tracé de la courbe
m_pYGraph->YAutoPlot(pData, 400);
Ci dessous un exemple d'utilisation de la fonction YDataPlot :

// Génération d'une sinusoide de 2 alternances de 200 points par alternance
// Soit 400 points au total
// avec un gain de 5 pour une amplitude de -5 à +5
double* pData = new double[400];
TUtilities::CreateWave(pData,
TXS::Waveform_Sinus,
5,
0,
0,
2,
200);
// On définie les axes du TYGraph
m_pYGraph->SetAxisMinMax(TXS::XBottomAxis, 0, 600);
m_pYGraph->SetAxisMinMax(TXS::YLeftAxis, -10, 10);
// Tracé de la courbe
m_pYGraph->YDataPlot(pData, 400);
Tracé de plusieurs courbes en fonction des indices de leurs tableaux
A chaque fois que vous utilisez une fonction YAutoPlot ou YDataPlot vous ajoutez une courbe au graphique. Les courbes déjà présentes sont conservées. Pour tracer plusieurs courbes, il suffit donc d'utiliser une de ces fonctions pour chaque courbe à tracer. Dans l'exemple suivant nous tracons une première courbe avec une fonction YDataPlot, puis une seconde avec une fonction YAutoPlot. L'ordre d'appel de ces deux fonctions est important car le tracé est optimisé pour se faire en fin de boucle de messages de Qt. C'est donc la dernière fonction qui décide des axes automatiques :

// Génération d'une sinusoide de 2 alternances de 200 points par alternance
// Soit 400 points au total
// avec un gain de 5 pour une amplitude de -5 à +5
double* pSinewave = new double[400];
TUtilities::CreateWave(pSinewave,
TXS::Waveform_Sinus,
5,
0,
0,
2,
200);
// Génération d'un triangle de 2 alternances de 200 points par alternance
// Soit 400 points au total
// avec un gain de 5 pour une amplitude de -3 à +3
double* pTriangle = new double[400];
TUtilities::CreateWave(pTriangle,
TXS::Waveform_Sinus,
3,
0,
0,
2,
200);
// Tracé de la courbe
m_pYGraph->YDataPlot(pTriangle, 400);
m_pYGraph->YAutoPlot(pSinewave, 400);
Il est possible d'utiliser une surcharge de ces fonctions qui prennent en paramètre des listes (type Qt QList) de pointeurs de double et d'entiers. On remplace alors les deux dernières lignes par :
QList<double *> Datas;
Datas.append(pSinewave);
Datas.append(pTriangle);
QList<int> Sizes;
Sizes.append(400);
Sizes.append(400);
// Tracé des courbes
m_pYGraph->YAutoPlot(Datas, Sizes);
Modifier les propriétés des courbes
Une des limites de l'exemple précédent est que les deux courbes ont les mêmes propriétés, notamment elles ont la même couleur. Afin d'améliorer la visualisation, il serait bon de modifier au moins la propriété couleur d'une des deux courbes.
A chaque fois que vous passez à un objet TGraph ou TXYGraph un tableau de points à tracer, celui ci définie une instance de la classe TCurve pour ce tableau de points. Finalement, en interne, il utilise cette instance de TCurve pour effectuer le tracé. Il maintient toutes les courbes dans une liste de pointeurs d'objets de ce type. Vous pouvez accéder à cette liste, donc à tous les objets TCurve via sa méthode Curves. Cette fonction retourne la liste des courbes. Vous pouvez alors accéder à ces courbes par numéro d'indice. Ainsi pour modifier la couleur et l'épaisseur de la ligne des courbes définies dans l'exemple précédent, procédez comme suit :
// Tracé des courbes
m_pYGraph->YAutoPlot(Datas, Sizes);
m_pYGraph->Curves()->at(0)->SetColor(Qt::green);
m_pYGraph->Curves()->at(1)->SetColor(Qt::cyan);
m_pYGraph->Curves()->at(0)->SetThickness(2);
m_pYGraph->Curves()->at(1)->SetThickness(2);
On obtient alors une bien meilleure visibilité :

Supprimer des courbes d'un tracé
Lorsque vous obtenez la liste de courbes via la fonction Curves vous recevez un pointeur vers un objet Qt nommé QList. Vous pouvez alors utiliser toutes les fonctions des objets de cette classe. Ainsi, si vous voulez ssupprimer et effacer de la mémoire la dernière courbe ajoutée à votre tracé, vous pouvez exécuter le code suivant :Si vous souhaitez supprimer toutes les courbes d'un tracé, vous pouvez utiliser deux fonctions : RemoveCurves ou ClearCurves. La différence entre ces deux fonctions est que la première supprime les courbes mais ne les efface pas de la mémoire. La seconde les supprime et les efface de la mémoire. En général, dans les cas simples, on utilise la seconde fonction comme suit :
Cependant, comme nous le verrons dans l'exemple suivant, il est possible de définir et tracer des courbes sans utiliser des fonctions comme YAutoPlot. On peut directement définir soi même des objets TCurve et modifier direcctement la liste de courbes accessible via la fonction Curves. Dans ce cas, on peut vouloir supprimer une courbe de la liste sans pour autant la supprimer de la mémoire
Gérer et manipuler les courbes (The hard way...)
Si l'on souhaite définir les objets TCurve soi même, plutôt que de laisser les objets de tracé les créer pour nous via les fonctions comme YAutoPlot, la méthode Curves nous l'autorise. Ainsi, vous pouvez modifier le deuxième extrait de code du paragraphe Tracé de plusieurs courbes en fonction des indices de leur tableaux, tout en modifiant les propriétés des courbes comme le montre le paragraphe Modifier les propriétés des courbes, comme suit pour obtenir le même résultat :
// Génération d'une sinusoide de 2 alternances de 200 points par alternance
// Soit 400 points au total
// avec un gain de 5 pour une amplitude de -5 à +5
double* pSinewave = new double[400];
TUtilities::CreateWave(pSinewave,
TXS::Waveform_Sinus,
5,
0,
0,
2,
200);
// Génération d'un triangle de 2 alternances de 200 points par alternance
// Soit 400 points au total
// avec un gain de 5 pour une amplitude de -3 à +3
double* pTriangle = new double[400];
TUtilities::CreateWave(pTriangle,
TXS::Waveform_Sinus,
3,
0,
0,
2,
200);
// Définition des courbes
TCurve* pSinewaveCurve = new TCurve();
pSinewaveCurve->Set(pSinewave, 400);
pSinewaveCurve->SetColor(Qt::green);
pSinewaveCurve->SetThickness(2);
TCurve* pTriangleCurve = new TCurve();
pTriangleCurve->Set(pTriangle, 400);
pTriangleCurve->SetColor(Qt::cyan);
pTriangleCurve->SetThickness(2);
// Ajout des courbes à la liste des courbes de l'objet graphique
m_pYGraph->Curves()->append(pSinewaveCurve);
m_pYGraph->Curves()->append(pTriangleCurve);
// Tracé des courbes
m_pYGraph->AutoPlot();
Ici on utilise la fonction AutoPlot de la classe abstraite TGraph. Bien sur, son équivalent sans recalcul des axes existe : DataPlot.
Cette technique est plus complexe, impose plus de code, mais elle permet de manipuler les objets TCurve directement. Y compris de modifier leur contenu point par point.
Tracer une courbe en fonction du temps
Nous reprenons le premier exemple : Tracé d'une courbe en fonction des indices de son tableau. Mais nous souhaitons non pas tracer une courbe Y=f(X) mais plutôt une courbe Y=f(T) ou T est le temps. En fait, au niveau du tracé, on fait le même travail. La seule différence est que l'on demande à ce que l'axe X soit gradué en unités de temps. Pour cela la classe TGraph nous permet de modifier les propriété des axes.
A titre d'exemple, nous avons fait des mesures chaque minute d'une journée à partir de minuit. En fin de journée on dispose donc de 1440 mesures (24H X 60') (Dans cet exemple, nous simulons ces mesures par un signal de bruit aléatoire). On doit donc d'abord modifer les graduations de l'axe X afin qu'il présente des graduations temporelles, avec une graduation toutes les 2 heures. Nous allons également contraindre l'axe à utiliser une echelle fixe manuelle comme cela est expliquer à la rubrique Gestion des axes. Nous allons également modifier les limites de l'axe Y. Cet exemple, au dela de créer une graduation temporelle est un bon exemple pour la manipulation des axes. Pour cela, procéder comme suit :
double* pRandom = new double[1440];
TUtilities::CreateWave(pRandom,
TXS::Waveform_Noise,
1,
0,
0,
1,
1440);
// OBjet QDateTime initialisé à la date du jour à minuit
QDateTime DT = QDateTime::currentDateTime();
DT.setTime(QTime(0, 0));
// L'axe X travaille par intercvalles de 2 heures (120 minutes)
m_pYGraph->SetAxisTicksSpecification(TXS::XBottomAxis, TXS::TicksSpecification_Interval);
m_pYGraph->SetAxisTicksInterval(TXS::XBottomAxis, 120);
// On définie les minimums et maximums des axes X et Y
m_pYGraph->SetAxisMinMax(TXS::XBottomAxis, 0 ,1440);
m_pYGraph->SetAxisMinMax(TXS::YLeftAxis, -3 ,3);
// On définie l'echelle de temps de l'axe X
// La valeur passée à la fonction SetAxisSampleRate est exprimée en fréquence (hertz)
// 1 acquisition par minute correspond à une fréquence de 0.01666666666 Hertz
m_pYGraph->SetAxisUseTimeScale(TXS::XBottomAxis, true);
m_pYGraph->SetAxisTimeOrigin(TXS::XBottomAxis, DT);
m_pYGraph->SetAxisSampleRate(TXS::XBottomAxis, 0.01666666666);
m_pYGraph->SetAxisTimeFormat(TXS::XBottomAxis, TXS::TimeFormat_HHMM);
// On trace via un DataPlot car on a définie les axes nous même
m_pYGraph->YDataPlot(pRandom, 1440);
On obtient le résultat suivant :

Noter que l'on utilise la fonction de tracé YDataPlot. En effet il serait dommage d'utiliser un YAutoPlot qui recalculerait les axes alors que nous les avons spécifiés nous même.
ATTENTION : L'ordre du code suivant est important :
m_pYGraph->SetAxisTicksSpecification(TXS::XBottomAxis, TXS::TicksSpecification_Interval);
m_pYGraph->SetAxisTicksInterval(TXS::XBottomAxis, 120);
// On définie les minimums et maximums des axes X et Y
m_pYGraph->SetAxisMinMax(TXS::XBottomAxis, 0 ,1440);
m_pYGraph->SetAxisMinMax(TXS::YLeftAxis, -3 ,3);
En effet, comme on utilise une fonction YDataPlot pour tracer, au moment du tracé les axes ne sont pas recalculés. Ils sont calculés uniquement lorsque l'on utilise les fonctions SetAxisMinMax. Il est important à ce moment que les spécifications d'axes définies par les fonctions SetAxisTicksSpecification et SetAxisTicksInterval soient activées.
La règle à suivre est simple : Les fonctions de définition des graduations telles que présentées à la rubrique Gestion des axes doivent toujours être appelées avant de définir les limites des axes. Que ces limites soient définies par un YAutoPlot (ou un des ses équivalents) ou par un appel à la fonction SetAxisMinMax.
Tracé d'une courbe Y=f(X)
Jusqu'à présent nous avons tracer des courbes en fonction de leur indice. Dans cet exemple nous tracons une courbe en fonction d'un tableau de points. Un courbe de type Y=f(X). Pour cela nous utilisons la classe TXYGraph qui, pour chaque courbe reçoit un tableau pour les valeurs en X et un tableau pour les valeurs en Y. Un bon exemple consiste à tracer un cercle trigonométrique de rayon 1. C'est à dire une sinusoide en fonction d'une cosinusoide. On obtient le résultat suivant :

double* pXData = new double[360];
TUtilities::CreateWave(pXData,
TXS::Waveform_Sinus,
1,
0,
0,
1,
360);
double* pYData = new double[360];
TUtilities::CreateWave(pYData,
TXS::Waveform_Cosinus,
1,
0,
1,
0,
360);
m_pXYGraph->XYAutoPlot(pXData, pYData, 360);
Tracé de plusieurs courbes Y=f(X)
Le tracé de plusieurs courbes Y=f(X) répond au mêmes princiapes que ceux évoqués au paragraphe Tracé de plusieurs courbes en fonction des indices de leur tableaux.
Dans l'exemple suivant nous traçons un cercle trigonométrique et une seconde courbe d'une sinusoide en fonctions d'une sinusoide avec une amplitude moins élevée et avec 4 alternances sur les valeurs en Y. On utilise la méthode de transfert des courbes à tracer basée sur des QList :

TUtilities::CreateWave(pXData1,
TXS::Waveform_Sinus,
1,
0,
0,
1,
360);
double* pYData1 = new double[360];
TUtilities::CreateWave(pYData1,
TXS::Waveform_Cosinus,
1,
0,
0,
1,
360);
double* pXData2 = new double[360];
TUtilities::CreateWave(pXData2,
TXS::Waveform_Sinus,
0.75,
0,
0,
1,
360);
double* pYData2 = new double[360];
TUtilities::CreateWave(pYData2,
TXS::Waveform_Sinus,
0.5,
0,
0,
4,
90);
QList<double *> XDatas;
QList<double *> YDatas;
QList<int> Sizes;
XDatas.append(pXData1);
XDatas.append(pXData2);
YDatas.append(pYData1);
YDatas.append(pYData2);
Sizes.append(360);
Sizes.append(360);
m_pXYGraph->XYAutoPlot(XDatas, YDatas, Sizes);
Position des axes
Dans l'exemple précédent, il est usuel de positionner les axes à la coordonnée 0 de l'axe orthogonal. La classe TGraph expose une fonction nommée SetAxisPosition qui permet cela. Il est possible de positionner les axes à l'origine, au centre, à la fin ou au zéro de l'axe orthogonal. Ainsi pour positionner nos axes à la coordonnée 0 de l'axe orthogonal, nnous pourrions modifier le code juste avant la fonction de tracé comme suit :
m_pXYGraph->SetAxisPosition(TXS::YLeftAxis, TXS::Axis_At_Zero);
m_pXYGraph->XYAutoPlot(XDatas, YDatas, Sizes);
On obtient alors le résultat suivant ;
Axes multiples
Si nous reprenons l'exemple du paragraphe Gérer et manipuler les courbes (The hard way...) mais que l'une des courbes a une amplitude très supérieure à l'autre d'un facteur 100, nous obtenons le résultat suivant :

Sur un tel tracé, il devient difficile de lire des valeurs en Y pour la courbe bleu. C'est le cas typique ou il est bon de disposer d'un deuxième axe Y gradué de manière adaptée à la courbe bleu. La librairie permet d'utiliser 2 axes en X et 2 axes en Y. L'utilisation de deux axes en X peut sembler inutile, pourtant elle présente un intérêt. Si l'on reprend l'exemple du paragraphe Tracer une courbe en fonction du temps, on pourrait avoir un axe X gradué en indice, et le second gradué en temps. Nous allons modifier notre exemple, en ajoutant un deuxième axe Y. En accrochant la courbe bleu à cet axe. Il est bon dans ce cas d'attirbuer la même couleur aux axes et leurs compsoants que celle des courbes. Voici le code concerné :
// Soit 400 points au total
// avec un gain de 100 pour une amplitude de -100 à +100
double* pSinewave = new double[400];
TUtilities::CreateWave(pSinewave,
TXS::Waveform_Sinus,
100,
0,
0,
2,
200);
// Génération d'un triangle de 2 alternances de 200 points par alternance
// Soit 400 points au total
// avec un gain de 3 pour une amplitude de -1 à +1
double* pTriangle = new double[400];
TUtilities::CreateWave(pTriangle,
TXS::Waveform_Triangle,
1,
0,
0,
2,
200);
// La première courbe
TCurve* pSinewaveCurve = new TCurve();
pSinewaveCurve->Set(pSinewave, 400);
pSinewaveCurve->SetColor(Qt::green);
pSinewaveCurve->SetThickness(2);
// La seconde courbe
TCurve* pTriangleCurve = new TCurve();
pTriangleCurve->Set(pTriangle, 400);
pTriangleCurve->SetColor(Qt::cyan);
pTriangleCurve->SetThickness(2);
// Axe Y droit visible
m_pYGraph->SetAxisVisible(TXS::YRightAxis, true);
// La coubre triangle s'attache à l'axe droit
pTriangleCurve->SetYAxis(TXS::YRightAxis);
// On donne aux axes la couleur des courbes
m_pYGraph->SetAxisColor(TXS::YLeftAxis, QColor(Qt::green));
m_pYGraph->SetAxisTicksColor(TXS::YLeftAxis, QColor(Qt::green));
m_pYGraph->SetAxisLabelsColor(TXS::YLeftAxis, QColor(Qt::green));
m_pYGraph->SetAxisColor(TXS::YRightAxis, QColor(Qt::cyan));
m_pYGraph->SetAxisTicksColor(TXS::YRightAxis, QColor(Qt::cyan));
m_pYGraph->SetAxisLabelsColor(TXS::YRightAxis, QColor(Qt::cyan));
// On ajoute les courbes au tracé
m_pYGraph->Curves()->append(pSinewaveCurve);
m_pYGraph->Curves()->append(pTriangleCurve);
// Tracé des courbes
m_pYGraph->AutoPlot();
On obtient alors le tracé suivant. Notez les graduations différentes des deux axes :
Customisation
Il est possible de modifier certaines propriétés visuelles des objets de tracé : Ajouter des titre aux axes, modifier la couleur de la grille, modifier la couleur de fond, etc... Ci dessous un exemple de ces modifications (Les lignes de définition des courbes sont omises):
.../...
// Axe Y droit visible
m_pYGraph->SetAxisVisible(TXS::YRightAxis, true);
// La coubre triangle s'attache à l'axe droit
pTriangleCurve->SetYAxis(TXS::YRightAxis);
// On donne aux axes la couleur des courbes
m_pYGraph->SetAxisColor(TXS::YLeftAxis, QColor(Qt::green));
m_pYGraph->SetAxisTicksColor(TXS::YLeftAxis, QColor(Qt::green));
m_pYGraph->SetAxisLabelsColor(TXS::YLeftAxis, QColor(Qt::green));
m_pYGraph->SetAxisColor(TXS::YRightAxis, QColor(Qt::cyan));
m_pYGraph->SetAxisTicksColor(TXS::YRightAxis, QColor(Qt::cyan));
m_pYGraph->SetAxisLabelsColor(TXS::YRightAxis, QColor(Qt::cyan));
// On donne un titre aux axes Y
m_pYGraph->SetAxisTitle(TXS::YLeftAxis, "Sinus");
m_pYGraph->SetAxisTitle(TXS::YRightAxis, "Triangle");
m_pYGraph->SetAxisTitleColor(TXS::YLeftAxis, QColor(Qt::green));
m_pYGraph->SetAxisTitleColor(TXS::YRightAxis, QColor(Qt::cyan));
// Modification de la couleur et des lignes de grille verticale
m_pYGraph->Axis(TXS::XBottomAxis)->Grid()->SetMajorGridColor(QColor(Qt::yellow));
m_pYGraph->Axis(TXS::XBottomAxis)->Grid()->SetMajorGridStyle(TXS::DashDotLine);
// Et le fond sur le widget
m_pYGraph->SetBackgroundIsTransparent(false);
m_pYGraph->SetBackgroundColor(QColor(Qt::black));
// On ajoute les courbes au tracé
m_pYGraph->Curves()->append(pSinewaveCurve);
m_pYGraph->Curves()->append(pTriangleCurve);
// Tracé des courbes
m_pYGraph->AutoPlot();
On obtient alors la visualisation suivante :
Oscilloscope
Développer une application qui simule un oscilloscope est très simple. On dispose de deux options : Utiliser un timer (objet Qt QTimer) ou une application multithread. Cette deuxième option, dans l'absolu, est la meilleure mais cela est hors cadre de cette page. Nous allons donc utiliser un timer. Il n'y a que deux choses à prendre en compte. Tout d'abord un oscilloscope ne fonctionne jamais en axes automatiques. On choisi une gamme, et elle reste fixe. Sinon la lecture serait complexe avec des changements d'echelle potentiellement rapides. Il faut donc définir une gamme pour l'axe Y via une fonction SetAxisMinMax puis pour le tracé des courbes utiliser une fonction DataPlot. Enfin, il faut se rappeler qu'à chaque appel de DataPlot on ajoute une courbe au tracé. Ce n'est pas ce que nous voulons. Nous devons donc, avant de tracer la courbe, supprimer la courbe existante. Le moyen le plus simple est alors d'utiliser la fonction ClearCurves. Le reste est simple comme bonjour. Voici le code complet :
: QDialog(parent)
{
// Initialisation de la librairie TXSLib
TXS::Init();
// Dialogue de taille variable
setMinimumSize(400,400);
resize(800,600);
// Elément de l'interface opérateur
m_pLayout = new QHBoxLayout();
m_pYGraph = new TYGraph();
// Definition du timer
m_pTimer = new QTimer();
// Layouting
m_pLayout->addWidget(m_pYGraph);
setLayout(m_pLayout);
// Un oscilloscope doit avoir une echelle fixe
m_pYGraph->SetAxisMinMax(TXS::YLeftAxis, -5, 5);
// Connections
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(OnTimer()));
// Démarrage du timer
m_pTimer->start(100);
}
void Dialog::OnTimer()
{
double* pNoise = new double[100];
TUtilities::CreateWave(pNoise,
TXS::Waveform_Noise,
2,
0,
0,
1,
100);
m_pYGraph->ClearCurves();
m_pYGraph->YDataPlot(pNoise, 100);
}
Accumulateur
Ce mode de fonctionnement est à peu près équivalent au mode Oscilloscope, mais ici au lieu de remplacer la courbe à chaque tracé, on ajoute des points à la courbe. Pour cela, les objets TYGraph et TXYGraph disposent de fonctions dédiées à cela. Pour l'objet TYGraph les fonctions se nomment YAutoAdd et YDataAdd. Pour l'objet TXYGraph les fonctions se nomment XYAutoAdd et XYDataAdd. Sauf cas spéciaux, en général on utilise les fonctions YAutoAdd ou XYAutoAdd afin que les axes soient toujours recalculés. Le code est alors le suivant :
: QDialog(parent)
{
// Initialisation de la librairie TXSLib
TXS::Init();
// Dialogue de taille variable
setMinimumSize(400,400);
resize(800,600);
// Elément de l'interface opérateur
m_pLayout = new QHBoxLayout();
m_pYGraph = new TYGraph();
// Definition du timer
m_pTimer = new QTimer();
// Layouting
m_pLayout->addWidget(m_pYGraph);
setLayout(m_pLayout);
// Connections
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(OnTimer()));
// Démarrage du timer
m_pTimer->start(100);
}
void Dialog::OnTimer()
{
double* pNoise = new double[10];
TUtilities::CreateWave(pNoise,
TXS::Waveform_Noise,
2,
0,
0,
1,
10);
m_pYGraph->YAutoAdd(pNoise, 10);
}
Défilement
Ce mode de fonctionnement est à peu près équivalent au mode Accumulateur, mais ici, à chaque fois que l'on ajoute n points en fin de courbes, on en enlève autant au début. On obtient un fonctionnement de type "Strip, Chart". Pour cela, les objets TYGraph et TXYGraph disposent de fonctions dédiées à cela. Pour l'objet TYGraph les fonctions se nomment YAutoSlide et YDataSlide. Pour l'objet TXYGraph les fonctions se nomment XYAutoSlide et XYDataSlide. Sauf cas spéciaux, en général on utilise les fonctions YAutoSlide ou XYAutoSlide afin que les axes soient toujours recalculés. Bien sur, pour obtenir ce fonctionnement, il est important que le premier tracé ai plus de points que le nombre de points dont on fait glisser la courbe. Le code est alors le suivant :
: QDialog(parent)
{
// Initialisation de la librairie TXSLib
TXS::Init();
// Dialogue de taille variable
setMinimumSize(400,400);
resize(800,600);
// Elément de l'interface opérateur
m_pLayout = new QHBoxLayout();
m_pYGraph = new TYGraph();
// Definition du timer
m_pTimer = new QTimer();
// Layouting
m_pLayout->addWidget(m_pYGraph);
setLayout(m_pLayout);
// Connections
connect(m_pTimer, SIGNAL(timeout()), this, SLOT(OnTimer()));
// Premier tracé de 100 points
double* pNoise = new double[1000];
TUtilities::CreateWave(pNoise,
TXS::Waveform_Noise,
2,
0,
0,
1,
1000);
m_pYGraph->YAutoPlot(pNoise, 1000);
// Démarrage du timer
m_pTimer->start(100);
}
void Dialog::OnTimer()
{
double* pNoise = new double[100];
TUtilities::CreateWave(pNoise,
TXS::Waveform_Noise,
2,
0,
0,
1,
100);
m_pYGraph->YAutoSlide(pNoise, 100);
}
A l'exécution, notez que l'axe des X s'incrémente.
Ajout de marqueurs
Il est possible d'ajouter des marqueurs sur un tracé. Pour cela il faut tout d'abord activer les marqueurs en utilisant la fonction EnableMarkers. A partir de ce moment si l'on clique sur le tracé (pas necéssairement sur une courbe) avec les touches "Ctrl" et "Alt" appuyées, un nouveau marqueur est ajouté. Le signal d'ajout d'un marqueur eventMarkerAdded est émis. Si un slot est connecté sur ce signal, on peut alors suivre l'ajout des marqueurs. Les marqueurs se présente de la façon suivante :

Le code d'un slot connecté au signal eventMarkerAdded dans lequel on ajoute une ligne à un widget QTreeWidget pourrait être le suivant :
{
QTreeWidgetItem* pItem = new QTreeWidgetItem();
// Obtenir la valeur Y la plus proche
int Index = static_cast<int>(rint(XValue));
double YValue = m_pYGraph->Curves()->at(0)->At(Index)->GetY();
// ajout du marqueur dans le TreeWidget
pItem->setText(0, QString::number(nMarker + 1));
pItem->setText(1, QString::number(XValue));
pItem->setText(2, QString::number(YValue));
m_pMarkersTree->addTopLevelItem(pItem);
// On enlève la valeur sur le marqueur pour ne pas encombrer l'axe X
m_pYGraph->SetMarkerShowValue(nMarker, false);
}
Cet extrait de code intègre deux points importants. Le premier est lié aux lignes suivantes :
double YValue = m_pYGraph->Curves()->at(0)->At(Index)->GetY();
Ces lignes permettent de retrouver la valeur Y de la courbe à la position du marqueur. l'appel de la fonction "rint" avec le type cast vers un entier permet de retrouver l'indice le plus proche de la position du marqueur. On utilise ensuite cet indice pour lire la valeur en Y.
Le deuxième point concerne la numérotation des marqueurs. Le premier marqueur est à l'indice 0, mais il est affiché à l'indice 1 car pour l'utilisateur un marqueur numéroté 0 serait assez bizarre. Donc pour accéder au marqueur 1, il faut dans le code utiliser la valeur 0.
Reportez vous à la page TGraph pour vous renseigner sur les fonctions et propriétés liées aux marqueurs. Vous pouvez modifier les couleurs des marqueurs, ajouter des marqueurs par le code, supprimer des marqueurs, etc...
Fonctions de zoom directes à la souris
Une fonction très pratique sur les tracés est la possibilité de zoomer sur une partie de la ou des courbes. Pour cela la classe TGraph expose la fonction EnterZoomMode pour activer le mode zoom. La fonction ExitZoomOrReadoutMode met un terme à ce mode. Le code suivant est une exemple simple de mise en oeuvre de ce mode :
: QDialog(parent)
{
// Initialisation de la librairie TXSLib
TXS::Init();
// Dialogue de taille variable
setMinimumSize(400,400);
resize(800,600);
// Elément de l'interface opérateur
m_pLayout = new QVBoxLayout();
m_pButtonsLayout = new QHBoxLayout();
m_pYGraph = new TYGraph();
m_pEnterZoomButton = new QPushButton();
m_pExitZoomButton = new QPushButton();
// Réglage des boutons
m_pEnterZoomButton->setFixedWidth(150);
m_pExitZoomButton->setFixedWidth(150);
m_pEnterZoomButton->setText("Activation Zoom");
m_pExitZoomButton->setText("Desactivation Zoom");
// Layouting
m_pButtonsLayout->addStretch();
m_pButtonsLayout->addWidget(m_pEnterZoomButton);
m_pButtonsLayout->addWidget(m_pExitZoomButton);
m_pButtonsLayout->addStretch();
m_pLayout->addWidget(m_pYGraph);
m_pLayout->addLayout(m_pButtonsLayout);
setLayout(m_pLayout);
// Génération d'une sinusoide de 2 alternances de 200 points par alternance
// Soit 400 points au total
// avec un gain de 5 pour une amplitude de -5 à +5
double* pSinewave = new double[400];
TUtilities::CreateWave(pSinewave,
TXS::Waveform_Sinus,
5,
0,
0,
2,
200);
m_pYGraph->YAutoPlot(pSinewave, 400);
// Connections
connect(m_pEnterZoomButton, SIGNAL(clicked()), this, SLOT(OnEnterZoom()));
connect(m_pExitZoomButton, SIGNAL(clicked()), this, SLOT(OnExitZoom()));
}
void Dialog::OnEnterZoom()
{
m_pYGraph->EnterZoomMode(true);
}
void Dialog::OnExitZoom()
{
m_pYGraph->ExitZoomOrReadoutMode(true);
}
Un point important est qu'il ne faut jamais enchainer la mise en service du mode zoom avec une fonction de tracé. La raison est liée au fait que le tracé s'effectue en fin de la boucle des messages de Qt. Mettre en service le mode zoom avant que le tracé soit à jour peut alors conduire à un fonctionnement non standard.
Fonctions de zoom à l'aide de curseurs
La fonctionnalité de zoom présentée au paragraphe précédent est pratique mais peu précise. Pour mettre en place une méthode de zooming plus précise, la librairie propose une méthode de "readout". Cette méthode affiche deux curseurs verticaux que l'on peut déplacer à la souris ou au clavier. L'image ci-dessous montre un tracé avec le mode Readout activé et les deux curseurs sélectionnés :
Pour activer ce mode, il faut utiliser la fonction EnterReadoutMode. La fonction ExitZoomOrReadoutMode permet de mettre fin à ce mode. La fonction ZoomFromReadout permet de zoomer entre les deux curseurs. Lorsqu'un curseur est déplacé, le signal eventReadoutCursorMoved est émis. Comme pour les marqueurs, vous pouvez également lire les positions des curseurs. Vous pouvez aussi positionner des marqueurs à la position des curseurs.
Reportez vous à la page TGraph pour vous renseigner sur les fonctions et propriétés liées au mode Readout....