サーフェスグラフ・ギャラリー
Surface3D グラフの3つの異なる使い方を紹介するギャラリー。
Surface Graph Galleryでは、Surface3D グラフを使った3つの異なるカスタム機能をデモンストレーションしています。これらの機能は、アプリケーション内にそれぞれのタブを持っています。
以下の節では、これらの機能のみに焦点を当て、基本的な機能の説明は省略します - より詳細なQMLのサンプルドキュメントについては、単純な散布図を参照してください。
サンプルを実行する
からサンプルを実行するには Qt Creatorからサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳しくは、Building and Running an Exampleをご覧ください。
高さマップ
Height Map タブで、高さデータからサーフェスグラフを生成する。使用するデータは、ニュージーランドのルアペフ山とンガウルホー山の高さマップです。
グラフへのデータの追加
高さマップ画像から高さ情報を読み取るHeightMapSurfaceDataProxy を使ってデータを設定します。プロキシ自体はSurface3DSeries に含まれています。HeightMapSurfaceDataProxy 内のheightMapFile
プロパティは、高さデータを含む画像ファイルを指定します。プロキシ内の値プロパティは、表面積の幅、奥行き、高さの最小値と最大値を定義します。z
とx
の値は緯度と経度で、ほぼ実世界の位置にあり、y
はメートルです。
注: グラフの縦横比は現実の縮尺には設定されず、代わりに高さが誇張されている。
Surface3DSeries { id: heightSeries flatShadingEnabled: false drawMode: Surface3DSeries.DrawSurface HeightMapSurfaceDataProxy { heightMapFile: "://qml/qmlsurfacegallery/heightmap.png" // We don't want the default data values set by heightmap proxy, but use // actual coordinate and height values instead autoScaleY: true minYValue: 740 maxYValue: 2787 minZValue: -374 // ~ -39.374411"N maxZValue: -116 // ~ -39.115971"N minXValue: 472 // ~ 175.471767"E maxXValue: 781 // ~ 175.780758"E } onDrawModeChanged: heightMapView.checkState() }
データの表示
main.qml
で、データを表示するためにSurface3D 要素を設定します。
まず、サーフェスに使用するカスタム・グラデーションを定義します。ColorGradient で、0.0から1.0までの色を設定し、グラフをより鮮やかにするために2ストップ追加します:
ColorGradient { id: surfaceGradient ColorGradientStop { position: 0.0; color: "darkgreen"} ColorGradientStop { position: 0.15; color: "darkslategray" } ColorGradientStop { position: 0.7; color: "peru" } ColorGradientStop { position: 1.0; color: "white" } }
この要素をSurface3D で使用するtheme
のbaseGradients
プロパティに設定します:
theme: Theme3D { type: Theme3D.ThemeStoneMoss font.family: "STCaiyun" font.pointSize: 35 colorStyle: Theme3D.ColorStyleRangeGradient baseGradients: [surfaceGradient] // Use the custom gradient }
ボタンを使って、他のSurface3D 機能をコントロールする。
最初のボタンは、サーフェスグリッドのオンとオフを切り替えます。描画モードを完全にクリアすることはできないので、サーフェス自体が表示されていない限り、サーフェスグリッドを非表示にすることはできません:
onClicked: { if (heightSeries.drawMode & Surface3DSeries.DrawWireframe) heightSeries.drawMode &= ~Surface3DSeries.DrawWireframe; else heightSeries.drawMode |= Surface3DSeries.DrawWireframe; }
2番目のボタンはサーフェスグリッドの色を設定します:
onClicked: { if (Qt.colorEqual(heightSeries.wireframeColor, "#000000")) { heightSeries.wireframeColor = "red"; text = "Black surface\ngrid color"; } else { heightSeries.wireframeColor = "black"; text = "Red surface\ngrid color"; } }
3番目は、サーフェス描画モードでサーフェスのオン/オフを切り替えます。描画モードを完全にクリアすることはできないので、サーフェスグリッドが表示されていない限り、サーフェス自体を隠すことはできません:
onClicked: { if (heightSeries.drawMode & Surface3DSeries.DrawSurface) heightSeries.drawMode &= ~Surface3DSeries.DrawSurface; else heightSeries.drawMode |= Surface3DSeries.DrawSurface; }
4番目はシェーディングモードを設定します。例をOpenGL ESシステムで実行している場合、フラットシェーディングは使用できません:
onClicked: { if (heightSeries.flatShadingEnabled) { heightSeries.flatShadingEnabled = false; text = "Show\nFlat" } else { heightSeries.flatShadingEnabled = true; text = "Show\nSmooth" } }
残りのボタンは、グラフの背景機能をコントロールします。
スペクトログラム
Spectrogram タブでは、極およびデカルトスペクトログラムを表示し、2Dで表示するために正射投影を使用します。
スペクトログラムは、異なる値を強調するために使用される範囲勾配を持つサーフェスグラフです。通常、スペクトログラムは2次元のサーフェスで表示され、これはグラフのトップダウンの正射投影ビューでシミュレートされます。2D効果を強制するには、正投影モードでマウスまたはタッチによるグラフの回転を無効にします。
スペクトログラムの作成
2D スペクトログラムを作成するには、Surface3DSeries で指定されたデータでSurface3D アイテムをItemModelSurfaceDataProxy で定義します:
Surface3D { id: surfaceGraph anchors.fill: parent Surface3DSeries { id: surfaceSeries flatShadingEnabled: false drawMode: Surface3DSeries.DrawSurface baseGradient: surfaceGradient colorStyle: Theme3D.ColorStyleRangeGradient itemLabelFormat: "(@xLabel, @zLabel): @yLabel" ItemModelSurfaceDataProxy { itemModel: surfaceData.model rowRole: "radius" columnRole: "angle" yPosRole: "value" } }
2D 効果を有効にするための主なプロパティはorthoProjection とscene.activeCamera.cameraPreset です。グラフの正射投影を有効にして遠近感を取り除き、グラフを真上から見てY次元を取り除く:
// Remove the perspective and view the graph from top down to achieve 2D effect orthoProjection: true scene.activeCamera.cameraPreset: Camera3D.CameraPresetDirectlyAbove
この視点だと横軸のグリッドがほとんど表面に隠れてしまうので、グラフの上に描かれるように横軸のグリッドを反転させる:
flipHorizontalGrid: true
極スペクトル図
データによっては、デカルトグラフではなく極座標グラフを使う方が自然な場合があります。これはpolar プロパティでサポートされています。
極とデカルトのモードを切り替えるボタンを追加します:
Button { id: polarToggle anchors.margins: 5 anchors.left: parent.left anchors.top: parent.top width: spectrogramView.buttonWidth // Calculated elsewhere based on screen orientation text: "Switch to\n" + (surfaceGraph.polar ? "cartesian" : "polar") onClicked: surfaceGraph.polar = !surfaceGraph.polar; }
極座標モードでは、X軸は角度極軸に変換され、Z軸は半径極軸に変換されます。サーフェスポイントは新しい軸に従って再計算されます。
半径軸ラベルはデフォルトではグラフの外側に描画される。グラフ内の0度の角度軸のすぐ隣に描くには、小さなオフセットだけを定義します:
radialLabelOffset: 0.01
2D効果を強制するには、デフォルトの入力ハンドラをカスタムハンドラでオーバーライドし、投影モードに基づいてrotationEnabled プロパティを自動的に切り替えることによって、正投影モードでのグラフ回転を無効にします:
inputHandler: TouchInputHandler3D { rotationEnabled: !surfaceGraph.orthoProjection }
オシロスコープ
Oscilloscope タブで、C++ と QML をアプリケーションで組み合わせ、動的に変化するデータを表示します。
C++のデータソース
アイテムモデルベースのプロキシは、単純なグラフや静的なグラフには適していますが、リアルタイムに変化するデータを表示する場合には、基本的なプロキシを使って最高のパフォーマンスを実現してください。これらはQMLではサポートされていません。なぜなら、格納されているデータ項目はQObject を継承していないため、QMLのコードから直接操作することができないからです。この制限を克服するために、単純なDataSource
クラスを C++ で実装し、 シリーズのデータプロキシにデータを入力します。
DataSource
クラスを作成し、QML から呼び出せるメソッドを二つ用意します:
class DataSource : public QObject { Q_OBJECT ... Q_INVOKABLE void generateData(int cacheCount, int rowCount, int columnCount, float xMin, float xMax, float yMin, float yMax, float zMin, float zMax); Q_INVOKABLE void update(QSurface3DSeries *series);
最初のメソッドgenerateData()
は、表示するオシロスコープの模擬データのキャッシュを作成します。データはQSurfaceDataProxy が受け付ける形式でキャッシュされます:
// Populate caches auto *generator = QRandomGenerator::global(); for (int i = 0; i < cacheCount; ++i) { QSurfaceDataArray &cache = m_data[i]; float cacheXAdjustment = cacheStep * i; float cacheIndexAdjustment = cacheIndexStep * i; for (int j = 0; j < rowCount; ++j) { QSurfaceDataRow &row = *(cache[j]); float rowMod = (float(j)) / float(rowCount); float yRangeMod = yRange * rowMod; float zRangeMod = zRange * rowMod; float z = zRangeMod + zMin; qreal rowColWaveAngleMul = M_PI * M_PI * rowMod; float rowColWaveMul = yRangeMod * 0.2f; for (int k = 0; k < columnCount; k++) { float colMod = (float(k)) / float(columnCount); float xRangeMod = xRange * colMod; float x = xRangeMod + xMin + cacheXAdjustment; float colWave = float(qSin((2.0 * M_PI * colMod) - (1.0 / 2.0 * M_PI)) + 1.0); float y = (colWave * ((float(qSin(rowColWaveAngleMul * colMod) + 1.0)))) * rowColWaveMul + generator->bounded(0.15f) * yRangeMod; int index = k + cacheIndexAdjustment; if (index >= columnCount) { // Wrap over index -= columnCount; x -= xRange; } row[index] = QVector3D(x, y, z); } } }
2 番目のメソッドupdate()
は、キャッシュされたデータの 1 セットを別の配列にコピーし、QSurfaceDataProxy::resetArray() を呼び出すと、その配列がシリーズのデータプロキシに設定されます。オーバーヘッドを最小化するために、配列の寸法が変更されていない場合は、同じ配列を再利用する:
// Each iteration uses data from a different cached array if (++m_index >= m_data.size()) m_index = 0; const QSurfaceDataArray &array = m_data.at(m_index); int newRowCount = array.size(); int newColumnCount = array.at(0)->size(); // If the first time or the dimensions of the cache array have changed, // reconstruct the reset array if (!m_resetArray || series->dataProxy()->rowCount() != newRowCount || series->dataProxy()->columnCount() != newColumnCount) { m_resetArray = new QSurfaceDataArray(); m_resetArray->reserve(newRowCount); for (int i = 0; i < newRowCount; ++i) m_resetArray->append(new QSurfaceDataRow(newColumnCount)); } // Copy items from our cache to the reset array for (int i = 0; i < newRowCount; ++i) { const QSurfaceDataRow &sourceRow = *(array.at(i)); QSurfaceDataRow &row = *(*m_resetArray)[i]; std::copy(sourceRow.cbegin(), sourceRow.cend(), row.begin()); } // Notify the proxy that data has changed series->dataProxy()->resetArray(m_resetArray);
以前にプロキシに設定した配列ポインタを操作しているにもかかわらず、QSurfaceDataProxy::resetArray ()は、その中のデータを変更した後でも、グラフにデータをレンダリングするように促すために呼び出す必要がある。
QMLからDataSource
のメソッドにアクセスできるようにするには、データソースをQML_ELEMENT として公開します:
class DataSource : public QObject { Q_OBJECT QML_ELEMENT
さらに、CMakeLists.txt で QML モジュールとして宣言してください:
qt6_add_qml_module(qmlsurfacegallery URI SurfaceGallery VERSION 1.0 NO_RESOURCE_TARGET_PATH SOURCES datasource.cpp datasource.h ... )
DataSource
クラスメソッドのパラメータとしてQSurface3DSeries のポインタをすべての環境、ビルドで使用するためには、メタタイプが登録されていることを確認してください:
qRegisterMetaType<QSurface3DSeries *>();
QML アプリケーション
DataSource
を使用するには、QML モジュールをインポートし、使用するDataSource
のインスタンスを作成します:
import SurfaceGallery ... DataSource { id: dataSource }
Surface3D グラフを定義し、Surface3DSeries を与える:
Surface3D { id: surfaceGraph anchors.fill: parent Surface3DSeries { id: surfaceSeries drawMode: Surface3DSeries.DrawSurfaceAndWireframe itemLabelFormat: "@xLabel, @zLabel: @yLabel"
グラフに添付するSurface3DSeries にプロキシを指定しない。これにより、シリーズはデフォルトのQSurfaceDataProxy を利用することになります。
項目ラベルをitemLabelVisible で隠す。動的で変化の速いデータでは、選択ラベルが浮いていると邪魔になり、読みにくくなります。
itemLabelVisible: false
選択ポインタの上にあるデフォルトのフローティング・ラベルの代わりに、Text
要素に選択項目情報を表示することができます:
onItemLabelChanged: { if (surfaceSeries.selectedPoint == surfaceSeries.invalidSelectionPosition) selectionText.text = "No selection"; else selectionText.text = surfaceSeries.itemLabel; }
グラフが完成したら、generateData()
のヘルパー関数を呼び出してDataSource
キャッシュを初期化し、DataSource
の同名のメソッドを呼び出します:
Component.onCompleted: oscilloscopeView.generateData(); ... function generateData() { dataSource.generateData(oscilloscopeView.sampleCache, oscilloscopeView.sampleRows, oscilloscopeView.sampleColumns, surfaceGraph.axisX.min, surfaceGraph.axisX.max, surfaceGraph.axisY.min, surfaceGraph.axisY.max, surfaceGraph.axisZ.min, surfaceGraph.axisZ.max); }
データの更新をトリガーするには、DataSource
のupdate()
メソッドを要求された間隔で呼び出すTimer
を定義する:
Timer { id: refreshTimer interval: 1000 / frequencySlider.value running: true repeat: true onTriggered: dataSource.update(surfaceSeries); }
ダイレクト・レンダリングの有効化
このアプリケーションは、急速に変化する多くのデータを扱う可能性があるため、パフォーマンスのためにダイレクト・レンダリング・モードを使用します。このモードでアンチエイリアスを有効にするには、アプリケーションウィンドウのサーフェスフォーマットを変更します。QQuickView で使用されるデフォルトのフォーマットは、アンチエイリアスをサポートしていません。main.cpp
のサーフェスフォーマットを変更するには、提供されているユーティリティ関数を使用してください:
#include <QtDataVisualization/qutils.h> ... // Enable antialiasing in direct rendering mode viewer.setFormat(qDefaultSurfaceFormat(true));
コンテンツ例
© 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.