Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add common widget for pose #424

Merged
merged 11 commits into from
Jul 1, 2022
291 changes: 291 additions & 0 deletions include/ignition/gui/qml/GzPose.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
/*
* Copyright (C) 2022 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.3
import QtQuick.Controls.Styles 1.4

/**
* Item displaying 3D pose information.
*
* Users should load values to xValues, yValues, etc.
AzulRadio marked this conversation as resolved.
Show resolved Hide resolved
* If readOnly == False,
* users can read from signal pararmeters of gzPoseSet: _x, _y, etc.
AzulRadio marked this conversation as resolved.
Show resolved Hide resolved
*
* Usage example:
* GzPose {
* id: gzPose
* readOnly: false
* xValue: xValueFromCPP
* yValue: yValueFromCPP
* zValue: zValueFromCPP
* rollValue: rollValueFromCPP
* pitchValue: pitchValueFromCPP
* yawValue: yawValueFromCPP
* onGzPoseSet: {
* myFunc(_x, _y, _z, _roll, _pitch, _yaw)
* }
* }
**/

Item {
id: gzPoseRoot

// Read-only / write
property bool readOnly: false

// User input value.
property double xValue
property double yValue
property double zValue
property double rollValue
property double pitchValue
property double yawValue

/**
* Used to read spinbox values
* @params: _x, _y, _z, _roll, _pitch, _yaw: corresponding spinBoxes values
* @note: When readOnly == false, user should read spinbox value from its
* parameters.
* When readOnly == true, this signal is unused.
*/
signal gzPoseSet(double _x, double _y, double _z, double _roll, double _pitch, double _yaw)


/*** The following are private variables: ***/
// Show Pose bar (used to control expand)
property bool show: true
AzulRadio marked this conversation as resolved.
Show resolved Hide resolved

height: gzPoseContent.height

// Left indentation
property int indentation: 10

// Horizontal margins
property int margin: 5
AzulRadio marked this conversation as resolved.
Show resolved Hide resolved

// Maximum spinbox value
property double spinMax: 1000000
AzulRadio marked this conversation as resolved.
Show resolved Hide resolved

// local variables to store spinbox values
property var xItem: {}
property var yItem: {}
property var zItem: {}
property var rollItem: {}
property var pitchItem: {}
property var yawItem: {}

// Dummy component to use its functions.
IgnHelpers {
id: gzHelper
}
Comment on lines +93 to +96
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to set up IgnHelpers.qml in a way that we don't have to instantiate a dummy component and just be able to call the functions (e.g., IgnHelpers.getDecimals(...))?

This is out of scope for this PR but if it is possible, after we merge this, this could potentially be a next task for you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well. This is doable but we need to make sure every qml using this common widget must also include IgnHelpers.qml as well. Do we want to do this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that this would be a nice change. This PR is just following the weird pattern that I started with IgnHelpers a while back, so we don't need to block on it.

we need to make sure every qml using this common widget must also include IgnHelpers.qml as well.

I think there may be ways to work around this, either with a singleton or a javascript file.

/*** Private variables end: ***/

/**
* Used to create a spin box
*/
Component {
id: writableNumber
IgnSpinBox {
id: writableSpin
value: numberValue
minimumValue: -spinMax
maximumValue: spinMax
decimals: gzHelper.getDecimals(writableSpin.width)
onEditingFinished: {
gzPoseRoot.gzPoseSet(xItem.value, yItem.value, zItem.value, rollItem.value, pitchItem.value, yawItem.value)
}
}
}

/**
* Used to create a read-only number
*/
Component {
id: readOnlyNumber
Text {
id: numberText
anchors.fill: parent
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
text: {
var decimals = gzHelper.getDecimals(numberText.width)
return numberValue.toFixed(decimals)
}
}
}

Rectangle {
id: gzPoseContent
width: parent.width
height: show ? gzPoseGrid.height : 0
clip: true
color: "transparent"

Behavior on height {
NumberAnimation {
duration: 200;
easing.type: Easing.InOutQuad
}
}

GridLayout {
id: gzPoseGrid
width: parent.width
columns: 6

// Left spacer
AzulRadio marked this conversation as resolved.
Show resolved Hide resolved
Item {
Layout.rowSpan: 3
width: margin + indentation
}

Text {
text: 'X (m)'
leftPadding: 5
color: Material.theme == Material.Light ? "#444444" : "#bbbbbb"
font.pointSize: 12
}

Item {
Layout.fillWidth: true
height: 40
Loader {
id: xLoader
anchors.fill: parent
property double numberValue: xValue
sourceComponent: readOnly ? readOnlyNumber : writableNumber
onLoaded: {
xItem = xLoader.item
}
}
}

Text {
text: 'Roll (rad)'
leftPadding: 5
color: Material.theme == Material.Light ? "#444444" : "#bbbbbb"
font.pointSize: 12
}

Item {
Layout.fillWidth: true
height: 40
Loader {
id: rollLoader
anchors.fill: parent
property double numberValue: rollValue
sourceComponent: readOnly ? readOnlyNumber : writableNumber
onLoaded: {
rollItem = rollLoader.item
}
}
}

// Right spacer
Item {
Layout.rowSpan: 3
width: margin
}

Text {
text: 'Y (m)'
leftPadding: 5
color: Material.theme == Material.Light ? "#444444" : "#bbbbbb"
font.pointSize: 12
}

Item {
Layout.fillWidth: true
height: 40
Loader {
id: yLoader
anchors.fill: parent
property double numberValue: yValue
sourceComponent: readOnly ? readOnlyNumber : writableNumber
onLoaded: {
yItem = yLoader.item
}
}
}

Text {
text: 'Pitch (rad)'
leftPadding: 5
color: Material.theme == Material.Light ? "#444444" : "#bbbbbb"
font.pointSize: 12
}

Item {
Layout.fillWidth: true
height: 40
Loader {
id: pitchLoader
anchors.fill: parent
property double numberValue: pitchValue
sourceComponent: readOnly ? readOnlyNumber : writableNumber
onLoaded: {
pitchItem = pitchLoader.item
}
}
}

Text {
text: 'Z (m)'
leftPadding: 5
color: Material.theme == Material.Light ? "#444444" : "#bbbbbb"
font.pointSize: 12
}

Item {
Layout.fillWidth: true
height: 40
Loader {
id: zLoader
anchors.fill: parent
property double numberValue: zValue
sourceComponent: readOnly ? readOnlyNumber : writableNumber
onLoaded: {
zItem = zLoader.item
}
}
}

Text {
text: 'Yaw (rad)'
leftPadding: 5
color: Material.theme == Material.Light ? "#444444" : "#bbbbbb"
font.pointSize: 12
}

Item {
Layout.fillWidth: true
height: 40
Loader {
id: yawLoader
anchors.fill: parent
property double numberValue: yawValue
sourceComponent: readOnly ? readOnlyNumber : writableNumber
onLoaded: {
yawItem = yawLoader.item
}
}
}
} // end of GridLayout
} // end of Rectangle (gzPoseContent)
} // end of Rectangle (gzPoseRoot)
17 changes: 17 additions & 0 deletions include/ignition/gui/qml/IgnHelpers.qml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,21 @@ Item {

return result;
}

/**
* Helper function to get number of decimal digits based on a width value.
* @param _width Pixel width.
* @returns Number of decimals that fit with the provided width.
*/
function getDecimals(_width) {
// Use full decimals if the width is <= 0, which allows the value
// to appear correctly.
if (_width <= 0 || _width > 110)
return 6

if (_width <= 80)
return 2

return 4
}
}
2 changes: 2 additions & 0 deletions include/ignition/gui/resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<file>qtquickcontrols2.conf</file>

<file>qml/GzColor.qml</file>
<file>qml/GzPose.qml</file>
<file>qml/IgnCard.qml</file>
<file>qml/IgnCardSettings.qml</file>
<file>qml/IgnHelpers.qml</file>
Expand All @@ -27,6 +28,7 @@
<file alias="qmldir">qml/qmldir</file>
<!-- Add any QML components referenced in the qmldir file here -->
<file alias="GzColor.qml">qml/GzColor.qml</file>
<file alias="GzPose.qml">qml/GzPose.qml</file>
<file alias="IgnSnackBar.qml">qml/IgnSnackBar.qml</file>
<file alias="IgnSpinBox.qml">qml/IgnSpinBox.qml</file>
</qresource>
Expand Down