How can I create a dynamic delegate in QML? - qt

I want to create a view using ListView, where each row will have Rectangle elements defined by a model. Each element of the model represents a row, but these elements may have more than one Rectangle to draw, defined in a role with an array named blocs.
I don't know how to define the delegate to create the number of Rectangles defined in the model for each row of the ListView. I want each bloc to be independent from the others, so in the future I can make them draggable and resizeable.
This is my BlocModel.qml:
import QtQuick 2.0
ListModel {
ListElement{
blocs: [
ListElement{
OriginX: 100
length: 100
color: "blue"
},
ListElement{
OriginX: 300
length: 75
color: "green"
}
]
}
ListElement{
blocs: [
ListElement{
OriginX: 0
length: 50
color: "red"
},
ListElement{
OriginX: 100
length: 75
color: "yellow"
}
,
ListElement{
OriginX: 300
length: 100
color: "blue"
}
]
}
}
This is the main.qml:
import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.4
import QtQuick.Layouts 1.11
import QtQuick.Window 2.11
ApplicationWindow {
id: window
width: 400
height: 500
visible: true
ListView {
id: blocsListView
anchors.fill: parent
model: BlocModel {}
delegate: BlocDelegate {}
}
}
And this the BlocDelegate.qml:
import QtQuick 2.12
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.4
import QtQuick.Layouts 1.11
import QtQuick.Window 2.11
ItemDelegate{
id: root
width: parent.width
height: 50
//each row
Rectangle{
width: parent.width
height: parent.height
color: "gray"
//What should I add here to make the following element repeat according to the number of elements in the array "blocs"?
//each bloc should have the following delegate
/* Rectangle {
id: bloc
x: OriginX
width: length
height: parent.height
color: model.color
}*/
}
}
How can I make my delegate dynamic? Can I use somehow a Javascript for loop or should I use a C++ model and add the blocs from there?
Thanks!

A simple solution that may fit your requirement if all your delegate items are the same type(In this case only rectangle). If you are having delegate items of different types (Rectangle, Item, Button, RadioButton) you have to load the predefined items with matching types into a loader.
=========BlocDelegate.qml=======
import QtQuick 2.9
import QtQuick.Controls 2.2
Column {
width: parent.width
Repeater {
model: blocs
delegate: Rectangle {
height: 40
width: parent.width
color: rectcolor
}
}
}
========BlocModel.qml========
import QtQuick 2.0
ListModel {
ListElement {
blocs: [
ListElement {
rectcolor: "lightgreen"
},
ListElement {
rectcolor: "skyblue"
},
ListElement {
rectcolor: "orange"
}
]
}
ListElement {
blocs: [
ListElement {
rectcolor: "yellow"
},
ListElement {
rectcolor: "steelblue"
}
]
}
}

Related

Is it possible to put a QML widget in a child of an imported QML file?

Say I have the following code in a file called NestedCircle.qml:
import QtQuick 2.12
Rectangle {
color: "blue"
radius: Math.min(width, height)
property Rectangle innerCircle: Rectangle {
radius: Math.min(width, height)
x: 10
y: 10
width: parent.width - 20
height: parent.height - 20
color: "yellow"
}
}
Then in main.qml:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
title: "Three circles"
NestedCircle {
// the line below fails with a syntax error, Expected token `:'
property NestedCircle innerCircle.innerCircle: NestedCircle {
color: "red"
}
}
}
So assuming I need to use NestedCircle.qml and can't edit it (this is a minimal example, not the real application), is there a way to put something inside innerCircle wihtout re-making it?
No need for dynamic object creation here, you can do that purely declaratively. In fact here are 4 ways of doing it, dependings on your needs and the API you want:
// NestedCircle.qml
import QtQuick 2.12
Rectangle {
color: "blue"
radius: Math.min(width, height)
default property alias innerCircleData: innerCircle.data
property alias innerCircle: innerCircle
Rectangle {
id: innerCircle
radius: Math.min(width, height)
anchors.centerIn: parent
width: parent.width - 20
height: parent.height - 20
color: "yellow"
}
}
// main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
visible: true
width: 800
height: 640
title: "Three circles"
Row {
anchors.centerIn: parent
spacing: 30
NestedCircle { // setting parent in the nested circle
id: rootCircle
width: 100
height: 100
NestedCircle {
parent: rootCircle.innerCircle
width: 50
height: 50
anchors.centerIn: parent
}
}
NestedCircle { // assigning to the inner circle's children in the outer circle
width: 100
height: 100
innerCircle.children: NestedCircle {
width: 50
height: 50
anchors.centerIn: parent
}
}
NestedCircle { // using an alias to the innerCircle.data
width: 100
height: 100
innerCircleData: NestedCircle {
width: 50
height: 50
anchors.centerIn: parent
}
}
NestedCircle { // using an alias to the innerCircle.data as a default property
width: 100
height: 100
NestedCircle {
width: 50
height: 50
anchors.centerIn: parent
}
}
}
}
If you want to add an item that is inside another, a possible solution is for the first item to be a child of the second.
NestedCircle.qml
import QtQuick 2.12
Rectangle {
color: "blue"
radius: Math.min(width, height)
property Rectangle innerCircle: inner
Rectangle {
id: inner
radius: Math.min(width, height)
x: 10
y: 10
width: parent.width - 20
height: parent.height - 20
color: "yellow"
}
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
Window {
title: "Three circles"
Component{
id: provider
NestedCircle{
}
}
NestedCircle {
width: 100
height: 100
Component.onCompleted: {
var obj = provider.createObject(innerCircle, {width: 50, height: 50})
obj.anchors.centerIn = innerCircle
}
}
}

Extra space at the bottom of Flow

I have a QML code like this:
MyItem.qml:
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
Item {
id: root
width: parent.width
height: grid.height
Rectangle {
anchors.fill: root
color: "blue"
z: -1
}
Flow {
id: grid
width: parent.width
spacing: 5
Button {
text: qsTr("Button 1")
}
Button {
text: qsTr("Button 2")
}
Button {
text: qsTr("Button 3")
}
}
}
main.qml:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ColumnLayout {
anchors.fill: parent
Button {
Layout.fillWidth: true
Layout.fillHeight: true
text: "hello"
}
MyItem {
Layout.fillWidth: true
}
}
}
If the Flow is wide enough for all three buttons to be at the same line (as with RowLayout) there is an extra empty space at the bottom of the Flow (approximately Button.height * 2). Looks like the Flow height is always calculated as the sum of all its element heights.
What is the logic behind this behavior? How to make the Flow fit its content height?
EDIT1: It is not Flow, but 'root' item has the wrong height.
EDIT2: Download the sample app
The problem with your code is that the root element the expressions:
anchors.fill: parent
height: grid.height
are competing, in the first expression you indicate that the dimensions of the root will take the size of the window and this implies the height but in the next expression you are indicating that the height will no longer be from the window but from the grid, so that generates an indefinite behavior. The only solution is to establish that the width of the root item is that of the window.
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Item {
id: root
height: grid.height
width: parent.width
Rectangle {
anchors.fill: root
color: "blue"
}
Flow {
id: grid
width: parent.width
spacing: 5
Button {
text: qsTr("Button 1")
}
Button {
text: qsTr("Button 2")
}
Button {
text: qsTr("Button 3")
}
}
}
}
Update:
It seems that you do not know how they work (read https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#details), by default the height that is taken is the implicitHeight.
Also if you use layout you should not set anchors in the items that are directly affected by the layouts, in your case the CommandsTab is affected by the Layout so you should not use width: parent.width, is unnecesary.
CommandsTab.qml
import QtQuick 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
Item {
id: root
implicitHeight: grid.height
Rectangle {
anchors.fill: root
color: "blue"
z: -1
}
Flow {
id: grid
width: parent.width
spacing: 5
Button {
text: qsTr("Button 1")
}
Button {
text: qsTr("Button 2")
}
Button {
text: qsTr("Button 3")
}
}
}
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
import QtQuick.Layouts 1.3
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ColumnLayout {
anchors.fill: parent
Button {
Layout.fillWidth: true
Layout.fillHeight: true
text: "hello"
}
CommandsTab {
Layout.fillWidth: true
}
}
}

How to add animation to a component while destroying in QML

I am adding Animation to my project and most of the UI is dynamic. Currently, i am not able to add animation to the Component while destroying. Following is the test application code which describes the same:
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property bool removeClicked : true
Row{
anchors.bottom: parent.bottom
spacing:20
Button{
text:"Add"
onClicked: {
removeClicked = false
comp.createObject(myrow)
}
}
Button{
id:remBtn
text:"Remove"
onClicked: {
removeClicked = true
myrow.children[0].destroy() //Destroy the object
}
}
}
Row{
id:myrow
height:40
spacing:20
}
Component{
id:comp
Rectangle{
width:20
height:30
color: "red"
NumberAnimation on opacity{
id: destroyAnimation
from :removeClicked ? 1:0
to: removeClicked ? 0:1
duration: 1000
}
}
}
}
Any help will be Appreciated!!!
Shou should perform the animation before calling destroy on your dynamically created items. You could use a SequentialAnimation, combined with a ScriptAction to do so.
Here's a small example (the dynamic balls get destroyed when one clicks on them).
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
visible: true
width: 640
height: 480
Button{
text:"Add"
anchors.centerIn: parent
onClicked: {
comp.createObject(parent)
}
}
Component{
id:comp
Rectangle{
id: ball
height:30
width:height
radius: height/2
x: Math.random()*parent.width-width
y: Math.random()*parent.height-height
color: Qt.hsla(Math.random(), 0.5, 0.5, 1)
opacity: 0
Component.onCompleted: opacity = 1
Behavior on opacity{ NumberAnimation {}}
SequentialAnimation
{
id: destroyAnim
ScriptAction{script: ball.opacity = 0}
NumberAnimation{target: ball; property:"scale"; to: 5}
ScriptAction{script: ball.destroy()}
}
MouseArea
{
anchors.fill: parent
onClicked:destroyAnim.start()
}
}
}
}

how can i set text property of 'Text' QML Component in other qml file?

This is my Box.qml
I want to use this Box as customized QML Component.
import QtQuick 2.5
Rectangle{
id: sample
width: 100
height: 35
border.color: "Black"
color: "#778899"
Text{
font.pointSize: 10
font.bold: true
anchors.centerIn: parent
}
}
This is my main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Repeater")
Box{
text: "Number"
}
}
But this is not working
I am getting Following Error
qrc:/main.qml:11 Cannot assign to non-existent property "text"
You have to expose that property through property.
Box.qml
import QtQuick 2.5
Rectangle{
property string mytext
id: sample
width: 100
height: 35
border.color: "Black"
color: "#778899"
Text {
font.pointSize: 10
font.bold: true
anchors.centerIn: parent
text: mytext
}
}
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Repeater")
Box{
mytext: "Number"
}
}
Or use alias:
Box.qml
import QtQuick 2.5
Rectangle{
property alias text: txt.text
id: sample
width: 100
height: 35
border.color: "Black"
color: "#778899"
Text {
id: txt
font.pointSize: 10
font.bold: true
anchors.centerIn: parent
}
}
main.qml
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Repeater")
Box{
text: "Number"
}
}

center a checkbox in tableview with QML

I am using a checkbox control in a TableView, which I define with QML as follows:
import QtQuick 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
TableViewColumn {
title: ""
role: "check"
delegate: CheckBox {
anchors.fill: parent
checked: false
anchors { horizontalCenter: parent.horizontalCenter }
}
}
I can use it in a TableView as follows:
import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
ControlView {
visible: true
width: parent.width; height: parent.height
anchors.centerIn: parent
TableView {
id: reviewTable
width: parent.width; height: parent.height
anchors.centerIn: parent
TableViewCheckBoxColumn {
title: "Upload"
width: 100
resizable: false
}
TableViewColumn {
resizable: true
role: "Dummy"
title: "Dummy"
width: 250
}
style: TableViewStyle {
headerDelegate: Rectangle {
height: textItem.implicitHeight
width: textItem.implicitWidth
Text {
id: textItem
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
text: styleData.value
renderType: Text.NativeRendering
font.bold: true
}
}
}
model: ListModel {
ListElement {
Dummy: "value 1"
}
ListElement {
Dummy: "value 3"
}
}
}
}
Now despite having set the anchors (or any alignment property that were available), the checkbox remains aligned to the left. How can I center it horizontally wrt to the column?
Wrap the CheckBox in the delegate as below:
Item {
anchors.fill: parent
CheckBox {
anchors.centerIn: parent
checked: false
}
}
And btw, don't mix QtQuick versions in the same application.

Resources