Rotate a .stl file around a specific axis in Qt Qml - qt

I am trying to rotate my stl. file in the screen in x,y,z dimensions. My whole main.qml code is here:
ApplicationWindow
{
visible: true
width: 640
height: 480
title: qsTr("3D Viewer")
header: ToolBar
{
ToolButton
{
text: "Open 3D Model"
onPressed:
{
fileDialog.open()
}
}
}
FileDialog
{
id: fileDialog
onAccepted:
{
sceneLoader.source = fileDialog.fileUrl
}
}
Scene3D
{
anchors.fill: parent
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
Entity
{
id: sceneRoot
Camera
{
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 90
aspectRatio: 16/9
nearPlane : 0.1
farPlane : 100.0
position: Qt.vector3d( 0.0,-40.0, 10.0 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 10.0, 0.0, 0.0 )
}
OrbitCameraController
{
camera: camera
}
components: [
RenderSettings
{
activeFrameGraph: ForwardRenderer
{
clearColor: Qt.rgba(0.0,1, 1, 1)
camera: camera
}
},
InputSettings
{
}
]
Entity
{
id: satEntity
components: [
SceneLoader
{
id: sceneLoader
}
]
Transform {
id: satTransform
scale: 0.3
rotation: Qt.vector3d(0.0, 0.0, 0.0)
translation: Qt.vector3d(0.0, 0.0, 1)
rotationX: 0
rotationY: 0
rotationZ: 0
}
}
}
}
}
But its rotation doesn't change even I change the rotationX, rotationY, and rotationZ. Moreover there is no error it works but it always in the same position. It is basic but I don't know where I fail.

I have solved the problem and I share it for one who may see the same problem in the future.
ApplicationWindow
{
visible: true
width: 640
height: 480
title: qsTr("3D Viewer")
header: ToolBar
{
ToolButton
{
text: "Open 3D Model"
onPressed:
{
fileDialog.open()
}
}
}
FileDialog
{
id: fileDialog
onAccepted:
{
sceneLoader.source = fileDialog.fileUrl
}
}
Scene3D
{
anchors.fill: parent
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
Entity
{
id: sceneRoot
Camera
{
id: camera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 90
aspectRatio: 16/9
nearPlane : 0.1
farPlane : 100.0
position: Qt.vector3d( 0.0,-40.0, 10.0 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 10.0, 0.0, 0.0 )
}
OrbitCameraController
{
camera: camera
}
components: [
RenderSettings
{
activeFrameGraph: ForwardRenderer
{
clearColor: Qt.rgba(0.0,1, 1, 1)
camera: camera
}
},
InputSettings
{
}
]
Entity
{
id: satEntity
components: [
SceneLoader
{
id: sceneLoader
},
Transform {
id: satTransform
rotationX: 0 //you can type whatever you want.
rotationY: 0 //you can type whatever you want.
rotationZ: 0 //you can type whatever you want.
}
]
}
}
}
}

Related

Sequential movement of an object by coordinates

I need to move an object by the clock and counterclockwise sequentially. But the for loop works differently, it only moves in the latter direction. When you click on the button, the object must first turn clockwise, and then counterclockwise. Maybe there is some kind of delay when performing the animation? How can I do it?
import QtQuick 2.15
import QtQuick.Window 2.14
import QtQuick3D 1.15
import QtQuick.Controls 2.14
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
visibility: "Maximized"
property int scl: 5
property int angle: 360
Node{
id: standAloneScene
DirectionalLight {
ambientColor: Qt.rgba(1.0, 1.0, 1.0, 1.0)
}
Node {
id: sphere
Model {
id: model_sphere
source: "#Cube"
x: 200
y: 100
z: 0
materials: [
DefaultMaterial {
diffuseColor: Qt.rgba(0.053, 0.130, 0.219, 0.75)
}
]
}
}
ParallelAnimation{
id: start
running: false
NumberAnimation {
target: sphere
property: "eulerRotation.y"
to: angle
duration: 2000
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: model_sphere
property: "eulerRotation.y"
to: 2*angle
duration: 2000
easing.type: Easing.InOutQuad
}
}
OrthographicCamera {
id: cameraOrthographicFront
eulerRotation.y: 45
eulerRotation.x: -45
x: 600
y: 800
z: 600
}
}
Rectangle {
id: view
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
height: parent.height
color: "#848895"
border.color: "black"
View3D {
id: topLeftView
anchors.fill: parent
importScene: standAloneScene
camera: cameraOrthographicFront
}
Button {
id: posmoveZ
width: view.width/8
height: view.height/16
anchors.top: view.top
anchors.right: view.right
text: "start"
font.pixelSize: height
onClicked: {
for(var i=0; i<6; i++){
if(i % 2 == 0){
angle = 360
}
else{
angle = -360
}
start.restart();
}
}
}
}
}
The simplest answer to your problem is put your animations inside a SequentialAnimation, i.e.
SequentialAnimation {
loops: 3
ParallelAnimation {
id: clockwise
NumberAnimation { /* ... */ }
NumberAnimation { /* ... */ }
}
ParallelAnimation {
id: counterClockwise
NumberAnimation { /* ... */ }
NumberAnimation { /* ... */ }
}
}
Also, you should let QML use the default width and height of a Button. Here's the complete solution:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
import QtQuick3D 1.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
visibility: "Maximized"
property int scl: 5
property int angle: 360
Node{
id: standAloneScene
DirectionalLight {
ambientColor: Qt.rgba(1.0, 1.0, 1.0, 1.0)
}
Node {
id: sphere
Model {
id: model_sphere
source: "#Cube"
x: 200
y: 100
z: 0
materials: [
DefaultMaterial {
diffuseColor: Qt.rgba(0.053, 0.130, 0.219, 0.75)
}
]
}
}
SequentialAnimation {
id: sequentialAnimation
loops: 3
ParallelAnimation{
id: clockwise
running: false
property int count: 3
NumberAnimation {
target: sphere
property: "eulerRotation.y"
from: 360
to: 0
duration: 2000
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: model_sphere
property: "eulerRotation.y"
from: 720
to: 0
duration: 2000
easing.type: Easing.InOutQuad
}
}
ParallelAnimation{
id: counterClockwise
running: false
NumberAnimation {
target: sphere
property: "eulerRotation.y"
from: 0
to: 360
duration: 2000
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: model_sphere
property: "eulerRotation.y"
from: 0
to: 720
duration: 2000
easing.type: Easing.InOutQuad
}
}
}
OrthographicCamera {
id: cameraOrthographicFront
eulerRotation.y: 45
eulerRotation.x: -45
x: 600
y: 800
z: 600
}
}
Rectangle {
id: view
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
height: parent.height
color: "#848895"
border.color: "black"
View3D {
id: topLeftView
anchors.fill: parent
importScene: standAloneScene
camera: cameraOrthographicFront
}
Button {
id: posmoveZ
anchors.top: view.top
anchors.right: view.right
anchors.margins: 10
text: "start"
font.pixelSize: height
enabled: !clockwise.running && !counterClockwise.running
onClicked: {
sequentialAnimation.restart();
}
}
}
}

Multiple instances of Qt 3D Entity

I m trying to create multiple spheres in Qt 3D. following is the code I have developed.
Entity{
Repeater {
model: 10
SphereMesh {
id: sphereMesh1
radius: 1
}
Transform {
id: sphereTransform1
property real userAngle: 0.0
matrix: {
var m = Qt.matrix4x4();
m.rotate(userAngle, Qt.vector3d(0, 1, 0));
m.translate(Qt.vector3d(index, 0, 0));
return m;
}
}
PhongMaterial {
id: material1
}
Entity {
id: sphereEntity1
components: [ sphereMesh1, material1, sphereTransform1 ]
}
}
}
I have written a repeater above to create 10 spheres but the screen is coming blank. Any hints?
Use NodeInstantiator.
Entity {
id: root
Camera {
id: mainCamera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 45
aspectRatio: 16/9
nearPlane : 0.1
farPlane : 1000.0
position: Qt.vector3d( 0.0, 0.0, -40.0 )
upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
}
OrbitCameraController {
id: mainCameraController
camera: mainCamera
}
components: [
RenderSettings {
Viewport {
normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
RenderSurfaceSelector {
CameraSelector {
id: cameraSelector
camera: mainCamera
FrustumCulling {
ClearBuffers {
buffers: ClearBuffers.AllBuffers
clearColor: "#333333"
NoDraw {}
}
LayerFilter {
filterMode: LayerFilter.DiscardAnyMatchingLayers
layers: [topLayer]
}
LayerFilter {
filterMode: LayerFilter.AcceptAnyMatchingLayers
layers: [topLayer]
ClearBuffers {
buffers: ClearBuffers.DepthBuffer
}
}
}
}
}
}
},
InputSettings {}
]
Layer {
id: topLayer
recursive: true
}
ListModel {
id: entityModel
ListElement { x: 0; y: 0; z: 0 }
}
NodeInstantiator {
id: instance
model: entityModel
delegate:Entity{
SphereMesh {
id: sphereMesh1
radius:1
}
Transform {
id: sphereTransform1
translation: Qt.vector3d(x,y,z)
}
PhongMaterial {
id: material1
ambient: "red"
}
Entity {
id: sphereEntity1
components: [ sphereMesh1, material1, sphereTransform1 ]
}
}
Component.onCompleted:
{
for(var i=0;i<10;i++)
{
entityModel.append({"x" : i*3, "y" : 0.0, "z" : Math.random()} )
}
}
}
}

Why is animation triggered on load

When the window opens you can see the Rectangle sliding out. I set the y property to the parent height so it should be initially outside of the window why is this being animated?
My guess it's because of the parent:height. Maybe because parent.height is not available at loading time and it's initially set to 0?
I have following example to reproduce:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: test;
y: parent.height;
states: [
State {
name: "slideOut"
PropertyChanges{
target: test;
y: parent.height;
}
},
State {
name: "slideIn"
PropertyChanges{
target: test;
y: 0;
}
}
]
Behavior on y {
NumberAnimation {
duration: 500;
}
}
color: "red";
width: parent.width;
height: parent.height;
}
MouseArea {
anchors.fill: parent;
onClicked: {
if(test.state == "slideIn") {
test.state = "slideOut";
} else {
test.state = "slideIn";
}
}
}
}
Your guess sounds spot on to me.
You should use transitions with states instead:
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: test
y: parent.height
width: parent.width
height: parent.height
color: "red"
states: [
State {
name: "slideOut"
PropertyChanges {
target: test
y: parent.height
}
},
State {
name: "slideIn"
PropertyChanges {
target: test
y: 0
}
}
]
transitions: [
Transition {
NumberAnimation {
property: "y"
duration: 500
}
}
]
}
MouseArea {
anchors.fill: parent
onClicked: {
if (test.state == "slideIn") {
test.state = "slideOut"
} else {
test.state = "slideIn"
}
}
}
}
Another solution could be to use the enabled property of Behavior to only run the animation when the window is ready. I'm not sure which property you'd base it on though. Some ideas:
enabled: window.height > 0
enabled: window.active

qml mapToItem for quickmap item

I have a qml map, and a qml quick map item, wen I click on a point on map it places quick map item, well my quick map item is an image which is 300px in 300px, I want to get the coordinate of point(200,200) of image on map, I know that there is a function which is named item.mapToItem(item,x,y), Well I tried to use this function but it returns wrong values,Here is my whole code
Map {
id:map
anchors.fill: parent
plugin: mapPlugin
center: QtPositioning.coordinate(59.91, 10.75) // Oslo
zoomLevel: 7
MapQuickItem{
id:imagQuickitem
zoomLevel:1
sourceItem:Image {
id:image
asynchronous:true
smooth: false
transformOrigin: Item.Center
antialiasing: false
//visible: false
MouseArea{
anchors.fill: parent ;
onPressed:
{
if(!gcplistener && currentTool=="addgcp"){
gcpimgposition=Qt.point(mouse.x,mouse.y);
console.log("The first point is set")
currentgcppoint.row=gcpimgposition.x;
currentgcppoint.column=gcpimgposition.y;
gcplistener=true;
}
console.log(mouse.x + " "+ mouse.y)
console.log( imagQuickitem.mapToItem(map,mouse.x,mouse.y).x+ " "+imagQuickitem.mapToItem(map,mouse.x,mouse.y).y)
console.log( imagQuickitem.mapFromItem(map,mouse.x,mouse.y).x+ " "+imagQuickitem.mapFromItem(map,mouse.x,mouse.y).y)
}
}
}
coordinate: QtPositioning.coordinate(59.91, 10.75)
}
Row {
id: containerRow
layoutDirection: rightEdge() ? Qt.LeftToRight : Qt.RightToLeft
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: rightEdge() ? parent.right : undefined
anchors.left: rightEdge() ? undefined : parent.left
Button {
id: sliderToggler
width: 32
height: 96
checkable: true
checked: true
anchors.verticalCenter: parent.verticalCenter
transform: Scale {
origin.x: rightEdge() ? 0 : sliderToggler.width / 2
xScale: rightEdge() ? 1 : -1
}
style: ButtonStyle {
background: Rectangle {
color: "transparent"
}
}
property real shear: 0.333
property real buttonOpacity: 0.5
property real mirror : rightEdge() ? 1.0 : -1.0
Rectangle {
width: 16
height: 48
color: "seagreen"
antialiasing: true
opacity: sliderToggler.buttonOpacity
anchors.top: parent.top
anchors.left: sliderToggler.checked ? parent.left : parent.horizontalCenter
transform: Matrix4x4 {
property real d : sliderToggler.checked ? 1.0 : -1.0
matrix: Qt.matrix4x4(1.0, d * sliderToggler.shear, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0)
}
}
Rectangle {
width: 16
height: 48
color: "seagreen"
antialiasing: true
opacity: sliderToggler.buttonOpacity
anchors.top: parent.verticalCenter
anchors.right: sliderToggler.checked ? parent.right : parent.horizontalCenter
transform: Matrix4x4 {
property real d : sliderToggler.checked ? -1.0 : 1.0
matrix: Qt.matrix4x4(1.0, d * sliderToggler.shear, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0)
}
}
}
Rectangle {
id: sliderContainer
height: parent.height
width: sliderRow.width + 10
visible: sliderToggler.checked
color: Qt.rgba( 0, 191 / 255.0, 255 / 255.0, 0.07)
property var labelBorderColor: "transparent"
property var slidersHeight : sliderContainer.height
- rowSliderValues.height
- rowSliderLabels.height
- sliderColumn.spacing * 2
- sliderColumn.topPadding
- sliderColumn.bottomPadding
MouseArea{
onPressed: {
console.log("test")
}
}
Column {
id: sliderColumn
spacing: 10
topPadding: 16
bottomPadding: 48
anchors.centerIn: parent
// the sliders value labels
Row {
id: rowSliderValues
spacing: sliderRow.spacing
width: sliderRow.width
height: 32
property real entryWidth: zoomSlider.width
Rectangle{
color: labelBackground
height: parent.height
width: parent.entryWidth
border.color: sliderContainer.labelBorderColor
Label {
id: labelZoomValue
text: zoomSlider.value.toFixed(3)
font.pixelSize: fontSize
rotation: -90
anchors.centerIn: parent
}
}
Rectangle{
color: labelBackground
height: parent.height
width: parent.entryWidth
border.color: sliderContainer.labelBorderColor
Label {
id: labelBearingValue
text: opacityslider.value.toFixed(2)
font.pixelSize: fontSize
rotation: -90
anchors.centerIn: parent
}
}
Rectangle{
color: labelBackground
height: parent.height
width: parent.entryWidth
border.color: sliderContainer.labelBorderColor
Label {
id: labelTiltValue
text: imagescale.value.toFixed(2)
font.pixelSize: fontSize
rotation: -90
anchors.centerIn: parent
}
}
Rectangle{
color: labelBackground
height: parent.height
width: parent.entryWidth
border.color: sliderContainer.labelBorderColor
Label {
id: labelFovValue
text: rotationslider.value.toFixed(2)
font.pixelSize: fontSize
rotation: -90
anchors.centerIn: parent
}
}
} // rowSliderValues
// The sliders row
Row {
spacing: -10
id: sliderRow
height: sliderContainer.slidersHeight
C2.Slider {
id: zoomSlider
height: parent.height
orientation : Qt.Vertical
from : map.minimumZoomLevel
to : map.maximumZoomLevel
value : map.zoomLevel
onValueChanged: {
map.zoomLevel = value
}
}
C2.Slider {
id: opacityslider
height: parent.height
from: 0
to: 1
orientation : Qt.Vertical
value: image.opacity
onValueChanged: {
image.opacity = value;
}
}
C2.Slider {
id: imagescale
height: parent.height
orientation : Qt.Vertical
from: 0;
to: 5
value: image.scale
onValueChanged: {
image.scale= value;
rectangular.scale=value;
}
}
C2.Slider {
id: rotationslider
height: parent.height
orientation : Qt.Vertical
from: 0
to: 360
value: image.rotation
onValueChanged: {
image.rotation = value;
rectangular.rotation=value
}
}
} // Row sliders
// The labels row
Row {
id: rowSliderLabels
spacing: sliderRow.spacing
width: sliderRow.width
property real entryWidth: zoomSlider.width
property real entryHeight: 64
Rectangle{
color: labelBackground
height: parent.entryHeight
width: parent.entryWidth
border.color: sliderContainer.labelBorderColor
Label {
id: labelZoom
text: "Map Zoom"
font.pixelSize: fontSize
rotation: -90
anchors.centerIn: parent
}
}
Rectangle{
color: labelBackground
height: parent.entryHeight
width: parent.entryWidth
border.color: sliderContainer.labelBorderColor
Label {
id: labelBearing
text: "Image Opacity"
font.pixelSize: fontSize
rotation: -90
anchors.centerIn: parent
}
}
Rectangle{
color: labelBackground
height: parent.entryHeight
width: parent.entryWidth
border.color: sliderContainer.labelBorderColor
Label {
id: labelTilt
text: "Image Scale"
font.pixelSize: fontSize
rotation: -90
anchors.centerIn: parent
}
}
Rectangle{
color: labelBackground
height: parent.entryHeight
width: parent.entryWidth
border.color: sliderContainer.labelBorderColor
Label {
id: labelFov
text: "Image Rotation"
font.pixelSize: fontSize
rotation: -90
anchors.centerIn: parent
}
}
} // rowSliderLabels
} // Column
} // sliderContainer
}
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onClicked: mouse.accepted = false;
onReleased: mouse.accepted = false;
onDoubleClicked: mouse.accepted = false;
onPositionChanged: mouse.accepted = false;
onPressAndHold: mouse.accepted = false;
onPressed: {
mouse.accepted = false;
if(imagesourcepoint==null){
imagesourcepoint=Qt.point(mouse.x,mouse.y)
imagQuickitem.coordinate= map.toCoordinate(imagesourcepoint)
rectangular.topLeft=map.toCoordinate(imagesourcepoint);
var w=((image.width*0.0016)+map.toCoordinate(imagesourcepoint).longitude);
var h=(map.toCoordinate(imagesourcepoint).latitude)+(image.height*1.5);
console.log(w)
console.log(h)
if(!image.visible){
image.visible=true
}
}
switch(currentTool){
case "addgcp":
gcppoint=map.toCoordinate(Qt.point(mouse.x,mouse.y));
if(gcplistener){
row.text=gcpimgposition.x;
col.text=gcpimgposition.y;
x.text=gcppoint.longitude;
y.text=gcppoint.latitude;
inputgcpDialog.open();
}else{
currentgcppoint.lat=gcppoint.latitude;
currentgcppoint.lon=gcppoint.longitude;
}
if(gcpimgposition==null&&gcplistener==false){
//user must click on image
//FIXME:
messages.displayMessage("Please click on image, not outside");
}
gcpimgposition==null;
break;
case "moveimage":
//moving image
if(moveimageEnabled){
imagQuickitem.coordinate= map.toCoordinate(Qt.point(mouse.x,mouse.y))
}
break;
}
console.log(map.width + " "+ map.height)
console.log("z: "+map.zoomLevel )
console.log("image.mapToItem: "+ image.mapToItem(map,0,0).x+ " "+image.mapToItem(map,0,0).y)
console.log("image coordinate on map: "+ image.mapToItem(map,image.width,image.height).x+ " "+image.mapToItem(map,image.width,image.height).y)
// console.log( map.mapFromItem(null,0,0).x+ " "+map.mapFromItem(null,0,0).y)
// console.log( map.mapToItem(imagQuickitem,0,0).x+ " "+map.mapToItem(imagQuickitem,0,0).y)
// console.log( map.mapFromItem(imagQuickitem,0,0).x+ " "+map.mapFromItem(imagQuickitem,0,0).y)
onPressed: console.log('latitude = '+ (map.toCoordinate(Qt.point(mouse.x,mouse.y)).latitude),
'longitude = '+ (map.toCoordinate(Qt.point(mouse.x,mouse.y)).longitude));
}
}
}
well, my problem is in part Maps Mouse Area which I expect to get the coordinate of clicked point on Map object based o quickmapitems coordinate using mapToItem function.
My main function is in Mouse pressed button,In above code I expect image.mapToItem(map,0,0).y) to return the same as console.log(mouse.x + " "+ mouse.y) as the mouse.x and mouse.y are the position of (0,0) of image which are mapped on Map items coordinate, I know how to converts maps coordinate to geographic coordinate
I also kept some codes of sliders in map control as I think there must be a relation between these parameters and main map controls mapToItem function
You must call mapToItem() to replace image of imagQuickitem.
Use below code in MouseArea
var coor = image.mapToItem(map , 0 ,0)
Thanks

Creating QML States dynamically

I want to make an icon component that changes it picture and color depending on it state:
StateIcon.qml:
import QtQuick 2.0
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
Item {
Layout.preferredWidth: appLayout.icon.prefWidth
Layout.preferredHeight: appLayout.icon.prefHeight
property variant stateImage: stateImageInstance
Image {
id: stateImageInstance
width: appLayout.icon.prefWidth
height: appLayout.icon.prefWidth
sourceSize.width: width
sourceSize.height: height
}
property variant imageOverlay: imageOverlayInstance
ColorOverlay {
id: imageOverlayInstance
anchors.fill: stateImage
source: stateImage
}
transitions: Transition {
SequentialAnimation {
NumberAnimation {
target: stateImage; property: "scale"
to: 0; duration: 100
}
PropertyAction {
target: stateImage; property: "source"
}
PropertyAction {
target: imageOverlay; property: "color"
}
NumberAnimation {
target: stateImage; property: "scale"
to: 1; duration: 100
}
}
}
}
The problem is that I have to define states in the component instance:
main.qml:
StateIcon {
id: stateIcon
states: [
State {
name: "state1";
PropertyChanges {
target: stateIcon.stateImage
source: "qrc:/resources/icons/icon1.svg"
}
PropertyChanges {
target: stateIcon.imageOverlay; color: "gray"
}
},
State {
name: "state2";
PropertyChanges {
target: stateIcon.stateImage
source: "qrc:/resources/icons/icon2.svg"
}
PropertyChanges {
target: stateIcon.imageOverlay; color: "green"
}
}
...
]
state: "state1"
}
And now I want to know is it possible to define only state names, color and source in some array:
main.qml:
StateIcon {
id: stateIcon
rawStates: [
{
name: "state1",
iconSource: "qrc:/resources/icons/state1.svg",
color: "green"
},
{
name: "state2",
iconSource: "qrc:/resources/icons/state2.svg",
color: "green"
},
...
]
state: "state1"
}
And in the StateIcon.qml define states property dynamically using rawStates property?
Maybe something like that:
StateIcon.qml:
import QtQuick 2.0
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
Item {
property variant rawStates
Layout.preferredWidth: appLayout.icon.prefWidth
Layout.preferredHeight: appLayout.icon.prefHeight
Image {
id: stateImage
width: appLayout.icon.prefWidth
height: appLayout.icon.prefWidth
sourceSize.width: width
sourceSize.height: height
}
ColorOverlay {
id: imageOverlay
anchors.fill: stateImage
source: stateImage
}
states: [
for(var i=0; i<rawStates.length; ++i) {
?
}
]
transitions: Transition {
SequentialAnimation {
NumberAnimation {
target: stateImage; property: "scale"
to: 0; duration: 100
}
PropertyAction {
target: stateImage; property: "source"
}
PropertyAction {
target: imageOverlay; property: "color"
}
NumberAnimation {
target: stateImage; property: "scale"
to: 1; duration: 100
}
}
}
}
Instead of using States I would use a plain javascript associative arrays.
You can't use transitions but you could use Behavior instead. Not anything can be done with behavior but it's enough most of the time.
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQml 2.2
ApplicationWindow {
id: mainWindow
visible: true
minimumWidth: 500
minimumHeight: 500
Row {
Rectangle {
id: rect
width: 100
height: 100
property var stateDescriptors: {
'state0': {color: 'green'},
'state1': {color: 'red'},
'state2': {color: 'blue'},
'state3': {color: 'purple'},
'state4': {color: 'orange'}
}
property string iconState: "state0"
Text {
anchors.fill: parent
text: parent.iconState
}
color: stateDescriptors[iconState].color
Behavior on iconState {
SequentialAnimation {
NumberAnimation {
target: rect; property: "scale"
to: 0; duration: 100
}
PropertyAction { } //actually change the iconState here, since the color is binded to it, it will also change between the 2 scale animations
NumberAnimation {
target: rect; property: "scale"
to: 1; duration: 100
}
}
}
}
Button {
text: 'change state'
property int count: 0
onClicked: {
count = (count + 1) % Object.keys(rect.stateDescriptors).length
rect.iconState = 'state' + count
}
}
}
}
Maybe this helps you:
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQml 2.2
ApplicationWindow {
id: mainWindow
visible: true
minimumWidth: 500
minimumHeight: 500
Row {
Rectangle {
id: rect
width: 100
height: 100
Text {
anchors.fill: parent
text: parent.state
}
property var myStates: []
states: myStates
onStateChanged: console.log(Object.keys(rect.states))
}
Button {
text: 'add state'
onClicked: {
rect.myStates.push(statePrototype.createObject(rect,
{
name: 'state' + count,
color: Qt.rgba(Math.random(count),
Math.random(count),
Math.random(count),
Math.random(count))
}))
rect.myStatesChanged()
count++
}
}
Button {
text: 'change state'
onClicked: {
rect.state = 'state' + (count1 % count)
count1++
}
}
}
property int count: 0
property int count1: 0
Component {
id: statePrototype
State {
id: st
property color color
PropertyChanges {
target: rect
color: st.color
}
}
}
}
It seems to be not so easily possible to add States to states directly. With the extra mile going over a custom property var myStates it suddenly works. Don't forget to tell everyone, that myStatesChanged() after adding something!
EDIT Once more, with the list of JS Objects, and a Instantiator. The method is the same
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQml 2.2
ApplicationWindow {
id: mainWindow
visible: true
minimumWidth: 500
minimumHeight: 500
Row {
Rectangle {
id: rect
width: 100
height: 100
Text {
anchors.fill: parent
text: parent.state
}
property var myStates: []
states: myStates
onStateChanged: console.log(Object.keys(rect.states))
}
Button {
text: 'change state'
property int count: 0
onClicked: {
rect.state = 'state' + count % rect.myStates.length
count ++
}
}
Button {
text: 'add states'
onClicked: {
stateDescriptors.push( { name: 'state' + stateDescriptors.length, color: Qt.rgba(Math.random(1),
Math.random(2),
Math.random(3),
Math.random(4)) })
stateDescriptorsChanged()
}
}
}
Instantiator {
model: stateDescriptors
delegate: State {
name: modelData.name
PropertyChanges {
target: rect
color: modelData.color
}
Component.onCompleted: {
console.log('created', modelData.name)
rect.myStates.push(this)
rect.myStatesChanged()
}
Component.onDestruction: {
console.log('destroy', modelData.name)
rect.myStates.pop()
}
}
}
property var stateDescriptors: [
{
name: 'state0',
color: 'green'
},
{
name: 'state1',
color: 'red'
},
{
name: 'state2',
color: 'blue'
},
{
name: 'state3',
color: 'purple'
},
{
name: 'state4',
color: 'orange'
}
]
}

Resources