중첩된 도넛 차트 만들기

참고: 이 예제는 위젯이 있는 차트 갤러리 예제의 일부입니다.

QChartView 인스턴스를 만들고 앤티앨리어싱을 활성화하는 것으로 시작하겠습니다. 그런 다음 QChartView 인스턴스에서 QChart 객체를 가져옵니다. 범례가 비활성화되고 차트 제목이 설정됩니다. 마지막 줄은 차트의 애니메이션을 활성화합니다.

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);

도넛형 차트를 정의하는 데 사용되는 세 가지 변수가 정의됩니다. 최소 크기와 최대 크기는 전체 도넛의 상대적 크기를 정의합니다. minSize는 가장 작은 도넛의 상대적 내부 크기이고, maxSize는 가장 큰 도넛의 상대적 외부 크기입니다.

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

다음 코드 블록은 개별 도넛과 그 조각을 정의합니다. 먼저 새 QPieSeries 객체가 생성됩니다. 각 도넛의 슬라이스 개수는 무작위로 지정됩니다. 내부의 for 루프는 임의의 값과 동일한 레이블을 가진 슬라이스를 만듭니다. 다음으로 슬라이스의 레이블이 표시되도록 설정되고 색상이 흰색으로 설정됩니다. 예제를 더 흥미롭게 만들기 위해 슬라이스의 호버링 신호가 위젯의 슬롯에 연결되며, 그 내부 작동 방식은 나중에 설명합니다. 마지막으로 슬라이스가 도넛에 추가됩니다. 도넛의 중첩을 위해 도넛의 크기가 조정됩니다. 그런 다음 도넛이 위젯의 도넛 목록과 차트에 추가됩니다.

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);
}

마지막으로 위젯이 애플리케이션에서 사용하는 레이아웃에 배치됩니다.

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

이 예제를 더 재미있게 만들기 위해 도넛은 1.25초마다 무작위로 회전합니다.

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

위젯의 업데이트된 회전 슬롯은 아래에 정의되어 있습니다. 이 슬롯은 모든 도넛을 살펴보고 현재 회전을 임의의 값으로 수정합니다.

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);
    }
}

앞서 언급한 explodeSlice 슬롯 코드는 아래와 같습니다. 슬라이스가 폭발로 설정된 경우 도넛 회전을 제어하는 타이머를 중지합니다. 그런 다음 슬라이스의 시작 각도와 끝 각도를 슬라이스에서 얻습니다. 선택한 슬라이스를 강조 표시하려면 선택한 슬라이스가 포함된 슬라이스로부터 바깥쪽에 있는 다른 모든 도넛의 시작 및 끝 각도를 수정하여 강조 표시된 슬라이스의 길을 "막지" 않도록 합니다. 슬라이스가 더 이상 선택되지 않으면 원래 상태로 돌아갑니다.

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);
}

© 2025 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.