- Qt 6.5
- Qt Quick 3D
- Qt Quick 3D Examples and Tutorials
- Qt Quick 3D - Robot Arm Example
- Qt Quick 3D | Commercial or GPLv3

# Qt Quick 3D - Robot Arm Example

     // Copyright (C) 2022 The Qt Company Ltd.
     // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

     import QtQuick
     import QtQuick3D
     import QtQuick.Controls.Material
     import QtQuick.Controls
     import QtQuick.Layouts
     import RobotArm
     import Backend
     import QtQml

     Pane {
         id: root
         Material.theme: darkModeToggle.checked ? Material.Dark : Material.Light

         readonly property bool mobile: Qt.platform.os === "android"
         readonly property bool horizontal: width > height
         property real sliderWidth: width * 0.15
         property real buttonRowWidth: width * 0.12
         property real buttonMinWidth: 65

         leftPadding: 60
         rightPadding: 60
         topPadding: 50
         bottomPadding: 50

         width: 800
         height: 600
         state: "mobileHorizontal"

         Backend {
             id: backend
             rotation1Angle: rotation1Slider.value
             rotation2Angle: rotation2Slider.value
             rotation3Angle: rotation3Slider.value
             rotation4Angle: rotation4Slider.value
             clawsAngle: clawToggle.checked ? 0 : 90
         }

         Toggle {
             id: darkModeToggle
             text: qsTr("Dark mode")
             anchors.top: parent.top
         }

         ColumnLayout {
             id: slidersColumn
             spacing: 6
             anchors.bottom: parent.bottom

             LabeledSlider {
                 id: rotation1Slider
                 Layout.preferredWidth: root.sliderWidth
                 Layout.minimumWidth: 160
                 labelText: "Rotation 1"
                 from: -90
                 to: 90
                 value: 60
             }

             LabeledSlider {
                 id: rotation2Slider
                 Layout.preferredWidth: root.sliderWidth
                 Layout.minimumWidth: 160
                 labelText: "Rotation 2"
                 from: -135
                 to: 135
                 value: 45
             }

             LabeledSlider {
                 id: rotation3Slider
                 Layout.preferredWidth: root.sliderWidth
                 Layout.minimumWidth: 160
                 labelText: "Rotation 3"
                 from: -90
                 to: 90
                 value: 45
             }

             LabeledSlider {
                 id: rotation4Slider
                 Layout.preferredWidth: root.sliderWidth
                 Layout.minimumWidth: 160
                 labelText: "Rotation 4"
                 from: -180
                 to: 180
             }
         }

         Toggle {
             id: clawToggle
             text: qsTr("Claw")
             anchors.bottom: slidersColumn.top
             anchors.bottomMargin: 30
         }

         GridLayout {
             id: buttonsRow
             columns: 2
             rows: 2
             columnSpacing: 16
             rowSpacing: 8
             anchors.bottom: clawToggle.top
             anchors.bottomMargin: 30

             Button {
                 id: pose1
                 text: qsTr("Pose 1")
                 Layout.preferredWidth: root.buttonRowWidth / 2
                 Layout.minimumWidth: root.buttonMinWidth
                 Layout.preferredHeight: 45

                 Connections {
                     target: pose1
                     onClicked: {
                         rotation1Slider.value = 30
                         rotation2Slider.value = 60
                         rotation3Slider.value = 90
                         rotation4Slider.value = 145
                     }
                 }
             }

             Button {
                 id: pose2
                 text: qsTr("Pose 2")
                 Layout.preferredWidth: root.buttonRowWidth / 2
                 Layout.minimumWidth: root.buttonMinWidth
                 Layout.preferredHeight: 45

                 Connections {
                     target: pose2
                     onClicked: {
                         rotation1Slider.value = 60
                         rotation2Slider.value = 45
                         rotation3Slider.value = 45
                         rotation4Slider.value = 60
                     }
                 }
             }

             Button {
                 id: pose3
                 text: qsTr("Pose 3")
                 Layout.preferredWidth: root.buttonRowWidth / 2
                 Layout.minimumWidth: root.buttonMinWidth
                 Layout.preferredHeight: 45

                 Connections {
                     target: pose3
                     onClicked: {
                         rotation1Slider.value = -90
                         rotation2Slider.value = -60
                         rotation3Slider.value = -45
                         rotation4Slider.value = -180
                     }
                 }
             }

             Button {
                 id: resetPose
                 text: qsTr("Reset")
                 Layout.preferredWidth: root.buttonRowWidth / 2
                 Layout.minimumWidth: root.buttonMinWidth
                 Layout.preferredHeight: 45

                 Connections {
                     target: resetPose
                     onClicked: {
                         rotation1Slider.value = 0
                         rotation2Slider.value = 0
                         rotation3Slider.value = 0
                         rotation4Slider.value = 0
                         clawToggle.checked = false
                     }
                 }
             }
         }

         View3D {
             anchors.fill: parent

             camera: camera
             Node {
                 id: scene

                 PointLight {
                     x: 760
                     z: 770
                     quadraticFade: 0
                     brightness: 1
                 }

                 DirectionalLight {
                     eulerRotation.z: 30
                     eulerRotation.y: -165
                 }

                 DirectionalLight {
                     y: 1000
                     brightness: 0.4
                     eulerRotation.z: -180
                     eulerRotation.y: 90
                     eulerRotation.x: -90
                 }

                 PerspectiveCamera {
                     id: camera
                     x: 1050
                     y: 375
                     z: -40
                     pivot.x: 200
                     eulerRotation.y: 85
                 }
                 RoboticArm {
                     id: roboArm
                     rotation1: backend.rotation1Angle
                     rotation2: backend.rotation2Angle
                     rotation3: backend.rotation3Angle
                     rotation4: backend.rotation4Angle
                     clawsAngle: backend.clawsAngle
                 }
             }

             NodeIndicator {
                 scenePosition: roboArm.hand_position
                 isFocused: clawToggle.hasFocus
                 label: clawToggle.text
                 size: 30
             }

             NodeIndicator {
                 scenePosition: roboArm.hand_hinge_position
                 isFocused: rotation1Slider.activeFocus
                 label: rotation1Slider.labelText
                 size: 40
             }

             NodeIndicator {
                 scenePosition: roboArm.arm_position
                 isFocused: rotation2Slider.activeFocus
                 label: rotation2Slider.labelText
                 size: 50
             }

             NodeIndicator {
                 scenePosition: roboArm.forearm_position
                 isFocused: rotation3Slider.activeFocus
                 label: rotation3Slider.labelText
                 size: 60
             }

             NodeIndicator {
                 scenePosition: roboArm.root_position
                 isFocused: rotation4Slider.activeFocus
                 label: rotation4Slider.labelText
                 size: 60
             }

             environment: sceneEnvironment

             SceneEnvironment {
                 id: sceneEnvironment
                 antialiasingQuality: SceneEnvironment.VeryHigh
                 antialiasingMode: SceneEnvironment.MSAA
             }
         }

         Label {
             id: robotStatus
             text: backend.status
             anchors.top: parent.top
             font.italic: true
             anchors.horizontalCenter: parent.horizontalCenter
             anchors.topMargin: 15
         }

         states: [
             State {
                 name: "mobileHorizontal"
                 when: root.mobile && root.horizontal

                 PropertyChanges {
                     target: root
                     leftPadding: 45
                     topPadding: 15
                     bottomPadding: 0
                     sliderWidth: width * 0.4
                     buttonRowWidth: width * 0.2
                     buttonMinWidth: 75
                 }

                 PropertyChanges {
                     target: roboArm
                     z: -200
                 }
             },
             State {
                 name: "desktopVertical"
                 when: !root.mobile && !root.horizontal

                 PropertyChanges {
                     target: root
                     sliderWidth: width * 0.4
                     buttonRowWidth: width * 0.2
                     bottomPadding: 20
                 }
                 AnchorChanges {
                     target: slidersColumn
                     anchors.right: parent.right
                 }
                 PropertyChanges {
                     target: slidersColumn
                     anchors.rightMargin: 20
                 }

                 AnchorChanges {
                     target: buttonsRow
                     anchors.bottom: undefined
                     anchors.top: slidersColumn.top
                 }

                 AnchorChanges {
                     target: clawToggle
                     anchors.bottom: undefined
                     anchors.top: buttonsRow.bottom
                 }

                 PropertyChanges {
                     target: roboArm
                     scale.x: 0.7
                     scale.y: 0.7
                     scale.z: 0.7
                     y: 250
                     z: 150
                 }
             },
             State {
                 name: "mobileVertical"
                 when: root.mobile && !root.horizontal

                 PropertyChanges {
                     target: root
                     sliderWidth: width * 0.85
                     topPadding: 15
                     leftPadding: 45
                     bottomPadding: 0
                     buttonRowWidth: width * 0.2
                     buttonMinWidth: 75
                 }

                 AnchorChanges {
                     target: slidersColumn
                     anchors.left: undefined
                     anchors.horizontalCenter: parent.horizontalCenter
                 }

                 AnchorChanges {
                     target: clawToggle
                     anchors.left: undefined
                     anchors.right: slidersColumn.right
                 }

                 AnchorChanges {
                     target: buttonsRow
                     anchors.bottom: slidersColumn.top
                     anchors.left: slidersColumn.left
                 }

                 PropertyChanges {
                     target: roboArm
                     scale.x: 0.7
                     scale.y: 0.7
                     scale.z: 0.7
                     y: 280
                     z: 100
                 }
             }
         ]

         transitions: Transition {
             PropertyAnimation {
                 properties: "sliderWidth, scale.x, scale.y, scale.z, y, z"
             }
             AnchorAnimation {}
         }
     }