簡単な棒グラフ

QMLアプリケーションでBars3D

Simple Bar Graphでは、Bars3D と QML を使って簡単な3D棒グラフを作る方法を紹介しています。

次の節では、系列を切り替えたり、一度に複数の系列を表示したりする方法を説明します。QMLアプリケーションの基本的な機能については、「簡単な散布図」を参照してください。

例の実行

Qt Creator からサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳細については、Building and Running an Example を参照してください。

データ

サンプルのデータセットは、架空の会社の数年間の月次収入と支出です。データは、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" }
    ...

各データ項目には、タイムスタンプ、収入、支出の3つの役割があります。タイムスタンプの値は、<four digit year>-<two digit month> の形式です。通常は、年月を棒グラフの行と列に対応させますが、値としては収入か支出のどちらかしか表示できません。

次に、Bars3D グラフにデータを追加する。その中にBar3DSeries を2つ作成し、収入の系列から始める:

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.MultiMatchBehavior.Cumulative
    }
    ...

データは、系列内のItemModelBarDataProxyitemModel プロパティに添付します。valueRole には、income フィールドを指定します。年と月は同じフィールドにあるので、取得は少し複雑です。これらの値を抽出するには、rowRolecolumnRole の両方にtimestamp フィールドを指定する。さらに、各ロールのフィールド内容の正しい部分を抽出するために、これらのロールに対して検索パターンと置換ルールを指定します。検索パターンは通常のJavaScript正規表現であり、置換ルールは正規表現にマッチするフィールド内容を何に置き換えるかを指定します。この場合、フィールドの内容全体を、行と列の両方で最初にキャプチャされた部分文字列である年または月だけに置き換えます。正規表現による置換機能の詳細については、QString::replace(const QRegExp &rx, constQString &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.MultiMatchBehavior.Cumulative
    }
    ...

このモデルには、支出が負の値として含まれていますが、収入の棒グラフと比較しやすいように、正の棒グラフとして表示することもできます。valueRolePattern 、マイナス記号を削除します。デフォルトの置換は空文字列なので、置換文字列を指定する必要はありません。

系列のvisible プロパティを使用して、とりあえず2番目の系列を非表示にします。

カスタム軸ラベル

Axes.qml は、列軸のカテゴリ・ラベルを再定義します。データには月の数字が含まれているため、ラベルが乱雑になるからです:

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

軸ラベルを低いカメラ・アングルでも読みやすくするには、軸ラベルの自動回転を設定します。

系列の切り替え

main.qml で、グラフとさまざまなUI要素を設定する。ここには3つの興味深いコードブロックがある。最初のものは、2つの系列の可視性を変更するだけで、収入、支出、およびその両方の間で視覚化されたデータを変更する方法を示しています:

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"
    }
}

軸ラベル・フォーマットと項目選択ラベル・フォーマットを微調整して、実際には正の値として解決されていた支出について、負の符号を正しく表示するようにしています。

2番目の興味深いブロックは、プロキシのプロパティを調整することによって、可視化されたデータが変更されるところです:

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ヶ月を1本のバーにまとめます。これは、すべてのモデル項目にマッチするcolumnRolePattern 。こうすると、系列は1列だけになる。先ほどプロキシに指定した累積multiMatchBehavior は、各年のすべての12ヶ月の値を合計して1本の棒グラフにすることになります。

年のサブセットだけを表示するには、ItemModelBarDataProxy 項目でautoRowCategories をfalseに設定し、行のカテゴリーを明示的に定義する。こうすると、指定された行カテゴリーの項目だけが表示されます。

3番目の興味深いブロックは、ItemModelBarDataProxy メソッドrowCategoryIndex()columnCategoryIndex() を使って、行と列の値がわかっている場合に、項目の行と列のインデックスを取得する方法を示しています:

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

©2024 The Qt Company Ltd. 本文書に含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。