Q3DScatter en Qt : Guide pour les Graphiques de Nuages de Points 3D

Introduction à la Visualisation 3D avec QtDataVisualization

La bibliothèque Qt offre le module QtDataVisualization pour la création de visualisations de données en trois dimensions. Bien que ce framework soit plus adapté aux applications où la performance n'est pas le critère le plus critique et pour des ensembles de données de taille modérée, il permet de concevoir des représentations graphiques 3D esthétiques et interactives, telles que des nuages de points, des diagrammes à barres et des surfaces.

Types de Graphiques 3D Disponibles

Le module QtDataVisualization propose plusieurs classes pour la visualisation en 3D :

  • Q3DScatter (Nuage de Points 3D) : Idéal pour représenter des ensembles de données composés de points individuels dans un espace tridimensionnel. La fluidité dépend du matériel, mais environ un millier de points est généralement gérable sans latence notable. Ceci correspond, par exemple, à une grille de 10x10x10 points.
  • Q3DBars (Diagramme à Barres 3D) : Permet de visualiser des données catégorielles en utilisant des barres 3D. Ses performances sont comparables à celles de Q3DScatter.
  • Q3DSurface (Surface 3D) : Utilisé pour afficher des fonctions mathématiques ou des données de hauteur sous forme de surface continue. Ses capacités sont similaires aux autres types de graphiques 3D.

Explorer le Q3DScatter

La classe Q3DScatter est conçue pour afficher des nuages de points dans un environnement 3D interactif. Les utilisateurs peuvent manipuler la scène de plusieurs manières :

  • Rotation : Maintenez le bouton droit de la souris enfoncé et déplacez la souris.
  • Zoom : Utilisez la molette de la souris.
  • Sélection : Cliquez avec le bouton gauche de la souris (si cette fonctionnalité est activée).
  • Réinitialisation de la vue : Cliquez sur la molette de la souris pour revenir à la vue de caméra par défaut.

Pour les appareils tactiles, la rotation s'effectue par glissement, la sélection par appui long, et le zoom par pincement.

Si des axes personnalisés ne sont pas définis, Q3DScatter en crée par défaut. Ces axes peuvent être modifiés via leurs accesseurs respectifs. Il est également possible d'afficher plusieurs séries de données simultanément.

Construction d'un Graphique Q3DScatter Minimal

Voici un exemple de code simple pour créer et afficher un nuage de points 3D basique. Ce programme nécessite le module QtDataVisualization.

#include <QGuiApplication>
#include <Q3DScatter>
#include <QScatter3DSeries>
#include <QVector3D>
#include <QScatterDataArray>

// Utilisation de l'espace de noms pour simplifier l'accès aux classes 3D
using namespace QtDataVisualization;

int main(int argc, char **argv)
{
    QGuiApplication application(argc, argv);

    // Crée une instance du graphique de nuage de points 3D
    Q3DScatter *graphiqueNuage = new Q3DScatter();
    // Pour une fenêtre autonome, on peut ajuster les drapeaux (si nécessaire)
    // graphiqueNuage->setFlags(graphiqueNuage->flags() ^ Qt::FramelessWindowHint);

    // Crée une série de points 3D à ajouter au graphique
    QScatter3DSeries *serieDePoints = new QScatter3DSeries();
    serieDePoints->setMeshSmooth(true); // Active un rendu plus lisse des marqueurs de points

    // Définit les données pour le nuage de points
    QScatterDataArray donneesInitiales;
    donneesInitiales
        << QVector3D(0.2f, 0.8f, 0.5f)
        << QVector3D(-0.6f, -0.2f, -0.3f)
        << QVector3D(0.4f, -0.7f, 0.1f)
        << QVector3D(0.9f, 0.1f, 0.7f)
        << QVector3D(-0.1f, 0.5f, -0.8f);

    // Ajoute les points à la série via le proxy de données
    serieDePoints->dataProxy()->addItems(donneesInitiales);

    // Ajoute la série au graphique 3D
    graphiqueNuage->addSeries(serieDePoints);

    // Affiche le graphique dans une fenêtre dédiée
    graphiqueNuage->show();

    return application.exec();
}

L'exécution de ce code affichera une fenêtre avec quelques points en 3D. Vous pourrez interagir avec elle pour faire pivoter la scène ou zoomer.

Guide de Démarrage pour un Projet Q3DScatter

Étape 1 : Vérification de l'Installation du Module

Avant de commencer, assurez-vous que le module QtDataVisualization est bien installé avec votre version de Qt. Vous pouvez le vérifier en consultant la documentation Qt ou en cherchant des classes comme Q3DScatter dans l'aide de votre IDE.

Étape 2 : Configuration du Fichier Projet (.pro)

Pour lier le module à votre projet, ajoutez la ligne suivante à votre fichier .pro :

QT += datavisualization

Étape 3 : Inclusion des Fichiers d'En-tête Nécessaires

Dans vos fichiers source (.h ou .cpp) où vous utiliserez les classes de visualisation 3D, incluez les en-têtes pertinents :

#include <Q3DScatter>
#include <QScatter3DSeries>
#include <QVector3D>
#include <QScatterDataArray>
#include <QAbstract3DGraph> // Pour les paramètres globaux du graphique
#include <Q3DCamera>       // Pour la caméra
#include <Q3DTheme>        // Pour les thèmes

Étape 4 : Déclaration de l'Espace de Noms

Pour éviter de préfixer chaque classe avec QtDataVisualization::, vous pouvez déclarer l'utilisation de l'espace de noms au début de votre fichier source :

using namespace QtDataVisualization;

Étape 5 : Cadre de Construction du Graphique 3D

Voici un exemple de structure pour un widget Q3DScatter plus complet, montrant la configuraton des axes, des séries de données et des propriétés d'affichage :

#ifndef SCATTERPLOT3DWIDGET_H
#define SCATTERPLOT3DWIDGET_H

#include <QWidget>
#include <Q3DScatter>
#include <QScatter3DSeries>
#include <QVector3D>
#include <QList> // Pour QList<QVector3D>

// Utilisation de l'espace de noms pour les classes de visualisation 3D
using namespace QtDataVisualization;

namespace Ui {
class ScatterPlot3DWidget;
}

class ScatterPlot3DWidget : public QWidget
{
    Q_OBJECT

public:
    explicit ScatterPlot3DWidget(QWidget *parent = nullptr);
    ~ScatterPlot3DWidget();

    // Méthode publique pour mettre à jour les données du nuage de points
    void updatePlotData(const QList<QVector3D>& newDataPoints);

protected:
    // Initialise les composants du graphique 3D
    void initializePlotComponents();
    // Gère le redimensionnement du widget conteneur
    void resizeEvent(QResizeEvent *event) override;

private:
    Ui::ScatterPlot3DWidget *ui; // Interface utilisateur générée par Qt Designer

    Q3DScatter *m_scatterPlot;           // L'objet graphique de nuage de points 3D
    QWidget *m_widgetContainer;          // Le conteneur du graphique 3D
    QScatter3DSeries *m_dataSeries;      // La série de données pour le nuage de points
};

#endif // SCATTERPLOT3DWIDGET_H

#include "scatterplot3dwidget.h"
#include "ui_scatterplot3dwidget.h"
#include <Q3DTheme> // Pour les thèmes
#include <QAbstract3DGraph> // Pour ShadowQuality
#include <Q3DCamera> // Pour CameraPreset
#include <QtMath> // Pour qMin, qMax
#include <cfloat> // Pour FLT_MAX, FLT_MIN

ScatterPlot3DWidget::ScatterPlot3DWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ScatterPlot3DWidget),
    m_scatterPlot(nullptr),
    m_widgetContainer(nullptr),
    m_dataSeries(nullptr)
{
    ui->setupUi(this);
    setWindowTitle("Exemple de Nuage de Points 3D avec Q3DScatter");
    initializePlotComponents();
}

ScatterPlot3DWidget::~ScatterPlot3DWidget()
{
    delete ui;
}

void ScatterPlot3DWidget::updatePlotData(const QList<QVector3D>& newDataPoints)
{
    if (!m_dataSeries) {
        return; // La série n'est pas initialisée
    }

    QScatterDataArray dataPointsArray;
    float xMin = FLT_MAX, xMax = FLT_MIN;
    float yMin = FLT_MAX, yMax = FLT_MIN;
    float zMin = FLT_MAX, zMax = FLT_MIN;

    // Initialise les bornes avec la première valeur si des points existent
    if (!newDataPoints.isEmpty()) {
        xMin = xMax = newDataPoints.at(0).x();
        yMin = yMax = newDataPoints.at(0).y();
        zMin = zMax = newDataPoints.at(0).z();
    }

    // Ajoute les points et met à jour les bornes des axes
    for (const QVector3D& point : newDataPoints)
    {
        dataPointsArray << point;
        xMin = qMin(xMin, point.x());
        xMax = qMax(xMax, point.x());
        yMin = qMin(yMin, point.y());
        yMax = qMax(yMax, point.y());
        zMin = qMin(zMin, point.z());
        zMax = qMax(zMax, point.z());
    }

    // Définit les nouvelles données pour la série, remplaçant les anciennes
    m_dataSeries->dataProxy()->setItems(dataPointsArray);

    // Ajuste les plages des axes avec une petite marge pour la visibilité
    const float margin = 0.1f; // Marge de 10%
    m_scatterPlot->axisX()->setRange(xMin - margin, xMax + margin);
    m_scatterPlot->axisY()->setRange(yMin - margin, yMax + margin);
    m_scatterPlot->axisZ()->setRange(zMin - margin, zMax + margin);
}

void ScatterPlot3DWidget::initializePlotComponents()
{
    m_scatterPlot = new Q3DScatter();
    // Crée un conteneur pour le graphique 3D, parenté à ce widget
    m_widgetContainer = QWidget::createWindowContainer(m_scatterPlot, this);
    m_widgetContainer->setGeometry(this->rect()); // Positionne le conteneur

    // Configuration des axes
    m_scatterPlot->axisX()->setTitle("Axe X");
    m_scatterPlot->axisY()->setTitle("Axe Y");
    m_scatterPlot->axisZ()->setTitle("Axe Z");
    m_scatterPlot->axisX()->setTitleVisible(true);
    m_scatterPlot->axisY()->setTitleVisible(true);
    m_scatterPlot->axisZ()->setTitleVisible(true);

    // Crée une série de points pour le nuage 3D
    m_dataSeries = new QScatter3DSeries(m_scatterPlot);
    m_dataSeries->setMeshSmooth(true); // Active le lissage des points pour un rendu plus doux
    m_dataSeries->setItemLabelVisible(true); // Rend les labels de points visibles
    m_dataSeries->setItemLabelFormat(QStringLiteral("X:%2.1f Y:%2.1f Z:%2.1f")); // Format du label des points

    // Ajoute la série au graphique 3D
    m_scatterPlot->addSeries(m_dataSeries);

    // Paramètres d'affichage globaux du graphique
    m_scatterPlot->setShadowQuality(QAbstract3DGraph::ShadowQualityMedium); // Qualité moyenne des ombres
    m_scatterPlot->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFrontLow); // Vue de caméra initiale
    m_scatterPlot->activeTheme()->setGridEnabled(true); // Active l'affichage de la grille
    m_scatterPlot->activeTheme()->setBackgroundEnabled(false); // Désactive l'arrière-plan par défaut
    m_scatterPlot->activeTheme()->setLabelBackgroundEnabled(false); // Désactive l'arrière-plan des labels d'axes

    // Données initiales pour la démonstration
    QList<QVector3D> initialData;
    initialData << QVector3D(1.0f, 1.0f, 1.0f) << QVector3D(1.0f, 2.0f, 3.0f) << QVector3D(1.0f, 3.0f, 5.0f)
                << QVector3D(3.0f, 1.0f, 1.0f) << QVector3D(3.0f, 2.0f, 3.0f) << QVector3D(3.0f, 3.0f, 5.0f)
                << QVector3D(5.0f, 1.0f, 1.0f) << QVector3D(5.0f, 2.0f, 3.0f) << QVector3D(5.0f, 3.0f, 5.0f);
    updatePlotData(initialData); // Charge les données initiales
}

void ScatterPlot3DWidget::resizeEvent(QResizeEvent *event)
{
    // Redimensionne le conteneur du graphique pour qu'il occupe tout l'espace du widget parent
    if (m_widgetContainer)
    {
        m_widgetContainer->setGeometry(rect());
    }
    QWidget::resizeEvent(event); // Appel à la méthode de base
}

Résolution des Problèmes Courants

Problème : La Classe Q3DScatter est Introuvable

Symptôme : Le compilateur ou l'IDE affiche des erreurs indiquant que la classe Q3DScatter est indéfinie ou inconnue.

Cause : Les classes du module QtDataVisualization résident dans un espace de noms spécifique qui doit être explicitement importé ou qualifié.

Solution : Ajoutez la directive using namespace QtDataVisualization; au début de vos fichiers source où ces classes sont utilisées, ou qualifiez les noms de classe directement (par exemple, QtDataVisualization::Q3DScatter).

Étiquettes: Qt Q3DScatter QtDataVisualization C++ Graphiques3D

Publié le 23 juillet à 12h28