diff options
author | Kimmo Leppälä <kimmo.leppala@qt.io> | 2023-04-14 22:21:43 +0300 |
---|---|---|
committer | Kai Köhne <kai.koehne@qt.io> | 2023-05-09 18:37:33 +0200 |
commit | 02efcba73d7f7d1d1eb49a6741558b2b6528838b (patch) | |
tree | 6cc527060bb4385220ffced8358c689702a8b16f | |
parent | 8cf277e3f21d8170964363888ebb6f01c730f013 (diff) | |
download | qtdoc-02efcba73d7f7d1d1eb49a6741558b2b6528838b.tar.gz |
Move robotarm example from quick3d to qtdoc
Pick-to: 6.5
Change-Id: Iac113bc7ed773f291ef09ebe144e1de1e0e58136
Reviewed-by: Kai Köhne <kai.koehne@qt.io>
45 files changed, 1349 insertions, 0 deletions
diff --git a/examples/demos/CMakeLists.txt b/examples/demos/CMakeLists.txt index 983ab909..de4bc228 100644 --- a/examples/demos/CMakeLists.txt +++ b/examples/demos/CMakeLists.txt @@ -26,3 +26,6 @@ endif() if(TARGET Qt6::Widgets) qt_internal_add_example(documentviewer) endif() +if(TARGET Qt::Quick AND TARGET Qt::QuickControls2 AND TARGET Qt::Quick3D) + qt_internal_add_example(robotarm) +endif() diff --git a/examples/demos/robotarm/Backend/CMakeLists.txt b/examples/demos/robotarm/Backend/CMakeLists.txt new file mode 100644 index 00000000..4d2e1eb4 --- /dev/null +++ b/examples/demos/robotarm/Backend/CMakeLists.txt @@ -0,0 +1,14 @@ +find_package(Qt6 REQUIRED COMPONENTS Gui) + +qt_add_qml_module(backendmodule + URI Backend + VERSION 1.0 + SOURCES + animatedparam.cpp + animatedparam.h + backend.cpp + backend.h + RESOURCE_PREFIX "/" +) + +target_link_libraries(backendmodule PUBLIC Qt6::Gui) diff --git a/examples/demos/robotarm/Backend/animatedparam.cpp b/examples/demos/robotarm/Backend/animatedparam.cpp new file mode 100644 index 00000000..e72678e3 --- /dev/null +++ b/examples/demos/robotarm/Backend/animatedparam.cpp @@ -0,0 +1,35 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "animatedparam.h" + +#include <QVariantAnimation> + +AnimatedParam::AnimatedParam(QObject *parent) : QVariantAnimation(parent) +{ + setDuration(1500); + setEasingCurve(QEasingCurve::InOutCubic); + + connect(this, &QVariantAnimation::valueChanged, this, &AnimatedParam::valueChanged); + connect(this, &QAbstractAnimation::stateChanged, this, [this](QAbstractAnimation::State newState, QAbstractAnimation::State) { + m_isRunning = (newState == QAbstractAnimation::Running); + }); +} + +int AnimatedParam::value() const +{ + return currentValue().toInt(); +} + +void AnimatedParam::setValue(int newValue) +{ + stop(); + setStartValue(value()); + setEndValue(newValue); + start(); +} + +bool AnimatedParam::isRunning() const +{ + return m_isRunning; +} diff --git a/examples/demos/robotarm/Backend/animatedparam.h b/examples/demos/robotarm/Backend/animatedparam.h new file mode 100644 index 00000000..e88576f7 --- /dev/null +++ b/examples/demos/robotarm/Backend/animatedparam.h @@ -0,0 +1,32 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef ANIMATEDPARAM_H +#define ANIMATEDPARAM_H + +#include <QProperty> +#include <QVariantAnimation> + +//! [class definition] +class AnimatedParam : public QVariantAnimation +{ + Q_OBJECT + Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged) + //! [class definition] + +public: + AnimatedParam(QObject *parent = nullptr); + + int value() const; + void setValue(int newValue); + + bool isRunning() const; + +signals: + void valueChanged(); + +private: + QProperty<bool> m_isRunning; +}; + +#endif // ANIMATEDPARAM_H diff --git a/examples/demos/robotarm/Backend/backend.cpp b/examples/demos/robotarm/Backend/backend.cpp new file mode 100644 index 00000000..e51aa13e --- /dev/null +++ b/examples/demos/robotarm/Backend/backend.cpp @@ -0,0 +1,115 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "backend.h" +#include <QTransform> + +Backend::Backend(QObject *parent) : QObject(parent) +{ + connect(&m_rotation1Angle, &AnimatedParam::valueChanged, this, &Backend::rot1AngleChanged); + connect(&m_rotation2Angle, &AnimatedParam::valueChanged, this, &Backend::rot2AngleChanged); + connect(&m_rotation3Angle, &AnimatedParam::valueChanged, this, &Backend::rot3AngleChanged); + connect(&m_rotation4Angle, &AnimatedParam::valueChanged, this, &Backend::rot4AngleChanged); + connect(&m_clawsAngle, &AnimatedParam::valueChanged, this, &Backend::clawsAngleChanged); + + m_status.setBinding([this]() { + return m_isCollision.value() ? QString("Collision!") + : m_rotation1Angle.isRunning() || m_rotation2Angle.isRunning() || m_rotation3Angle.isRunning() + || m_rotation4Angle.isRunning() + ? QString("Busy") + : QString("Ready"); + }); + + connect(&m_rotation1Angle, &AnimatedParam::valueChanged, this, &Backend::detectCollision); + connect(&m_rotation2Angle, &AnimatedParam::valueChanged, this, &Backend::detectCollision); + connect(&m_rotation3Angle, &AnimatedParam::valueChanged, this, &Backend::detectCollision); + connect(&m_rotation4Angle, &AnimatedParam::valueChanged, this, &Backend::detectCollision); +} + +int Backend::rotation1Angle() const +{ + return m_rotation1Angle.value(); +} + +void Backend::setRot1Angle(const int angle) +{ + m_rotation1Angle.setValue(angle); +} + +int Backend::rotation2Angle() const +{ + return m_rotation2Angle.value(); +} + +void Backend::setRot2Angle(const int angle) +{ + m_rotation2Angle.setValue(angle); +} + +int Backend::rotation3Angle() const +{ + return m_rotation3Angle.value(); +} + +void Backend::setRot3Angle(const int angle) +{ + m_rotation3Angle.setValue(angle); +} + +int Backend::rotation4Angle() const +{ + return m_rotation4Angle.value(); +} + +void Backend::setRot4Angle(const int angle) +{ + m_rotation4Angle.setValue(angle); +} + +int Backend::clawsAngle() const +{ + return m_clawsAngle.value(); +} + +void Backend::setClawsAngle(const int angle) +{ + m_clawsAngle.setValue(angle); +} + +QString Backend::status() const +{ + return m_status; +} + +QBindable<QString> Backend::bindableStatus() const +{ + return &m_status; +} + +void Backend::detectCollision() +{ + // simple aproximate collision detection, uses hardcoded model dimensions + + QPolygon pol1(QRect(-70, 0, 70, 300)); + + QTransform t; + + t.rotate(8.7); + t.translate(0, 259); + + t.rotate(-20.); + t.rotate(rotation3Angle()); + + QPolygon pol2 = t.mapToPolygon(QRect(-35, 0, 35, 233)); + t.translate(0, 233); + t.rotate(15); + t.rotate(rotation2Angle()); + + QPolygon pol3 = t.mapToPolygon(QRect(-27, 0, 27, 212)); + t.translate(0, 212); + t.rotate(rotation1Angle()); + + QPolygon pol4 = t.mapToPolygon(QRect(-42, 0, 42, 180)); + + m_isCollision.setValue(pol1.intersects(pol3) || pol1.intersects(pol4) || pol2.intersects(pol4)); +} diff --git a/examples/demos/robotarm/Backend/backend.h b/examples/demos/robotarm/Backend/backend.h new file mode 100644 index 00000000..e7b92234 --- /dev/null +++ b/examples/demos/robotarm/Backend/backend.h @@ -0,0 +1,66 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef BACKEND_H +#define BACKEND_H + +#include "animatedparam.h" + +#include <QObject> +#include <qqmlregistration.h> + +//! [class definition] +class Backend : public QObject +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int rotation1Angle READ rotation1Angle WRITE setRot1Angle NOTIFY rot1AngleChanged) + Q_PROPERTY(int rotation2Angle READ rotation2Angle WRITE setRot2Angle NOTIFY rot2AngleChanged) + Q_PROPERTY(int rotation3Angle READ rotation3Angle WRITE setRot3Angle NOTIFY rot3AngleChanged) + Q_PROPERTY(int rotation4Angle READ rotation4Angle WRITE setRot4Angle NOTIFY rot4AngleChanged) + Q_PROPERTY(int clawsAngle READ clawsAngle WRITE setClawsAngle NOTIFY clawsAngleChanged) + Q_PROPERTY(QString status READ status BINDABLE bindableStatus) + //! [class definition] + +public: + explicit Backend(QObject *parent = nullptr); + + int rotation1Angle() const; + void setRot1Angle(const int angle); + + int rotation2Angle() const; + void setRot2Angle(const int angle); + + int rotation3Angle() const; + void setRot3Angle(const int angle); + + int rotation4Angle() const; + void setRot4Angle(const int angle); + + int clawsAngle() const; + void setClawsAngle(const int angle); + + QString status() const; + QBindable<QString> bindableStatus() const; + +signals: + void rot1AngleChanged(); + void rot2AngleChanged(); + void rot3AngleChanged(); + void rot4AngleChanged(); + void clawsAngleChanged(); + +private: + AnimatedParam m_rotation1Angle; + AnimatedParam m_rotation2Angle; + AnimatedParam m_rotation3Angle; + AnimatedParam m_rotation4Angle; + AnimatedParam m_clawsAngle; + + QProperty<QString> m_status; + QProperty<bool> m_isCollision; + + void detectCollision(); +}; + +#endif // BACKEND_H diff --git a/examples/demos/robotarm/CMakeLists.txt b/examples/demos/robotarm/CMakeLists.txt new file mode 100644 index 00000000..d718b18c --- /dev/null +++ b/examples/demos/robotarm/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.16) + +project(RobotArm LANGUAGES CXX) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quick3d/robotarm") + +find_package(Qt6 COMPONENTS Gui Qml Quick) +qt_standard_project_setup() +qt_add_executable(RobotArmApp src/main.cpp) + +qt_add_resources(RobotArmApp "configuration" + PREFIX "/" + FILES + qtquickcontrols2.conf +) + +target_link_libraries(RobotArmApp PUBLIC + Qt::Core + Qt::Gui + Qt::Quick +) + +add_subdirectory(Backend) + +include(${CMAKE_CURRENT_SOURCE_DIR}/qmlmodules) diff --git a/examples/demos/robotarm/RobotArm.qmlproject b/examples/demos/robotarm/RobotArm.qmlproject new file mode 100644 index 00000000..69a8958f --- /dev/null +++ b/examples/demos/robotarm/RobotArm.qmlproject @@ -0,0 +1,92 @@ +import QmlProject + +Project { + mainFile: "content/App.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "content" + } + + QmlFiles { + directory: "imports" + } + + QmlFiles { + directory: "backend_mock" + } + + JavaScriptFiles { + directory: "content" + } + + JavaScriptFiles { + directory: "imports" + } + + ImageFiles { + directory: "content" + } + + Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + Files { + filter: "qmldir" + directory: "." + } + + Files { + filter: "*.ttf;*.otf" + } + + Files { + filter: "*.wav;*.mp3" + } + + Files { + filter: "*.mp4" + } + + Files { + filter: "*.glsl;*.glslv;*.glslf;*.vsh;*.fsh;*.vert;*.frag" + } + + Files { + filter: "*.mesh" + directory: "asset_imports" + } + + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + QT_LOGGING_RULES: "qt.qml.connections=false" + QT_ENABLE_HIGHDPI_SCALING: "0" + /* Useful for debugging + QSG_VISUALIZE=batches + QSG_VISUALIZE=clip + QSG_VISUALIZE=changes + QSG_VISUALIZE=overdraw + */ + } + + qt6Project: true + + /* List of plugin directories passed to QML runtime */ + importPaths: [ "imports", "asset_imports", "backend_mock" ] + + /* Required for deployment */ + targetDirectory: "/opt/RobotArm" + + qdsVersion: "3.0" + + /* If any modules the project imports require widgets (e.g. QtCharts), widgetApp must be true */ + widgetApp: true + + multilanguageSupport: true + supportedLanguages: ["en"] + primaryLanguage: "en" + +} diff --git a/examples/demos/robotarm/backend_mock/Backend/BackendMock.qml b/examples/demos/robotarm/backend_mock/Backend/BackendMock.qml new file mode 100644 index 00000000..3ee8c7f2 --- /dev/null +++ b/examples/demos/robotarm/backend_mock/Backend/BackendMock.qml @@ -0,0 +1,41 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +QtObject { + property int rotation1Angle + property int rotation2Angle + property int rotation3Angle + property int rotation4Angle + property int clawsAngle + readonly property string status: "Ready" + + Behavior on rotation1Angle { + SmoothedAnimation { + velocity: 100 + } + } + + Behavior on rotation2Angle { + SmoothedAnimation { + velocity: 80 + } + } + + Behavior on rotation3Angle { + SmoothedAnimation { + velocity: 60 + } + } + + Behavior on rotation4Angle { + SmoothedAnimation { + velocity: 60 + } + } + + Behavior on clawsAngle { + SmoothedAnimation {} + } +} diff --git a/examples/demos/robotarm/backend_mock/Backend/qmldir b/examples/demos/robotarm/backend_mock/Backend/qmldir new file mode 100644 index 00000000..c5f8357e --- /dev/null +++ b/examples/demos/robotarm/backend_mock/Backend/qmldir @@ -0,0 +1,2 @@ +module Backend +Backend 1.0 BackendMock.qml diff --git a/examples/demos/robotarm/content/App.qml b/examples/demos/robotarm/content/App.qml new file mode 100644 index 00000000..1cd1fdf9 --- /dev/null +++ b/examples/demos/robotarm/content/App.qml @@ -0,0 +1,20 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import RobotArm + +Window { + width: Constants.width + height: Constants.height + + minimumWidth: 800 + minimumHeight: 600 + + visible: true + title: "RobotArm" + + MainScreen { + anchors.fill: parent + } +} diff --git a/examples/demos/robotarm/content/CMakeLists.txt b/examples/demos/robotarm/content/CMakeLists.txt new file mode 100644 index 00000000..5a6d289f --- /dev/null +++ b/examples/demos/robotarm/content/CMakeLists.txt @@ -0,0 +1,31 @@ +### This file is automatically generated by Qt Design Studio. +### Do not change + +qt_add_library(content STATIC) +qt6_add_qml_module(content + URI "content" + VERSION 1.0 + QML_FILES + App.qml + LabeledSlider.ui.qml + MainScreen.ui.qml + NodeIndicator.qml + RoboticArm.ui.qml + Toggle.ui.qml + RESOURCES + fonts/fonts.txt + maps/qt.png + meshes/arm.mesh + meshes/base.mesh + meshes/forearm.mesh + meshes/hand.mesh + meshes/hand_grab_b.mesh + meshes/hand_grab_b_hinge_1.mesh + meshes/hand_grab_b_hinge_2.mesh + meshes/hand_grab_t.mesh + meshes/hand_grab_t_hinge_1.mesh + meshes/hand_grab_t_hinge_2.mesh + meshes/hand_hinge.mesh + meshes/root.mesh + RESOURCE_PREFIX "/" +) diff --git a/examples/demos/robotarm/content/LabeledSlider.ui.qml b/examples/demos/robotarm/content/LabeledSlider.ui.qml new file mode 100644 index 00000000..58a328bc --- /dev/null +++ b/examples/demos/robotarm/content/LabeledSlider.ui.qml @@ -0,0 +1,29 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +/* +This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only. +It is supposed to be strictly declarative and only uses a subset of QML. If you edit +this file manually, you might introduce QML code that is not supported by Qt Design Studio. +Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files. +*/ +import QtQuick +import QtQuick.Controls + +Slider { + property string labelText: qsTr("Text") + stepSize: 1 + + Label { + text: parent.labelText + anchors.left: parent.left + anchors.bottom: parent.top + bottomPadding: -12 + } + Label { + text: parent.value + anchors.right: parent.right + anchors.bottom: parent.top + bottomPadding: -12 + } +} diff --git a/examples/demos/robotarm/content/MainScreen.ui.qml b/examples/demos/robotarm/content/MainScreen.ui.qml new file mode 100644 index 00000000..1af531e3 --- /dev/null +++ b/examples/demos/robotarm/content/MainScreen.ui.qml @@ -0,0 +1,389 @@ +// 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 {} + } +} diff --git a/examples/demos/robotarm/content/NodeIndicator.qml b/examples/demos/robotarm/content/NodeIndicator.qml new file mode 100644 index 00000000..9a3fc0c1 --- /dev/null +++ b/examples/demos/robotarm/content/NodeIndicator.qml @@ -0,0 +1,58 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Material +import QtQuick3D +import RobotArm + +Item { + id: root + property real size: 100 + property bool isFocused: true + property View3D view3D: parent + property vector3d scenePosition + property vector3d screenPosition + property alias label: label.text + + x: screenPosition.x + y: screenPosition.y + + visible: x > 0 && y > 0 + + Label { + id: label + enabled: root.isFocused + anchors.bottom: rect.top + anchors.horizontalCenter: rect.horizontalCenter + } + + Rectangle { + id: rect + width: root.size + height: root.size + color: "Transparent" + radius: width / 2 + border.width: 2 + border.color: root.isFocused ? Material.accentColor : Material.secondaryTextColor + + anchors.horizontalCenter: parent.left + anchors.verticalCenter: parent.top + } + + Component.onCompleted: { + screenPosition = Qt.binding(function() { + let w = view3D.width // force the binding to update when width or height changes + let h = view3D.height + + return view3D.mapFrom3DScene(scenePosition) + } ) + } +} + +/*##^## +Designer { + D{i:0;autoSize:true;height:480;width:640} +} +##^##*/ diff --git a/examples/demos/robotarm/content/RoboticArm.ui.qml b/examples/demos/robotarm/content/RoboticArm.ui.qml new file mode 100644 index 00000000..c8554de6 --- /dev/null +++ b/examples/demos/robotarm/content/RoboticArm.ui.qml @@ -0,0 +1,163 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick3D + +Node { + id: rootNode + property int rotation1 + property int rotation2 + property int rotation3 + property int rotation4 + property int clawsAngle + + readonly property alias hand_position: hand_grab_t.scenePosition + readonly property alias hand_hinge_position: hand_hinge.scenePosition + readonly property alias arm_position: arm.scenePosition + readonly property alias forearm_position: forearm.scenePosition + readonly property alias root_position: root.scenePosition + + Model { + id: base + scale.x: 100 + scale.y: 100 + scale.z: 100 + source: "meshes/base.mesh" + eulerRotation.x: -90 + + DefaultMaterial { + id: steel_material + diffuseColor: "#ff595959" + } + + DefaultMaterial { + id: plastic_material + } + materials: [steel_material, plastic_material] + + Model { + id: root + y: -5.96047e-08 + z: 1.0472 + eulerRotation.z: rotation4 + source: "meshes/root.mesh" + + DefaultMaterial { + id: plastic_color_material + diffuseColor: "#41cd52" + } + materials: [plastic_material, plastic_color_material, steel_material] + + Model { + id: forearm + x: 5.32907e-15 + y: -0.165542 + z: 1.53472 + eulerRotation.x: rotation3 + source: "meshes/forearm.mesh" + materials: [plastic_material, steel_material] + + Model { + id: arm + x: -7.43453e-07 + y: 0.667101 + z: 2.23365 + eulerRotation.x: rotation2 + source: "meshes/arm.mesh" + + DefaultMaterial { + id: plastic_qt_material + diffuseMap: Texture { + source: "maps/qt.png" + pivotU: 0.5 + pivotV: 0.5 + generateMipmaps: true + mipFilter: Texture.Linear + } + } + materials: [plastic_material, plastic_qt_material, steel_material] + + Model { + id: hand_hinge + x: 7.43453e-07 + y: 0.0635689 + z: 2.12289 + eulerRotation.x: rotation1 + source: "meshes/hand_hinge.mesh" + materials: [plastic_material] + + Model { + id: hand + x: 3.35649e-06 + y: 2.38419e-07 + z: 0.366503 + source: "meshes/hand.mesh" + materials: [plastic_material, steel_material] + + Model { + id: hand_grab_t_hinge_2 + x: -9.5112e-07 + y: 0.323057 + z: 0.472305 + eulerRotation: hand_grab_t_hinge_1.eulerRotation + source: "meshes/hand_grab_t_hinge_2.mesh" + materials: [steel_material] + } + + Model { + id: hand_grab_t_hinge_1 + x: -9.3061e-07 + y: 0.143685 + z: 0.728553 + eulerRotation.x: clawsAngle * -1 + source: "meshes/hand_grab_t_hinge_1.mesh" + materials: [steel_material] + + Model { + id: hand_grab_t + x: -2.42588e-06 + y: -0.0327932 + z: 0.414757 + eulerRotation.x: hand_grab_t_hinge_1.eulerRotation.x * -1 + source: "meshes/hand_grab_t.mesh" + materials: [plastic_color_material, steel_material] + } + } + + Model { + id: hand_grab_b_hinge_1 + x: -9.38738e-07 + y: -0.143685 + z: 0.728553 + eulerRotation.x: clawsAngle + source: "meshes/hand_grab_b_hinge_1.mesh" + materials: [steel_material] + + Model { + id: hand_grab_b + x: -2.41775e-06 + y: 0.0327224 + z: 0.413965 + eulerRotation.x: hand_grab_b_hinge_1.eulerRotation.x * -1 + source: "meshes/hand_grab_b.mesh" + materials: [plastic_color_material, steel_material] + } + } + + Model { + id: hand_grab_b_hinge_2 + x: -9.5112e-07 + y: -0.323058 + z: 0.472305 + eulerRotation: hand_grab_b_hinge_1.eulerRotation + source: "meshes/hand_grab_b_hinge_2.mesh" + materials: [steel_material] + } + } + } + } + } + } + } +} diff --git a/examples/demos/robotarm/content/Toggle.ui.qml b/examples/demos/robotarm/content/Toggle.ui.qml new file mode 100644 index 00000000..98812219 --- /dev/null +++ b/examples/demos/robotarm/content/Toggle.ui.qml @@ -0,0 +1,30 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +/* +This is a UI file (.ui.qml) that is intended to be edited in Qt Design Studio only. +It is supposed to be strictly declarative and only uses a subset of QML. If you edit +this file manually, you might introduce QML code that is not supported by Qt Design Studio. +Check out https://doc.qt.io/qtcreator/creator-quick-ui-forms.html for details on .ui.qml files. +*/ +import QtQuick +import QtQuick.Controls + +Item { + property string text + property alias checked: toggleIndicator.checked + readonly property alias hasFocus: toggleIndicator.activeFocus + implicitWidth: toggleText.width + toggleIndicator.width + implicitHeight: 50 + + Label { + id: toggleText + text: parent.text + anchors.verticalCenter: toggleIndicator.verticalCenter + } + Switch { + id: toggleIndicator + anchors.left: toggleText.right + anchors.rightMargin: 8 + } +} diff --git a/examples/demos/robotarm/content/fonts/fonts.txt b/examples/demos/robotarm/content/fonts/fonts.txt new file mode 100644 index 00000000..ab961220 --- /dev/null +++ b/examples/demos/robotarm/content/fonts/fonts.txt @@ -0,0 +1 @@ +Fonts in this folder are loaded automatically. diff --git a/examples/demos/robotarm/content/maps/qt.png b/examples/demos/robotarm/content/maps/qt.png Binary files differnew file mode 100644 index 00000000..45171461 --- /dev/null +++ b/examples/demos/robotarm/content/maps/qt.png diff --git a/examples/demos/robotarm/content/meshes/arm.mesh b/examples/demos/robotarm/content/meshes/arm.mesh Binary files differnew file mode 100644 index 00000000..b005076b --- /dev/null +++ b/examples/demos/robotarm/content/meshes/arm.mesh diff --git a/examples/demos/robotarm/content/meshes/base.mesh b/examples/demos/robotarm/content/meshes/base.mesh Binary files differnew file mode 100644 index 00000000..f8506c85 --- /dev/null +++ b/examples/demos/robotarm/content/meshes/base.mesh diff --git a/examples/demos/robotarm/content/meshes/forearm.mesh b/examples/demos/robotarm/content/meshes/forearm.mesh Binary files differnew file mode 100644 index 00000000..690c577e --- /dev/null +++ b/examples/demos/robotarm/content/meshes/forearm.mesh diff --git a/examples/demos/robotarm/content/meshes/hand.mesh b/examples/demos/robotarm/content/meshes/hand.mesh Binary files differnew file mode 100644 index 00000000..c87009e6 --- /dev/null +++ b/examples/demos/robotarm/content/meshes/hand.mesh diff --git a/examples/demos/robotarm/content/meshes/hand_grab_b.mesh b/examples/demos/robotarm/content/meshes/hand_grab_b.mesh Binary files differnew file mode 100644 index 00000000..c1784f8a --- /dev/null +++ b/examples/demos/robotarm/content/meshes/hand_grab_b.mesh diff --git a/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_1.mesh b/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_1.mesh Binary files differnew file mode 100644 index 00000000..0c5686d5 --- /dev/null +++ b/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_1.mesh diff --git a/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_2.mesh b/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_2.mesh Binary files differnew file mode 100644 index 00000000..cf7ced56 --- /dev/null +++ b/examples/demos/robotarm/content/meshes/hand_grab_b_hinge_2.mesh diff --git a/examples/demos/robotarm/content/meshes/hand_grab_t.mesh b/examples/demos/robotarm/content/meshes/hand_grab_t.mesh Binary files differnew file mode 100644 index 00000000..f9ebd265 --- /dev/null +++ b/examples/demos/robotarm/content/meshes/hand_grab_t.mesh diff --git a/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_1.mesh b/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_1.mesh Binary files differnew file mode 100644 index 00000000..3e7a42bc --- /dev/null +++ b/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_1.mesh diff --git a/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_2.mesh b/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_2.mesh Binary files differnew file mode 100644 index 00000000..3e7a42bc --- /dev/null +++ b/examples/demos/robotarm/content/meshes/hand_grab_t_hinge_2.mesh diff --git a/examples/demos/robotarm/content/meshes/hand_hinge.mesh b/examples/demos/robotarm/content/meshes/hand_hinge.mesh Binary files differnew file mode 100644 index 00000000..7b0f0af0 --- /dev/null +++ b/examples/demos/robotarm/content/meshes/hand_hinge.mesh diff --git a/examples/demos/robotarm/content/meshes/root.mesh b/examples/demos/robotarm/content/meshes/root.mesh Binary files differnew file mode 100644 index 00000000..1501c2ed --- /dev/null +++ b/examples/demos/robotarm/content/meshes/root.mesh diff --git a/examples/demos/robotarm/doc/images/robotarm-example.png b/examples/demos/robotarm/doc/images/robotarm-example.png Binary files differnew file mode 100644 index 00000000..c8d15839 --- /dev/null +++ b/examples/demos/robotarm/doc/images/robotarm-example.png diff --git a/examples/demos/robotarm/doc/src/robotarm.qdoc b/examples/demos/robotarm/doc/src/robotarm.qdoc new file mode 100644 index 00000000..98000bd6 --- /dev/null +++ b/examples/demos/robotarm/doc/src/robotarm.qdoc @@ -0,0 +1,23 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \example demos/robotarm + \ingroup qtquickdemos + \title Robot Arm Example + \brief Demonstrates how to add a C++ backend to a 3D project from Qt Design Studio. + This example demonstrates adding a C++ backend to a 3D project created in Qt Design Studio. The example itself consists of + an interactive industrial robot arm in a Qt Quick 3D scene. The 2D UI to control the robot arm is implement using Qt Quick Controls. + \meta {tag} {demo,quick} + \image robotarm-example.png + + For Qt Design Studio the Robot Arm Example comes with a simple QML based QML module in backend_moc/Backend/Backend_moc.qml, + that serves as a backend for the project when using it with Qt Design Studio. + The C++ application implements a compatible backend as a C++ based QML module. Both QML modules implement the same API, which ensures + compatibility between the two modules. + + The Qt Quick 3D scene for the Robot Arm is defined in content/RoboticArm.ui.qml. The 2D UI is implemented in content/MainScreen.ui.qml + and is repsonsive and also supports a light and dark mode. The example uses the Material style from Qt Quick Controls and the dark and light + theme to implement both modes. + +*/ diff --git a/examples/demos/robotarm/imports/CMakeLists.txt b/examples/demos/robotarm/imports/CMakeLists.txt new file mode 100644 index 00000000..649d3bbf --- /dev/null +++ b/examples/demos/robotarm/imports/CMakeLists.txt @@ -0,0 +1,4 @@ +### This file is automatically generated by Qt Design Studio. +### Do not change + +add_subdirectory(RobotArm) diff --git a/examples/demos/robotarm/imports/RobotArm/CMakeLists.txt b/examples/demos/robotarm/imports/RobotArm/CMakeLists.txt new file mode 100644 index 00000000..2636a8f9 --- /dev/null +++ b/examples/demos/robotarm/imports/RobotArm/CMakeLists.txt @@ -0,0 +1,16 @@ +### This file is automatically generated by Qt Design Studio. +### Do not change + +qt_add_library(RobotArm STATIC) +set_source_files_properties(Constants.qml + PROPERTIES + QT_QML_SINGLETON_TYPE true +) + +qt6_add_qml_module(RobotArm + URI "RobotArm" + VERSION 1.0 + QML_FILES + Constants.qml + RESOURCE_PREFIX "/" +) diff --git a/examples/demos/robotarm/imports/RobotArm/Constants.qml b/examples/demos/robotarm/imports/RobotArm/Constants.qml new file mode 100644 index 00000000..65cfc6c4 --- /dev/null +++ b/examples/demos/robotarm/imports/RobotArm/Constants.qml @@ -0,0 +1,22 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +pragma Singleton +import QtQuick + +QtObject { + readonly property int width: 1280 + readonly property int height: 720 + + /* Edit this comment to add your custom font */ + readonly property font font: Qt.font({ + family: Qt.application.font.family, + pixelSize: Qt.application.font.pixelSize + }) + readonly property font largeFont: Qt.font({ + family: Qt.application.font.family, + pixelSize: Qt.application.font.pixelSize * 1.6 + }) + + readonly property color backgroundColor: "#c2c2c2" +} diff --git a/examples/demos/robotarm/imports/RobotArm/designer/plugin.metainfo b/examples/demos/robotarm/imports/RobotArm/designer/plugin.metainfo new file mode 100644 index 00000000..9acd6b59 --- /dev/null +++ b/examples/demos/robotarm/imports/RobotArm/designer/plugin.metainfo @@ -0,0 +1,13 @@ +MetaInfo { + Type { + name: "RobotArm.EventListSimulator" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + visibleInNavigator: true + canBeDroppedInNavigator: true + canBeDroppedInFormEditor: false + canBeDroppedInView3D: false + } + } +} diff --git a/examples/demos/robotarm/imports/RobotArm/qmldir b/examples/demos/robotarm/imports/RobotArm/qmldir new file mode 100644 index 00000000..3b7ff7bd --- /dev/null +++ b/examples/demos/robotarm/imports/RobotArm/qmldir @@ -0,0 +1,5 @@ +Module RobotArm +singleton Constants 1.0 Constants.qml +EventListSimulator 1.0 EventListSimulator.qml +EventListModel 1.0 EventListModel.qml +DirectoryFontLoader 1.0 DirectoryFontLoader.qml diff --git a/examples/demos/robotarm/main.qml b/examples/demos/robotarm/main.qml new file mode 100644 index 00000000..fc77418f --- /dev/null +++ b/examples/demos/robotarm/main.qml @@ -0,0 +1,11 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +/* This file is generated and only relevant for integrating the project into a Qt 6 and cmake based +C++ project. */ + +import QtQuick +import content + +App { +} diff --git a/examples/demos/robotarm/qmlmodules b/examples/demos/robotarm/qmlmodules new file mode 100644 index 00000000..0b3c1fcc --- /dev/null +++ b/examples/demos/robotarm/qmlmodules @@ -0,0 +1,23 @@ +### This file is automatically generated by Qt Design Studio. +### Do not change + +qt6_add_qml_module(RobotArmApp + URI "Main" + VERSION 1.0 + NO_PLUGIN + QML_FILES main.qml + RESOURCE_PREFIX "/" +) + +add_subdirectory(content) +add_subdirectory(imports) + +set(QML_IMPORT_PATH + ${CMAKE_BINARY_DIR} + CACHE STRING "") + +target_link_libraries(RobotArmApp PRIVATE + contentplugin + RobotArmplugin + backendmodule +) diff --git a/examples/demos/robotarm/qtquickcontrols2.conf b/examples/demos/robotarm/qtquickcontrols2.conf new file mode 100644 index 00000000..164b7114 --- /dev/null +++ b/examples/demos/robotarm/qtquickcontrols2.conf @@ -0,0 +1,6 @@ +[Controls] +Style=Material + +[Material] +Theme=Light +Accent=Green diff --git a/examples/demos/robotarm/src/app_environment.h b/examples/demos/robotarm/src/app_environment.h new file mode 100644 index 00000000..57407419 --- /dev/null +++ b/examples/demos/robotarm/src/app_environment.h @@ -0,0 +1,17 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +/* + * This file is automatically generated by Qt Design Studio. + * Do not change. +*/ + +#include <QGuiApplication> + +void set_qt_environment() +{ + qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1"); + qputenv("QT_ENABLE_HIGHDPI_SCALING", "0"); + qputenv("QT_LOGGING_RULES", "qt.qml.connections=false"); + qputenv("QT_QUICK_CONTROLS_CONF", ":/qtquickcontrols2.conf"); +} diff --git a/examples/demos/robotarm/src/import_qml_plugins.h b/examples/demos/robotarm/src/import_qml_plugins.h new file mode 100644 index 00000000..50ff7ca7 --- /dev/null +++ b/examples/demos/robotarm/src/import_qml_plugins.h @@ -0,0 +1,12 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +/* + * This file is automatically generated by Qt Design Studio. + * Do not change. +*/ + +#include <QtQml/qqmlextensionplugin.h> + +Q_IMPORT_QML_PLUGIN(contentPlugin) +Q_IMPORT_QML_PLUGIN(RobotArmPlugin) diff --git a/examples/demos/robotarm/src/main.cpp b/examples/demos/robotarm/src/main.cpp new file mode 100644 index 00000000..45726947 --- /dev/null +++ b/examples/demos/robotarm/src/main.cpp @@ -0,0 +1,43 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +/* + * This file is automatically generated by Qt Design Studio. + * Do not change. +*/ + +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +#include "app_environment.h" +#include "import_qml_plugins.h" + +int main(int argc, char *argv[]) +{ + set_qt_environment(); + + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + const QUrl url(u"qrc:Main/main.qml"_qs); + QObject::connect( + &engine, + &QQmlApplicationEngine::objectCreated, + &app, + [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, + Qt::QueuedConnection); + + engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml"); + engine.addImportPath(":/"); + + engine.load(url); + + if (engine.rootObjects().isEmpty()) { + return -1; + } + + return app.exec(); +} diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp index 99969ae6..177a383b 100644 --- a/tests/auto/quick/examples/tst_examples.cpp +++ b/tests/auto/quick/examples/tst_examples.cpp @@ -63,6 +63,7 @@ tst_examples::tst_examples() excludedFiles << "examples/quick/shapes/content/main.qml"; // relies on resources excludedFiles << "examples/quick/shapes/content/interactive.qml"; // relies on resources excludedFiles << "examples/demos/addressbook/qml/main.qml"; // relies on resources + excludedFiles << "examples/demos/robotarm/main.qml"; // relies on custom import #ifdef QT_NO_XMLPATTERNS excludedDirs << "demos/twitter"; |