QML Render the same Entity in two Scene3Ds - qml

How can I insert the same entity to be displayed into more than one Scene3Ds in QML?
(it'll probably be abundantly clear that I'm pretty new to QML)
I would like to create something similar to the Multi Viewport QML Example, except I would like to have the viewports in differet layouts (e.g. a SplitView).
Ultimately I would like my entities (Mesh or SceneLoader entities) to be created completely outside of my views (my Scene3D), and be able to show the same data in multiple views without having copies.
I haven't had much luck putting a Layout into a Scene3D, and even so, that would constrain me against having the same data show up elsewhere. I simply can't figure out how to define my data outside the views, especially since I can't figure out how to append to the components/data/children properties.
For example, in the (long) example below, I figured out how to define torusMesh2 outside of the Scene3D by setting it's .parent to inject it into scene3DRightEntity, but I cannot figure out how to inject it into both Scene3Ds
import QtQuick 2.0
import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0
import QtQuick.Layouts 1.3
import QtQuick.Controls 1.3
import QtQuick.Scene3D 2.0
import Qt3D.Extras 2.0
Item {
Button {
text: 'Button'
onClicked: {
console.log('Button clicked');
}
}
SphereMesh {
id: torusMesh2; radius: 5
parent: scene3DRightEntity
}
PhongMaterial { id: material2; parent: scene3DRightEntity }
Transform { id: torusTransform2; scale3D: Qt.vector3d(1.5, 1, 0.5); rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45); parent: scene3DRightEntity }
Rectangle {
id: topRect
anchors.fill: parent; anchors.margins: 50
color: 'green'
SplitView {
anchors.fill: parent; orientation: Qt.Horizontal
Rectangle {
id: scene
anchors.margins: 50; width: 200; Layout.minimumWidth: 100; Layout.maximumWidth: 500
color: "darkRed"
Text { text: "View 1"; anchors.centerIn: parent }
Scene3D {
id: scene3dLeft
anchors.fill: parent; anchors.margins: 10; focus: true
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
Entity {
SimpleCamera {
id: camera1; fieldOfView: 45; position: Qt.vector3d( 0.0, 0.0, 40.0 )
}
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
camera: camera1.camera
clearColor: "transparent"
}
}
, InputSettings { }
]
TorusMesh {
id: torusMesh1; radius: 5; minorRadius: 1; rings: 100; slices: 20
}
PhongMaterial { id: material1 }
Transform { id: torusTransform1; scale3D: Qt.vector3d(1.5, 1, 0.5); rotation: fromAxisAndAngle(Qt.vector3d(1, 0, 0), 45) }
Entity {
id: torusEntity1
components: [ torusMesh1, material1, torusTransform1 ]
}
}
}
}
Rectangle {
id: scene2
Layout.fillWidth: true; Layout.minimumWidth: 50; Layout.maximumWidth: 400; height: 300
color: "darkBlue"
Scene3D {
id: scene3dRight
anchors.fill: parent; anchors.margins: 50; focus: true;
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
Entity {
id: scene3DRightEntity
SimpleCamera {
id: camera2
position: Qt.vector3d( 0.0, 0.0, 40.0 )
}
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
camera: camera2.camera
clearColor: "transparent"
}
}
, InputSettings { }
]
Entity {
id: torusEntity2
components: [ torusMesh2, material2, torusTransform2 ]
}
}
}
}
}
}
} // Item
UPDATE
My first post was creating torusMesh2 as a child of the SplitView, this edit moves it to the top of the tree and sets its .parent.

Related

How can I make a custom delegate for my ListView?

I'm trying to make a custom ListView (AppDataListView) that can be used for data manipulation (essentially having a bunch of inputs). The problem I'm having is finding a way to tell AppDataListView what input types to use in the delegate of the ListView.
I currently try and do this by creating a loader in the ListView delegate, setting its source to a property of type Component, and then when I create an instance of AppDataListView, I specify the Component... However, I don't have any access to the model data, so it's kind of pointless.
Does anyone know how I can accomplish this?
Main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick .Controls 2.12
import QtQuick.Layouts 1.12
Window {
width: 640
height: 480
visible: true
ListModel {
id: sampleData
ListElement {
itemId: 1
name: "Name1"
}
ListElement {
itemId: 2
name: "Name2"
}
ListElement {
itemId: 3
name: "Name3"
}
ListElement {
itemId: 4
name: "Name4"
}
}
AppDataListView {
anchors.fill: parent
headers: ["ID", "Name"]
model: sampleData
delegate: Component {
RowLayout {
anchors.fill: parent
TextArea {
// I can't access itemId from here even though this is loaded into the delegate.
text: itemId
}
TextArea {
// I can't access name from here even though this is loaded into the delegate.
text: name
}
}
}
}
}
AppDataListView.qml
import QtQuick 2.0
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
Rectangle {
property var headers: []
property alias model: listView.model
property alias listView: listView
required property Component delegate;
id: root
color: "#bdbdbd"
ColumnLayout {
anchors.fill: parent
anchors.margins: 1
spacing:0
RowLayout {
spacing: 1
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Item {
Layout.minimumWidth: 30
Layout.maximumWidth: 30
}
Repeater {
id: headerRepeater
model: headers
delegate: Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
gradient: Gradient {
GradientStop { position: 0.0; color: "#FFFFFF" }
GradientStop { position: 0.5; color: "#F1F1F1" }
GradientStop { position: 1.0; color: "#FFFFFF" }
}
Label {
text: modelData
padding: 5
anchors.centerIn: parent
}
}
}
}
ListView {
id: listView
Layout.fillWidth: true
Layout.fillHeight: true
interactive: true
clip: true
boundsBehavior: Flickable.StopAtBounds
spacing: 1
ScrollBar.vertical: ScrollBar {
active: true
policy: ScrollBar.AlwaysOn
}
delegate: RowLayout {
width: parent.width
spacing: 1
Button {
id: rowBtn
Layout.minimumWidth: 30
Layout.maximumWidth: 30
background: Rectangle {
gradient: Gradient {
GradientStop { position: 0.0; color: (rowBtn.down ? "#56aff5" : rowBtn.hovered ? "#d9ebf9" : "#FFFFFF") }
GradientStop { position: 0.5; color: (rowBtn.down ? "#1b93f1" : rowBtn.hovered ? "#a4b2bd" : "#F1F1F1") }
GradientStop { position: 1.0; color: (rowBtn.down ? "#56aff5" : rowBtn.hovered ? "#d9ebf9" : "#FFFFFF") }
}
}
}
Loader {
sourceComponent: root.delegate
}
}
}
}
}
Capturing #Amfasis excellent comments to the question and adding some more detail....
Change your Loader reference to this:
Loader {
sourceComponent: root.delegate
property int itemId: model.itemId
property string name: model.name
}
fixes it. Here's why....
A Component declaration is put into the QML namespace hierarchy where it is declared not where it is instantiated at runtime. In other words, by declaring the delegate within main.qml, it can only see that namespace regardless of where it is instantiated at (in this case in AppDataListView).
More info here:
https://doc.qt.io/qt-5/qml-qtqml-component.html#creation-context
As #Amfasis pointed out, the workaround in this case is to declare properties on the Loader to pass in the model references you need. In this case the Loader acts as a bridge of sorts from the ListView namespace over to the delegate Component's namespace in main.qml.
More info on that here:
https://doc.qt.io/qt-5/qml-qtquick-loader.html#using-a-loader-within-a-view-delegate

How to show/hide KDE plasmoid's tooltip programatically?

Is there a way to make a plasmoid tooltip to show/hide programatically?
I tried by setting a ToolTipArea over the compact representation, and trying to trigger it with a Timer - it does not work (the regular tooltip keeps showing but only when hovering the plasmoid icon (aka compactRepresentation):
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
Item {
Layout.preferredWidth: 200
Layout.preferredHeight: 300
Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation
Plasmoid.compactRepresentation: Item
{
anchors.fill: parent
MouseArea
{
onClicked:
{
plasmoid.expanded = !plasmoid.expanded
}
}
PlasmaCore.ToolTipArea
{
id: toolTip
width: parent.width
height: parent.height
anchors.fill: parent
mainItem: tooltipContentItem
active: false
interactive: true
}
Timer
{
interval: 3000
running: true
repeat: true
onTriggered:
{
if (tooltipContentItem.active == false)
{
toolTip.showToolTip()
toolTip.active == true
}
else
{
toolTip.hideToolTip()
toolTip.active == false
}
}
}
}
Item
{
id: tooltipContentItem
implicitWidth: 300
implicitHeight: 200
ColumnLayout
{
id: mainLayout
anchors
{
left: parent.left
top: parent.top
margins: PlasmaCore.Units.gridUnit / 2
}
PlasmaExtras.Heading
{
id: tooltipMaintext
level: 3
Layout.minimumWidth: Math.min(implicitWidth, preferredTextWidth)
Layout.maximumWidth: preferredTextWidth
elide: Text.ElideRight
text: "Test"
}
PlasmaComponents.Label
{
id: tooltipSubtext
Layout.minimumWidth: Math.min(implicitWidth, preferredTextWidth)
Layout.maximumWidth: preferredTextWidth
text: "Testing text"
opacity: 0.6
}
}
}
}
There's the toolTipItem QQuickItem too, but I cannot figure out if it is possible to make it show or hide on command (this bit was borrowed from KDE's digitalclock plasmoid:
import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
Item {
Layout.preferredWidth: 200
Layout.preferredHeight: 300
Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation
Plasmoid.compactRepresentation: Item
{
anchors.fill: parent
MouseArea
{
onClicked:
{
plasmoid.expanded = !plasmoid.expanded
}
}
}
plasmoid.toolTipItem: Loader
{
id: toolTipLoader
source: "Tooltip.qml" // Just holds the tooltip contents
}
}

How to highlight the clicked (by mouse) element of a delegate w.r.t FolderListModel?

import QtQuick 2.0
import Qt.labs.folderlistmodel 2.0
Item
{
Component {
id: highlight
Rectangle {
id: rooot
width: 180; height: 20
color: ListView.isCurrentItem ? "black" : "red"; radius: 5
y: list.currentItem.y
Behavior on y {
SpringAnimation {
spring: 3
damping: 0.2
}
}
}
}
ListView {
id: list
width: 480; height: 400
model: folderModel
delegate: Text { id: h; text: fileName }
highlight: highlight
highlightFollowsCurrentItem: false
focus: true
}
FolderListModel
{
id: folderModel
folder: "/home/anisha/"
nameFilters: ["*"]
}
}
This works only when I use keyboard. How to make it work on mouse clicks?
To react on mouse events you need to place MouseArea item.
In the sample below (being an expanded version of the code you provided) I have added a MouseArea to the delegate item that upon being clicked sets the ListView's currentIndex to the delegate's index (a special property visible in the ListView's delegate).
import QtQuick 2.0
import Qt.labs.folderlistmodel 2.0
Item
{
Component {
id: highlight
Rectangle {
id: rooot
width: 180; height: 20
color: ListView.isCurrentItem ? "black" : "red"; radius: 5
y: list.currentItem.y
Behavior on y {
SpringAnimation {
spring: 3
damping: 0.2
}
}
}
}
ListView {
id: list
width: 480; height: 400
model: folderModel
delegate:
Text {
id: h;
text: fileName
MouseArea {
anchors.fill: parent
onClicked: list.currentIndex = index
}
}
highlight: highlight
highlightFollowsCurrentItem: false
focus: true
}
FolderListModel
{
id: folderModel
folder: "/home/anisha/"
nameFilters: ["*"]
}
}
As an alternative approach you might try placing a single MouseArea filling the whole ListView and use ListView's indexAt(int x, int y) method to check which delegate was clicked. However, you would need to care about more edge-conditions in such case.

Scale Element in StateELement

I can write lines below and got it work:
states: State {
name: "active"; when:myitem.activeFocus;
PropertyChanges { target: myitem; z:1000; scale: 1.2 }
}
transitions: Transition {
NumberAnimation { properties: scale; duration: 1000 }
}
But in these lines i can not give specific origin to scale property!
I found Scale Element
transform: Scale { origin.x: 25; origin.y: 25; xScale: 3}
How can i inject this into state property above, because i want to use "when" property of state,
i want scaling to run on that "when" condition.
Or is there any other way to scale in a condition with specifying origins?
Thanks for any idea.
You should set an id for the Scale element. Then you can change its properties in the "active" state.
Here a minimal working example:
import QtQuick 1.0
Item {
height: 200; width: 500
Rectangle {
id: myitem
height: 10; width: 100
anchors.centerIn: parent
color: "blue"
transform: Scale { id: scaleTransform; origin.x: 25; origin.y: 25 }
states: State {
name: "active"; when: mouseArea.pressed
PropertyChanges { target: scaleTransform; xScale: 3 }
}
transitions: Transition {
NumberAnimation { property: "xScale"; duration: 1000 }
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
}
}
Another possibility without states could be:
import QtQuick 1.0
Item {
height: 200; width: 500
Rectangle {
id: myitem
height: 10; width: 100
anchors.centerIn: parent
color: "blue"
transform: Scale {
id: scaleTransform
origin.x: 25; origin.y: 25
xScale: mouseArea.pressed ? 3 : 1 // <-
Behavior on xScale { // for animation
NumberAnimation { duration: 1000 }
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
}
}
xScale is set to 3 when mouse is pressed, else to 1 (if you don't know the " ternary operation" look here).

how to use one model in two views

i have one model for pictures and want to show them in grid view or list view (fullscreen) synchronously. if user clicks on one image in grid view i want this image to be shown in fullscreen mode (in listview).
i have one solution but my list view scrolls till "the current index".
THanx.
PhotoView.qml
Rectangle {
width: settings.pageWidth
height: settings.pageHeight
anchors.fill: parent
VisualDataModel {
id: visualModel
delegate: PhotoDelegate {}
model: photosModel
}
ListView {
id:fullscreen
anchors.fill: parent;
orientation: ListView.Horizontal
snapMode: ListView.SnapOneItem;
flickDeceleration: 500
//highlightFollowsCurrentItem: true
highlightRangeMode: ListView.StrictlyEnforceRange
preferredHighlightBegin: 0; preferredHighlightEnd: 0
cacheBuffer: width;
spacing: settings.largeMargin
model: visualModel.parts.grid
}
GridView{
id:grid
width: settings.pageWidth
height: settings.pageHeight
anchors.fill: parent
cellWidth: settings.gridCellWidth
cellHeight: settings.gridCellHeight
snapMode: GridView.SnapToRow
cacheBuffer: settings.pageHeight
clip: true
model: visualModel.parts.grid
}
// // Menu/Back Button
// IconButton{
// id: backButton
// iconSource: "qrc:///gfx/back_arrow.png"
// anchors.right: parent.right
// anchors.bottom: parent.bottom
// onClicked: mainWindow.close();
// }
// Fade Top
Image{
id:bottom_fade
source: "qrc:///gfx/bottom-page-fade.png"
height: 33
width: settings.pageWidth
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
}
// Fade Bottom
Image{
id:top_fade
source: "qrc:///gfx/top-page-fade.png"
height: 33
width: settings.pageWidth
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
}
}
PhotoDelegate.qml
import QtQuick 1.0
import Qt 4.7
import "../views"
import "../model"
import "../views/components"
Package {
Item { id: listDelegate; Package.name: 'list'; width: settings.pageWidth; height: settings.pageHeight }
Item { id: gridDelegate; Package.name: 'grid'; width: settings.pageWidth; height: settings.pageHeight }
Item {
id: wrapper
width: settings.pageWidth; height: settings.pageHeight
Image {
id: thumbnail; smooth: true; source: thumbnail_url
}
MouseArea {
anchors.fill: parent
onClicked: {
if (wrapper.state == 'inGrid') {
listDelegate.fullscreen.view.currentIndex = index;
wrapper.state = 'fullscreen'
} else {
gridDelegate.grid.view.currentIndex = index;
wrapper.state = 'inGrid'
}
}
}
state: 'inGrid'
states: [
State {
name: 'fullscreen'
ParentChange { target: wrapper; parent: listDelegate; x: 0; y: 0;
width: listDelegate.width; height: listDelegate.height
}
},
State {
name: 'inGrid'
ParentChange {
target: wrapper; parent: gridDelegate
x: 0; y: 0; width: gridDelegate.width; height: gridDelegate.height
}
}
]
transitions: [
Transition {
from: 'inGrid'; to: 'fullscreen'
SequentialAnimation {
PauseAnimation { duration: gridItem.GridView.isCurrentItem ? 0 : 600 }
ParentAnimation {
target: wrapper; via: foreground
NumberAnimation {
targets: [ wrapper]
properties: 'x,y,width,height,opacity'
duration: gridItem.GridView.isCurrentItem ? 600 : 1; easing.type: 'OutQuart'
}
}
}
},
Transition {
from: 'fullscreen'; to: 'inGrid'
ParentAnimation {
target: wrapper; via: foreground
NumberAnimation {
targets: [ wrapper ]
properties: 'x,y,width,height,opacity'
duration: gridItem.GridView.isCurrentItem ? 600 : 1; easing.type: 'OutQuart'
}
}
}
]
}
}
To showing selected item without scrolling until index of selected item is to synchronize the view elements current index:
onCurrentIndexChanged: {
your_component.positionViewAtIndex(currentIndex, YourViewElement.Contain)
}