이 페이지에서

조종석

그래프를 사용하여 비행기 조종석을 모방한 예제 애플리케이션.

속도계, 진동 그래프, 항법 지도 및 비행 표시기가 장착된 항공기 조종석 계기판

이 조종석 예제에서는 가상의 비행기 제어 화면을 그리기 위해 다양한 그래프 유형을 활용합니다. 예제에서 사용된 그래프는 다음과 같습니다:

예제 실행하기

다음 위치에서 예제를 실행할 수 있습니다:

속도계

속도계 뷰는 단일 GraphsView 내에 3개의 PieSeries 으로 구성되어 있으며, 두 다이얼의 바늘 역할은 QML Rectangles가 담당합니다.

GraphsTheme 의 backgroundVisible 속성은 false 로 설정되어 있습니다. 모든 PieSlices 에는 각각 고유한 사용자 정의 색상이 적용되어 있습니다. 중앙이 비어 있는 원형 눈금을 표시하기 위해, 이 예제에서는 holeSize 값을 달리한 PieSeries 를 사용합니다.

GraphsView {
    id: chart
    anchors.fill: parent
    anchors.margins: 20

    theme: GraphsTheme {
        backgroundVisible: false
        borderColors: ["#252525"]
    }

    PieSeries {
        id: pieOuter
        pieSize: 1
        holeSize: 0.8
        startAngle: -120
        endAngle: 120

        PieSlice { label: "Stall"; value: 1; color: "#ff0000"; labelVisible: false }
        PieSlice { label: "Optimal"; value: 4; color: "#00ff00"; labelVisible: false }
        PieSlice { label: "Overspeed"; value: 1; color: "#ffaa22"; labelVisible: false }
    }

    PieSeries {
        pieSize: 0.8
        holeSize: 0.6
        startAngle: -120
        endAngle: 120

        PieSlice { value: 1; color: "#ffffff"; }
        PieSlice { value: 4; color: "#252525"; }
        PieSlice { value: 1; color: "#ffffff"; }
        PieSlice { value: 4; color: "#252525"; }
        PieSlice { value: 1; color: "#ffffff"; }
        PieSlice { value: 4; color: "#252525"; }
        PieSlice { value: 1; color: "#ffffff"; }
        PieSlice { value: 4; color: "#252525"; }
        PieSlice { value: 1; color: "#ffffff"; }
        PieSlice { value: 4; color: "#252525"; }
        PieSlice { value: 1; color: "#ffffff"; }
        PieSlice { value: 4; color: "#252525"; }
        PieSlice { value: 1; color: "#ffffff"; }
        PieSlice { value: 4; color: "#252525"; }
        PieSlice { value: 1; color: "#ffffff"; }
        PieSlice { value: 4; color: "#252525"; }
        PieSlice { value: 1; color: "#ffffff"; }
        PieSlice { value: 4; color: "#252525"; }
        PieSlice { value: 1; color: "#ffffff"; }
    }

    PieSeries {
        pieSize: 0.6
        holeSize: 1.0
        startAngle: -120
        endAngle: 120
        verticalPosition: 1

        PieSlice { label: "Stall"; value: 1; color: "#ff0000"; labelVisible: false }
        PieSlice { label: "Optimal"; value: 4; color: "#00ff00"; labelVisible: false  }
        PieSlice { label: "Overspeed"; value: 1; color: "#ffaa22"; labelVisible: false  }
    }
}

진동 패널

진동 패널에는 런타임 시 동적으로 업데이트되는 LineSeries 가 포함되어 있습니다. 실행 후, QML의 invokeAppend 함수를 사용하여 일련의 점이 시리즈에 추가됩니다. 그런 다음 매 프레임마다 replace 함수를 사용하여 값을 계산된 값으로 대체합니다. 사용자는 진동 그래프 패널을 통해 이러한 값을 변경할 수 있습니다.

LineSeries {
    id: line
    property int divisions: 500
    property real amplitude: 0.5
    property real resolution: 0.5

    FrameAnimation {
        running: true

        onTriggered: {
            for (let i = 0; i < line.divisions; ++i) {
                let y = Math.sin(line.resolution*i)
                y *= Math.cos(i)
                y *= Math.sin(i / line.divisions * 3.2) * 3 * line.amplitude * Math.random()

               line.replace(i, (i/line.divisions) * 8.0, y + 4)
            }
        }
    }

    Component.onCompleted: {
        for (let i = 1; i <= divisions; ++i) {
            append((i/divisions) * 8.0, 4.0)
        }
    }

    function change(newDivs) {
        let delta = newDivs - divisions

        if (delta < 0) {
            delta = Math.abs(delta)
            removeMultiple(count - 1 - delta, delta)
        } else {
            for (let i = 0; i < delta; ++i) {
                append(((count + i)/divisions) * 8.0, 4.0)
            }
        }

        divisions = newDivs
    }
}

진동 그래프 패널

이 패널에는 진동 패널에 표시되는 시각화에 영향을 미치는 값을 조정할 수 있는 제어 노브가 포함되어 있습니다.

탐색 맵은 두 가지 주요 섹션으로 구성됩니다:

  • 왼쪽 패널에는 각 막대의 음영을 지정할 때 ‘ BarSets ’ 색상을 활용하는 사용자 정의 셰이더가 적용된 여러 개의 ‘ BarSeries ’가 포함되어 있습니다.
    BarSeries {
        property real barOpacity: 0.
    
        id: barSeries
        barsType: BarSeries.BarsType.Stacked
        barWidth: 0.2
    
        barDelegate: Item {
            id: delegate
            antialiasing: true
            property real barOpacity: 0.5
            property color barColor
            property string barLabel
    
            FrameAnimation {
                running: true
                onTriggered: {
                    delegate.barOpacity = Math.abs(Math.sin(elapsedTime))
                }
            }
            ShaderEffect {
                id: effect
                readonly property alias iTime: delegate.barOpacity
                readonly property alias iColor: delegate.barColor
                readonly property vector3d iResolution: Qt.vector3d(width, height, 1.0)
    
                blending: true
                fragmentShader: 'bar.frag.qsb'
                anchors.fill: parent
            }
        }
    
        BarSet { id: set1; label: "Low"; values: [1, 2, 3, 1]; color: "red" }
        BarSet { id: set2; label: "Medium"; values: [2, 2, 0, 4]; color: "yellow"}
        BarSet { id: set3; label: "High"; values: [3, 2, 3, 1]; color: "green"}
    }
  • 오른쪽 패널에는 다음이 포함되어 있습니다:
    • 가상의 호수를 시각화하는 두 개의 ‘ LineSeries ’가 포함된 ‘ AreaSeries ’입니다.
    • 그 위에 위치한 ScatterSeries 는 지도 위에 공항과 같은 사용자 정의 아이콘을 표시합니다.
    • ADD 버튼을 누르면 경로를 만들기 위해 점(point)을 이동할 수 있는 LineSeries 가 추가됩니다. 이 LineSeries 는 pointDelegate에 사용자 정의 셰이더를 사용하고, ScatterSeries 는 이미지를 사용합니다.
    AreaSeries {
        property double x: 0
        property double y: 0
    
        id: lake1
        color: "blue"
        upperSeries: LineSeries {
            id: s1
            XYPoint { x: 0.0; y: -3.5 }
            XYPoint { x: 1.0; y: -5.0 }
            XYPoint { x: 2.0; y: -2.5 }
            XYPoint { x: 2.5; y: -4.0 }
            XYPoint { x: 3.0; y: -4.2 }
        }
    
        lowerSeries: LineSeries {
            id: s2
            XYPoint { x: 0.0; y: -7.2 }
            XYPoint { x: 1.0; y: -7.0 }
            XYPoint { x: 2.0; y: -8.5 }
            XYPoint { x: 2.5; y: -8.0 }
            XYPoint { x: 3.0; y: -9.0 }
            XYPoint { x: 4.0; y: -6.5 }
        }
    }
    
    AreaSeries {
        property double x: 0
        property double y: 0
    
        id: lake2
        color: "blue"
        upperSeries: LineSeries {
            id: s3
            XYPoint { x: 0.0; y: 1.5 }
            XYPoint { x: 1.0; y: 3.0 }
            XYPoint { x: 2.0; y: 4.5 }
            XYPoint { x: 2.5; y: 4.8 }
            XYPoint { x: 3.0; y: 4.0 }
        }
    
        lowerSeries: LineSeries {
            id: s4
            XYPoint { x: 0.0; y: 0.0 }
            XYPoint { x: 1.0; y: 0.5 }
            XYPoint { x: 2.0; y: 0.2 }
            XYPoint { x: 2.5; y: 1.5 }
            XYPoint { x: 3.0; y: 1.0 }
            XYPoint { x: 4.0; y: 0.6 }
        }
    }
    
    // POI
    ScatterSeries {
        name: "Airport"
    
        pointDelegate: Image {
                source: "airplane-ico.png"
                mipmap: true
                width: 30
                height: 30
        }
    
        XYPoint{x: 4.0; y: 5.7}
        XYPoint{x: 2.2; y: 8.2}
        XYPoint{x: 6.4; y: 1.2}
        XYPoint{x: 7.4; y: 7.8}
    }
    
    LineSeries {
        id: linePath
        selectable: true
        draggable: true
        color: "white"
        pointDelegate: Item {
            width: 50
            height: 50
            property real pointValueX
            property real pointValueY
    
            FrameAnimation {
                id: scatterAnim
                running: true
            }
    
            ShaderEffect {
                readonly property vector3d iResolution: Qt.vector3d(width, height, 1.0)
                readonly property alias iTime: scatterAnim.elapsedTime
    
                blending: true
                fragmentShader: 'circleMarker.frag.qsb'
                anchors.fill: parent
            }
    
            Text {
                color: "white"
                font.pointSize: 4
                text: "LAT: " + pointValueX.toFixed(1) + ", " + "LON: " + pointValueY.toFixed(1)
            }
        }
    }

주 비행 디스플레이

주 비행 디스플레이는 지형의 "3D" 뷰를 재현한 기능을 제공합니다. 이는 지면과 하늘을 각각 사용자 정의 색상으로 표현하기 위해 두 개의 AreaSeries 로 구현되었습니다. FrameAnimation 는 이 시계열의 값을 업데이트합니다.

AreaSeries {
    id: upperArea
    color: "cyan"

    upperSeries: LineSeries {
        XYPoint {x: 0; y: 10}
        XYPoint {x: 10; y: 10}
    }

    lowerSeries: LineSeries {
        id: upperLine
        XYPoint {x: 0; y: 3}
        XYPoint {x: 10; y: 4}
    }
}

FrameAnimation {
    running: true
    onTriggered: {
        upperLine.replace(0, upperLine.at(0).x, Math.sin(elapsedTime) + 6)
        upperLine.replace(1, upperLine.at(1).x, Math.cos(elapsedTime) + 6)
        lowerLine.replace(0, lowerLine.at(0).x, Math.sin(elapsedTime) + 6)
        lowerLine.replace(1, lowerLine.at(1).x, Math.cos(elapsedTime) + 6)
        barSet.values = [Math.sin(elapsedTime) + 5]
    }
}

왼쪽에는 테마가 적용된 BarSeries 가 y축 레이블을 표시합니다. 이 BarSeries 는 막대 표시를 위해 사용자 정의 셰이더를 사용합니다.

BarSeries {
    id: barSeries
    selectable: true
    barDelegate: Item {
        id: delegate
        antialiasing: true
        property real barOpacity: 0.5
        property color barColor
        property string barLabel

        ShaderEffect {
            id: effect
            readonly property vector3d iResolution: Qt.vector3d(width, height, 1.0)

            blending: true
            fragmentShader: 'pitchbar.frag.qsb'
            anchors.fill: parent
        }
    }

    BarSet { id: barSet; values: []; selectedColor: "red" }
}

뷰 하단에는 SplineSeries 가 비행기 아래의 지형 높이를 시각화합니다. 이 SplineSeries 는 매 프레임마다 업데이트됩니다. 시리즈를 업데이트하는 코드는 연속적인 점 시퀀스를 뒤쪽에 추가하고 앞쪽에서 제거합니다. 마우스를 올렸을 때, SplineSeries 는 y축 값(고도)을 표시하는 툴팁을 보여줍니다.

GraphsView {
    anchors.fill: parent
    anchors.leftMargin: -90
    anchors.rightMargin: -80
    anchors.bottomMargin: -30

    theme: GraphsTheme {
        backgroundVisible: false
        plotAreaBackgroundColor: "#11000000"
    }

    axisX: ValueAxis {
        max: 10
        subTickCount: 9
        lineVisible: false
        gridVisible: false
        subGridVisible: false
        labelsVisible: false
        visible: false
    }

    axisY: ValueAxis {
        max: 10
        subTickCount: 9
        lineVisible: false
        gridVisible: false
        subGridVisible: false
        labelsVisible: false
        visible: false
    }

    ToolTip {
        id: tooltip
    }

    onHoverEnter: {
        tooltip.visible = true;
    }

    onHoverExit: {
        tooltip.visible = false;
    }

    onHover: (seriesName, position, value) => {
                 tooltip.x = position.x + 1;
                 tooltip.y = position.y + 1;
                 tooltip.text = "Altitude: " + (value.y * 1000).toFixed(1) + "m";
             }

    FrameAnimation {
        property var points: []

        Component.onCompleted: {
            for (let i = 0; i < altitudeLine.count; ++i) {
                points[i] = altitudeLine.at(i)
            }
        }

        running: true
        onTriggered: {
            for (let i = 0; i < points.length; ++i) {
                points[i].x -= frameTime

                if (points[1].x <= -2) {
                    let p = points[0]
                    p.x = points[points.length - 1].x + 1
                    altitudeLine.append(p)
                    altitudeLine.remove(0)

                    points.length = 0

                    for (let i = 0; i < altitudeLine.count; ++i) {
                        points[i] = altitudeLine.at(i)
                    }
                }
            }
            altitudeLine.replace(points)
            altitudeLine.update()
        }
    }

    SplineSeries {
        id: altitudeLine
        hoverable: true
        width: 3

        XYPoint {x: 0; y: 5}
        XYPoint {x: 1; y: 2}
        XYPoint {x: 2; y: 5}
        XYPoint {x: 3; y: 4}
        XYPoint {x: 4; y: 6}
        XYPoint {x: 5; y: 7}
        XYPoint {x: 6; y: 9}
        XYPoint {x: 7; y: 8}
        XYPoint {x: 8; y: 9}
        XYPoint {x: 9; y: 6}
        XYPoint {x: 10; y: 6}
        XYPoint {x: 11; y: 6}
        XYPoint {x: 12; y: 1}
        XYPoint {x: 13; y: 9}
        XYPoint {x: 14; y: 1}
    }

}

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

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