軸の処理
QMLでカスタム入力ハンドラを使用して軸のドラッグを実装し、カスタム軸フォーマッタを作成します。
「軸の処理」では、軸に関する2つの異なるカスタム機能について解説します。これらの機能には、アプリケーション内にそれぞれ専用のタブが用意されています。
以下のセクションでは、これらの機能にのみ焦点を当て、基本的な機能の説明は省略します。QMLのサンプルに関するより詳細なドキュメントについては、「Simple Scatter Graph」を参照してください。

例の動作確認
以下の手順でサンプルを実行できます:
- Qt Creator
Welcome モードを開き、Examples からこの例を選択してください。詳細については、Qt Creator の「チュートリアル:ビルドと実行」を参照してください。
- Qt Extension for Visual Studio Code
Command Palette から「Qt: Open Qt examples 」コマンドを実行し、リストからこの例を選択します。詳細については、Qt Extension for Visual Studio Code の「チュートリアル: ビルドと実行」を参照してください。
軸のドラッグ
「Axis Dragging 」タブで、軸ラベルをドラッグして軸の範囲を変更できるようにするカスタム入力ハンドラをQMLで実装します。さらに、正射影を使用し、カスタムアイテムのプロパティを動的に更新します。
デフォルトの入力処理のオーバーライド
デフォルトの入力処理メカニズムを無効にするには、Scatter3D のデフォルトの入力ハンドラを解除します:
unsetDefaultInputHandler();
...次に、`MouseArea ` を追加し、親要素(scatterGraph が含まれているのと同じ `Item `)を埋めるように設定します。また、この例では他のボタンは必要ないため、マウスの左ボタン押下のみを受け付けるように設定します:
MouseArea {
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.LeftButton
...次に、マウスクリックを監視し、イベントが捕捉されたら、グラフに選択クエリを送信します:
onPressed: (mouse)=> {
scatterGraph.doPicking(Qt.point(mouse.x, mouse.y));
}onPositionChanged シグナルハンドラは、移動距離の計算に必要な現在のマウス位置を捕捉します:
onPositionChanged: (mouse)=> {
currentMouseX = mouse.x;
currentMouseY = mouse.y;
...onPositionChanged の最後に、後で説明する移動距離の計算に備えて、直前のマウス位置を保存します:
...
previousMouseX = currentMouseX;
previousMouseY = currentMouseY;
}マウス移動を軸範囲の変化に変換する
scatterGraph 内で、onSelectedElementChanged を監視します。このシグナルは、inputArea のonPressed で選択クエリが行われた後に発火します。要素の型は、関心のある型であるため、メインコンポーネントで定義したプロパティ(property int selectedAxisLabel: -1 )に設定します:
onSelectedElementChanged: {
if (selectedElement >= Graphs3D.ElementType.AxisXLabel
&& selectedElement <= Graphs3D.ElementType.AxisZLabel) {
selectedAxisLabel = selectedElement;
} else {
selectedAxisLabel = -1;
}
}次に、inputArea のonPositionChanged に戻り、マウスボタンが押されているか、および現在選択されている軸ラベルがあるかを確認します。条件が満たされている場合、マウスの動きから軸範囲の更新への変換を行う関数を呼び出します:
...
if (pressed && selectedAxisLabel != -1)
axisDragView.dragAxis();
...このケースでは、カメラの回転が固定されているため、変換は簡単です。あらかじめ計算された値を使用し、マウスの移動距離を算出し、その値を選択された軸の範囲に適用することができます:
function dragAxis() {
// Do nothing if previous mouse position is uninitialized
if (previousMouseX === -1)
return;
// Directional drag multipliers based on rotation. Camera is locked to 45 degrees, so we
// can use one precalculated value instead of calculating xx, xy, zx and zy individually
var cameraMultiplier = 0.70710678;
// Calculate the mouse move amount
var moveX = currentMouseX - previousMouseX;
var moveY = currentMouseY - previousMouseY;
// Adjust axes
switch (selectedAxisLabel) {
case Graphs3D.ElementType.AxisXLabel:
var distance = ((moveX - moveY) * cameraMultiplier) / dragSpeedModifier;
// Check if we need to change min or max first to avoid invalid ranges
if (distance > 0) {
scatterGraph.axisX.min -= distance;
scatterGraph.axisX.max -= distance;
} else {
scatterGraph.axisX.max -= distance;
scatterGraph.axisX.min -= distance;
}
break;
case Graphs3D.ElementType.AxisYLabel:
distance = moveY / dragSpeedModifier;
// Check if we need to change min or max first to avoid invalid ranges
if (distance > 0) {
scatterGraph.axisY.max += distance;
scatterGraph.axisY.min += distance;
} else {
scatterGraph.axisY.min += distance;
scatterGraph.axisY.max += distance;
}
break;
case Graphs3D.ElementType.AxisZLabel:
distance = ((moveX + moveY) * cameraMultiplier) / dragSpeedModifier;
// Check if we need to change min or max first to avoid invalid ranges
if (distance > 0) {
scatterGraph.axisZ.max += distance;
scatterGraph.axisZ.min += distance;
} else {
scatterGraph.axisZ.min += distance;
scatterGraph.axisZ.max += distance;
}
break;
}
}マウス移動から軸範囲の更新へのより高度な変換については、「Graph Gallery」を参照してください。
その他の機能
このサンプルでは、正投影の使用方法や、カスタム項目のプロパティをリアルタイムで更新する方法についても説明しています。
正投影は非常に簡単です。scatterGraph のorthoProjection プロパティを変更するだけで済みます。このサンプルには、これをオン/オフに切り替えるためのボタンが用意されています:
Button {
id: orthoToggle
width: axisDragView.portraitMode ? parent.width : parent.width / 3
text: "Display Orthographic"
anchors.left: axisDragView.portraitMode ? parent.left : rangeToggle.right
anchors.top: axisDragView.portraitMode ? rangeToggle.bottom : parent.top
onClicked: {
if (scatterGraph.orthoProjection) {
text = "Display Orthographic";
scatterGraph.orthoProjection = false;
// Orthographic projection disables shadows, so we need to switch them back on
scatterGraph.shadowQuality = Graphs3D.ShadowQuality.Medium
} else {
text = "Display Perspective";
scatterGraph.orthoProjection = true;
}
}
}カスタムアイテムの場合は、scatterGraph のcustomItemList に1を追加します:
customItemList: [
Custom3DItem {
id: qtCube
meshFile: ":/qml/axishandling/cube.mesh"
textureFile: ":/qml/axishandling/cubetexture.png"
position: Qt.vector3d(0.65, 0.35, 0.65)
scaling: Qt.vector3d(0.3, 0.3, 0.3)
}
]グラフ内のすべてのアイテムを追加、削除、およびローテーションするためのタイマーを実装し、その同じタイマーを使用してカスタムアイテムのローテーションも行います:
onTriggered: {
rotationAngle = rotationAngle + 1;
qtCube.setRotationAxisAndAngle(Qt.vector3d(1, 0, 1), rotationAngle);
...軸フォーマッタ
「Axis Formatter 」タブで、カスタム軸フォーマッタを作成します。また、定義済みの軸フォーマッタの使用方法も示されています。
カスタム軸フォーマッタ
軸フォーマッタをカスタマイズするには、`QValue3DAxisFormatter` をサブクラス化する必要がありますが、これは QML コードだけでは実現できません。この例では、軸が float 値をタイムスタンプとして解釈し、軸ラベルに日付を表示します。これを実現するために、`QValue3DAxisFormatter` をサブクラス化する `CustomFormatter` という新しいクラスを導入します:
class CustomFormatter : public QValue3DAxisFormatter
{
...QScatter3DSeries のfloat値は、データ幅の違いによりQDateTime の値に直接キャストできないため、両者の間にある種のマッピングが必要です。このマッピングを行うには、フォーマッタに基準日を指定し、QScatter3DSeries からのfloat値をその基準値からの日付オフセットとして解釈します。基準日はプロパティとして指定されます:
Q_PROPERTY(QDate originDate READ originDate WRITE setOriginDate NOTIFY originDateChanged)値からQDateTime へのマッピングには、valueToDateTime() メソッドを使用します:
QDateTime CustomFormatter::valueToDateTime(qreal value) const
{
return m_originDate.startOfDay().addMSecs(qint64(oneDayMs * value));
}軸フォーマッタとして機能させるには、CustomFormatter はいくつかの仮想メソッドを再実装する必要があります:
QValue3DAxisFormatter *createNewInstance() const override;
void populateCopy(QValue3DAxisFormatter ©) override;
void recalculate() override;
QString stringForValue(qreal value, const QString &format) override;最初の2つのメソッドは単純で、CustomFormatter の新しいインスタンスを作成し、必要なデータをそこにコピーするだけです。これら2つのメソッドを使用して、レンダリング用のフォーマッタのキャッシュを作成および更新します。populateCopy() のスーパークラスの実装を呼び出すことを忘れないでください:
QValue3DAxisFormatter *CustomFormatter::createNewInstance() const
{
return new CustomFormatter();
}
void CustomFormatter::populateCopy(QValue3DAxisFormatter ©)
{
QValue3DAxisFormatter::populateCopy(copy);
CustomFormatter *customFormatter = static_cast<CustomFormatter *>(©);
customFormatter->m_originDate = m_originDate;
customFormatter->m_selectionFormat = m_selectionFormat;
}CustomFormatter その処理の大部分は `recalculate() ` メソッドで行われます。このメソッドでは、フォーマッタがグリッド、サブグリッド、ラベルの位置を計算し、ラベル文字列の書式設定も行います。カスタムフォーマッタでは、軸のセグメント数を無視し、グリッド線を常に午前0時に描画するようにします。サブセグメント数とラベルの位置決めは通常通り処理されます:
void CustomFormatter::recalculate()
{
// We want our axis to always have gridlines at date breaks
// Convert range into QDateTimes
QDateTime minTime = valueToDateTime(qreal(axis()->min()));
QDateTime maxTime = valueToDateTime(qreal(axis()->max()));
// Find out the grid counts
QTime midnight(0, 0);
QDateTime minFullDate(minTime.date(), midnight);
int gridCount = 0;
if (minFullDate != minTime)
minFullDate = minFullDate.addDays(1);
QDateTime maxFullDate(maxTime.date(), midnight);
gridCount += minFullDate.daysTo(maxFullDate) + 1;
int subGridCount = axis()->subSegmentCount() - 1;
QList<float> gridPositions;
QList<float> subGridPositions;
QList<float> labelPositions;
QStringList labelStrings;
// Reserve space for position arrays and label strings
gridPositions.resize(gridCount);
subGridPositions.resize((gridCount + 1) * subGridCount);
labelPositions.resize(gridCount);
labelStrings.reserve(gridCount);
// Calculate positions and format labels
qint64 startMs = minTime.toMSecsSinceEpoch();
qint64 endMs = maxTime.toMSecsSinceEpoch();
qreal dateNormalizer = endMs - startMs;
qreal firstLineOffset = (minFullDate.toMSecsSinceEpoch() - startMs) / dateNormalizer;
qreal segmentStep = oneDayMs / dateNormalizer;
qreal subSegmentStep = 0;
if (subGridCount > 0)
subSegmentStep = segmentStep / qreal(subGridCount + 1);
for (int i = 0; i < gridCount; i++) {
qreal gridValue = firstLineOffset + (segmentStep * qreal(i));
gridPositions[i] = float(gridValue);
labelPositions[i] = float(gridValue);
labelStrings << minFullDate.addDays(i).toString(axis()->labelFormat());
}
for (int i = 0; i <= gridCount; i++) {
if (subGridPositions.size()) {
for (int j = 0; j < subGridCount; j++) {
float position;
if (i)
position = gridPositions.at(i - 1) + subSegmentStep * (j + 1);
else
position = gridPositions.at(0) - segmentStep + subSegmentStep * (j + 1);
if (position > 1.0f || position < 0.0f)
position = gridPositions.at(0);
subGridPositions[i * subGridCount + j] = position;
}
}
}
setGridPoitions(gridPositions);
setSubGridPositions(subGridPositions);
setlabelPositions(labelPositions);
setLabelStrings(labelStrings);
}軸ラベルは、日付のみが表示されるようにフォーマットされます。ただし、選択ラベルのタイムスタンプの解像度を高めるには、ユーザーがカスタマイズできるように、カスタムフォーマッタに別のプロパティを指定します:
Q_PROPERTY(QString selectionFormat READ selectionFormat WRITE setSelectionFormat NOTIFY
selectionFormatChanged)この選択形式のプロパティは、再実装されたstringToValue メソッドで使用されます。このメソッドでは、送信された形式は無視され、代わりにカスタム選択形式が適用されます。
QString CustomFormatter::stringForValue(qreal value, const QString &format)
{
Q_UNUSED(format);
return valueToDateTime(value).toString(m_selectionFormat);
}新しいカスタムフォーマッタを QML から利用可能にするには、それを宣言し、QML モジュールとして設定します。その方法については、「Surface Graph Gallery」を参照してください。
QML
QMLコードでは、各次元ごとに異なる軸を定義します:
axisZ: valueAxis
axisY: logAxis
axisX: dateAxisZ軸は、通常のValue3DAxis です:
Value3DAxis {
id: valueAxis
segmentCount: 5
subSegmentCount: 2
labelFormat: "%.2f"
min: 0
max: 10
}Y軸については、対数軸を定義します。Value3DAxis に対数スケールを表示させるには、軸のformatter プロパティにLogValue3DAxisFormatter を指定します:
Value3DAxis {
id: logAxis
formatter: LogValue3DAxisFormatter {
id: logAxisFormatter
base: 10
autoSubGrid: true
edgeLabelsVisible: true
}
labelFormat: "%.2f"
}そして最後に、X軸には新しいCustomFormatter を使用します:
Value3DAxis {
id: dateAxis
formatter: CustomFormatter {
originDate: "2023-01-01"
selectionFormat: "yyyy-MM-dd HH:mm:ss"
}
subSegmentCount: 2
labelFormat: "yyyy-MM-dd"
min: 0
max: 14
}アプリケーションの残りの部分は、軸の変更やグラフの表示を行う、比較的直感的なロジックで構成されています。
サンプル内容
© 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.