간단한 막대 그래프

QML 애플리케이션에서 Bars3D 사용.

단순 막대 그래프에서는 Bars3D 및 QML을 사용하여 간단한 3D 막대 그래프를 만드는 방법을 보여 줍니다.

다음 섹션에서는 계열을 전환하고 한 번에 두 개 이상의 계열을 표시하는 방법을 설명합니다. 기본 QML 애플리케이션 기능에 대한 자세한 내용은 단순 분산형 그래프를 참조하세요.

예제 실행하기

에서 예제를 실행하려면 Qt Creator에서 Welcome 모드를 열고 Examples 에서 예제를 선택합니다. 자세한 내용은 예제 빌드 및 실행하기를 참조하세요.

데이터

예제 데이터 집합은 가상의 회사의 몇 년 동안의 월별 수입 및 지출입니다. 데이터는 다음과 같이 Data.qml 의 목록 모델에 정의되어 있습니다:

ListModel {
    id: dataModel
    ListElement{ timestamp: "2016-01"; expenses: "-4";  income: "5" }
    ListElement{ timestamp: "2016-02"; expenses: "-5";  income: "6" }
    ListElement{ timestamp: "2016-03"; expenses: "-7";  income: "4" }
    ...

각 데이터 항목에는 타임스탬프, 수입 및 지출의 세 가지 역할이 있습니다. 타임스탬프 값의 형식은 <four digit year>-<two digit month> 입니다. 일반적으로 연도와 월을 막대형 차트의 행과 열에 매핑하지만, 수입 또는 지출 중 하나만 값으로 표시할 수 있습니다.

이제 Bars3D 그래프에 데이터를 추가합니다. 그 안에 수입에 대한 시리즈부터 시작하여 Bar3DSeries 두 개를 만듭니다:

Bar3DSeries {
    id: barSeries
    itemLabelFormat: "Income, @colLabel, @rowLabel: @valueLabel"
    baseGradient: barGradient

    ItemModelBarDataProxy {
        id: modelProxy
        itemModel: graphData.model
        rowRole: "timestamp"
        columnRole: "timestamp"
        valueRole: "income"
        rowRolePattern: /^(\d\d\d\d).*$/
        columnRolePattern: /^.*-(\d\d)$/
        rowRoleReplace: "\\1"
        columnRoleReplace: "\\1"
        multiMatchBehavior: ItemModelBarDataProxy.MMBCumulative
    }
    ...

데이터는 시리즈 내부의 ItemModelBarDataProxyitemModel 속성에 첨부됩니다. valueRole 의 경우 원하는 값을 포함하는 income 필드를 지정합니다. 연도와 월을 가져오는 것은 같은 필드에서 찾을 수 있기 때문에 조금 더 복잡합니다. 이러한 값을 추출하려면 rowRolecolumnRole 모두에 대해 timestamp 필드를 지정하고 해당 역할에 대한 검색 패턴과 바꾸기 규칙을 추가로 지정하여 각 역할에 대한 필드 콘텐츠의 올바른 부분을 추출합니다. 검색 패턴은 일반 JavaScript 정규식이며, 바꾸기 규칙은 정규식과 일치하는 필드 콘텐츠가 무엇으로 대체되는지를 지정합니다. 이 경우 전체 필드 콘텐츠를 행과 열 모두에서 캡처된 첫 번째 부분 문자열인 연도 또는 월로만 바꿉니다. 정규식으로 바꾸기 기능에 대한 자세한 내용은 QString::replace(const QRegExp &rx, const QString &after) 함수 설명서를 참조하세요.

multiMatchBehavior 속성은 여러 항목 모델 항목이 동일한 행/열 조합과 일치하는 경우 수행할 작업을 지정합니다. 이 경우 해당 값을 합산합니다. 항목 모델에 중복되는 월이 없으므로 월별 값을 표시할 때는 이 속성이 영향을 미치지 않지만 나중에 연간 합계를 표시하려는 경우 이 속성이 관련됩니다.

그런 다음 비용에 다른 계열을 추가합니다:

Bar3DSeries {
    id: secondarySeries
    visible: false
    itemLabelFormat: "Expenses, @colLabel, @rowLabel: -@valueLabel"
    baseGradient: secondaryGradient

    ItemModelBarDataProxy {
        id: secondaryProxy
        itemModel: graphData.model
        rowRole: "timestamp"
        columnRole: "timestamp"
        valueRole: "expenses"
        rowRolePattern: /^(\d\d\d\d).*$/
        columnRolePattern: /^.*-(\d\d)$/
        valueRolePattern: /-/
        rowRoleReplace: "\\1"
        columnRoleReplace: "\\1"
        multiMatchBehavior: ItemModelBarDataProxy.MMBCumulative
    }
    ...

모델에는 비용이 음수 값으로 포함되어 있지만 수입 막대와 쉽게 비교할 수 있도록 양수 막대로 표시하려고 합니다. 이를 위해 valueRolePattern 을 사용하여 마이너스 기호를 제거합니다. 기본 대체 문자열은 빈 문자열이므로 대체 문자열을 지정할 필요가 없습니다.

계열의 visible 속성을 사용하여 지금은 두 번째 계열을 숨깁니다.

사용자 지정 축 레이블

Axes.qml 데이터에 월별 숫자가 포함되어 있어 레이블이 복잡해지기 때문에 열 축의 범주 레이블을 재정의합니다:

CategoryAxis3D {
    id: columnAxis
    labels: ["January", "February", "March", "April", "May", "June",
        "July", "August", "September", "October", "November", "December"]
    labelAutoRotation: 30
}

낮은 카메라 각도에서 축 레이블을 더 읽기 쉽게 만들려면 자동 축 레이블 회전을 설정합니다.

시리즈 전환

main.qml 에서 그래프와 다양한 UI 요소를 설정합니다. 여기서 주목해야 할 세 가지 흥미로운 코드 블록이 있습니다. 첫 번째는 두 계열의 가시성을 간단히 변경하여 수입과 지출, 또는 두 가지 모두 간에 시각화된 데이터를 변경하는 방법을 보여줍니다:

onClicked: {
    if (text === "Show Expenses") {
        barSeries.visible = false;
        secondarySeries.visible = true;
        barGraph.valueAxis.labelFormat = "-%.2f M\u20AC";
        secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: @valueLabel";
        text = "Show Both";
    } else if (text === "Show Both") {
        barSeries.visible = true;
        barGraph.valueAxis.labelFormat = "%.2f M\u20AC";
        secondarySeries.itemLabelFormat = "Expenses, @colLabel, @rowLabel: -@valueLabel";
        text = "Show Income";
    } else { // text === "Show Income"
        secondarySeries.visible = false;
        text = "Show Expenses";
    }
}

축 레이블 형식과 항목 선택 레이블 형식을 조정하여 실제로는 양수 값으로 해결되던 비용에 음수 부호가 제대로 표시되도록 합니다.

두 번째로 흥미로운 블록은 프록시 속성을 조정하여 시각화된 데이터를 변경한 곳입니다:

onClicked: {
    if (text === "Show yearly totals") {
        modelProxy.autoRowCategories = true;
        secondaryProxy.autoRowCategories = true;
        modelProxy.columnRolePattern = /^.*$/;
        secondaryProxy.columnRolePattern = /^.*$/;
        graphAxes.value.autoAdjustRange = true;
        barGraph.columnAxis = graphAxes.total;
        text = "Show all years";
    } else if (text === "Show all years") {
        modelProxy.autoRowCategories = true;
        secondaryProxy.autoRowCategories = true;
        modelProxy.columnRolePattern = /^.*-(\d\d)$/;
        secondaryProxy.columnRolePattern = /^.*-(\d\d)$/;
        graphAxes.value.min = 0;
        graphAxes.value.max = 35;
        barGraph.columnAxis = graphAxes.column;
        text = "Show 2020 - 2022";
    } else { // text === "Show 2020 - 2022"
        // Explicitly defining row categories, since we do not want to show data for
        // all years in the model, just for the selected ones.
        modelProxy.autoRowCategories = false;
        secondaryProxy.autoRowCategories = false;
        modelProxy.rowCategories = ["2020", "2021", "2022"];
        secondaryProxy.rowCategories = ["2020", "2021", "2022"];
        text = "Show yearly totals";
    }
}

연간 합계를 표시하려면 각 연도의 12개월을 하나의 막대로 결합합니다. 모든 모델 항목과 일치하는 columnRolePattern 을 지정하면 됩니다. 이렇게 하면 데이터 프록시는 하나의 열만 갖게 됩니다. 프록시에 앞서 지정한 누적 multiMatchBehavior 이 이제 관련되어 매년 모든 12개월의 값이 단일 막대에 합산됩니다.

연도의 하위 집합만 표시하려면 ItemModelBarDataProxy 항목에서 autoRowCategories 을 false로 설정하고 행 카테고리를 명시적으로 정의합니다. 이렇게 하면 지정된 행 카테고리의 항목만 시각화됩니다.

세 번째 흥미로운 블록은 ItemModelBarDataProxy 메서드 rowCategoryIndex()columnCategoryIndex() 를 사용하여 행과 열 값을 알고 있는 경우 항목의 행 및 열 인덱스를 가져오는 방법을 보여줍니다:

onCurrentRowChanged: {
    var timestamp = graphData.model.get(mainview.currentRow).timestamp;
    var pattern = /(\d\d\d\d)-(\d\d)/;
    var matches = pattern.exec(timestamp);
    var rowIndex = modelProxy.rowCategoryIndex(matches[1]);
    var colIndex;
    if (barGraph.columnAxis == graphAxes.total)
        colIndex = 0 ;// Just one column when showing yearly totals
    else
        colIndex = modelProxy.columnCategoryIndex(matches[2]);
    if (selectedSeries.visible)
        mainview.selectedSeries.selectedBar = Qt.point(rowIndex, colIndex);
    else if (barSeries.visible)
        barSeries.selectedBar = Qt.point(rowIndex, colIndex);
    else
        secondarySeries.selectedBar = Qt.point(rowIndex, colIndex);
}

예제 프로젝트 @ code.qt.io

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