Création de graphiques en anneau imbriqués

Remarque : ceci fait partie de l’exemple « Charts with Widgets Gallery ».

Capture d'écran présentant une illustration de plusieurs graphiques en anneau combinés

Commençons par créer une instance de QChartView et activons l'anticrénelage. Un objet QChart est ensuite obtenu à partir de l'instance de QChartView. La légende est désactivée et le titre du graphique est défini. La dernière ligne active les animations du graphique.

auto chartView = new QChartView(this);
chartView->setRenderHint(QPainter::Antialiasing);
QChart *chart = chartView->chart();
chart->legend()->setVisible(false);
chart->setTitle("Nested Donuts (Hover over segments to explode them)");
chart->setAnimationOptions(QChart::AllAnimations);
chart->layout()->setContentsMargins(0, 0, 0, 0);

Trois variables sont définies pour définir le graphique en anneau. Les tailles min et max définissent la taille relative de l’anneau complet. minSize correspond à la taille intérieure relative du plus petit anneau. maxSize correspond à la taille extérieure relative du plus grand anneau.

qreal minSize = 0.1;
qreal maxSize = 0.9;
int donutCount = 5;

Le bloc de code suivant définit les anneaux individuels et leurs parts. Tout d’abord, un nouvel objet QPieSeries est créé. Le nombre de parts dans chaque anneau est généré de manière aléatoire. La boucle « for » interne crée les parts avec une valeur aléatoire et une étiquette identique à cette valeur. Ensuite, l'étiquette de la part est rendue visible et sa couleur est définie sur blanc. Pour rendre l'exemple plus intéressant, le signal « hovered » de la part est connecté au slot du widget, dont le fonctionnement interne sera expliqué plus loin. Enfin, la part est ajoutée au beignet. La taille du beignet est ajustée pour permettre l’imbrication des beignets. Le beignet est ensuite ajouté à la liste des beignets du widget et au graphique.

for (int i = 0; i < donutCount; i++) {
    auto donut = new QPieSeries;
    int sliceCount =  3 + QRandomGenerator::global()->bounded(3);
    for (int j = 0; j < sliceCount; j++) {
        qreal value = 100 + QRandomGenerator::global()->bounded(100);
        auto slice = new QPieSlice(QString("%1").arg(value), value);
        slice->setLabelVisible(true);
        slice->setLabelColor(Qt::white);
        slice->setLabelPosition(QPieSlice::LabelInsideTangential);
        connect(slice, &QPieSlice::hovered, this, &NestedDonutsWidget::explodeSlice);
        donut->append(slice);
        donut->setHoleSize(minSize + i * (maxSize - minSize) / donutCount);
        donut->setPieSize(minSize + (i + 1) * (maxSize - minSize) / donutCount);
    }
    m_donuts.append(donut);
    chartView->chart()->addSeries(donut);
}

Enfin, le widget est placé dans une mise en page utilisée par l’application.

auto mainLayout = new QGridLayout;
mainLayout->addWidget(chartView, 1, 1);
setLayout(mainLayout);

Pour rendre l’exemple plus intéressant, les beignets sont pivotés de manière aléatoire toutes les 1,25 seconde.

m_updateTimer = new QTimer(this);
connect(m_updateTimer, &QTimer::timeout, this, &NestedDonutsWidget::updateRotation);
m_updateTimer->start(1250);

Le slot `updatedRotation` du widget est défini ci-dessous. Il parcourt tous les beignets et modifie leur rotation actuelle d’une valeur aléatoire.

void NestedDonutsWidget::updateRotation()
{
    for (int i = 0; i < m_donuts.count(); i++) {
        QPieSeries *donut = m_donuts.at(i);
        qreal phaseShift =  -50 + QRandomGenerator::global()->bounded(100);
        donut->setPieStartAngle(donut->pieStartAngle() + phaseShift);
        donut->setPieEndAngle(donut->pieEndAngle() + phaseShift);
    }
}

Le code du slot `explodeSlice` mentionné précédemment est fourni ci-dessous. Si la part est définie sur « explosée », le minuteur contrôlant la rotation des anneaux est arrêté. Ensuite, les angles de début et de fin de la part sont récupérés à partir de celle-ci. Pour mettre en évidence la part sélectionnée, les angles de début et de fin de tous les autres beignets situés à l’extérieur de celui qui contient la part sélectionnée sont modifiés afin qu’ils ne « bloquent » pas le passage de la part mise en évidence. Si la part n’est plus sélectionnée, on revient à l’état d’origine.

void NestedDonutsWidget::explodeSlice(bool exploded)
{
    auto slice = qobject_cast<QPieSlice *>(sender());
    if (exploded) {
        m_updateTimer->stop();
        qreal sliceStartAngle = slice->startAngle();
        qreal sliceEndAngle = slice->startAngle() + slice->angleSpan();

        QPieSeries *donut = slice->series();
        qreal seriesIndex = m_donuts.indexOf(donut);
        for (int i = seriesIndex + 1; i < m_donuts.count(); i++) {
            m_donuts.at(i)->setPieStartAngle(sliceEndAngle);
            m_donuts.at(i)->setPieEndAngle(360 + sliceStartAngle);
        }
    } else {
        for (int i = 0; i < m_donuts.count(); i++) {
            m_donuts.at(i)->setPieStartAngle(0);
            m_donuts.at(i)->setPieEndAngle(360);
        }
        m_updateTimer->start();
    }
    slice->setExploded(exploded);
}

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.