Qt QML specify ListView delegate property from outside - qt

I have a ChoicePage.qml like this:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
Item {
property alias searchBar: searchBar
property alias model: listView.model
property alias itemDelegate: listView.delegate
ColumnLayout {
height: parent.height
width: parent.width * 0.9
anchors.horizontalCenter: parent.horizontalCenter
TextField {
id: searchBar
Layout.preferredHeight: parent.height * 0.1
Layout.preferredWidth: parent.width
placeholderText: qsTr("Search..")
}
ListView {
id: listView
Layout.fillHeight: true
Layout.preferredWidth: parent.width
ScrollBar.vertical: ScrollBar {}
clip: true
spacing: height * 0.01
}
}
}
And a TestChoicePage.qml like this:
import QtQuick 2.0
ChoicePage {
model: proxy.list // c++ proxy that expose a list of QObject*
itemDelegate: RadioButton {
height: ListView.view.height * 0.1
width: ListView.view.width
text: modelData.description
}
}
I would like to have the RadioButton delegate item inside ChoicePage and define the modelData.description or whatelse the page needs from TestChoicePage, Test2ChoicePage and so on.
Is it possible?

Model's data cannot be accessed outside the delegate. Any model related bindings have to defined within the delegate.
Non model related properties of the delegate can be controlled externally.
New properties can be defined outside the delegate. These new properties can be binded to the delegate's properties.
See example below:
List.qml:
ListView {
width: 200
height: 200
spacing: 5
property string rectColor: ""
delegate: Rectangle{
width: parent.width
height: parent.height / 5
color: rectColor
required property string modelProperty
RadioButton{
text: parent.modelProperty
}
}
}
main.qml:
MyList{
model: myCppList
rectColor: "green"
}

Related

QML - Using ListView with dynamically generated ListElement

I have this QML with the ListView:
import QtQuick 2.0
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.0
import smah.light 1.0
import smah.zone 1.0
Page {
id: page
property var lights: ({})
property var lightNames: ([])
title: "Device control"
ListView {
id: lightList
anchors.fill: parent
model: listModel
delegate:
Rectangle{
width: lightList.width
height: lightList.height / 8
}
}
ListModel {
id: listModel
dynamicRoles: true
Component.onCompleted: {
listModel.clear()
for (var i=0; i<lights.length; i++) {
var component = Qt.createComponent("LightControls.qml",listModel)
listModel.append(component.createObject(lightList, {light_name: lights[i].getName }))
}
}
}
}
The LightControls.qml is:
import QtQuick 2.0
import QtQuick.Controls 2.5
import smah.light 1.0
Rectangle {
id: rectangle
property int light_type: 0
property int light_id: 0
property var light_name: "_DEF"
width: parent.width
height: 50
color: "#040000"
Text {
id: txtName
color: "#fefdfd"
text: qsTr(light_name)
anchors.left: parent.left
anchors.leftMargin: 15
anchors.top: parent.top
anchors.topMargin: 8
font.pixelSize: 20
}
Slider {
id: slider
x: 179
y: 10
width: 302
height: 22
value: 0.5
}
Text {
id: txtValue
x: 517
width: 45
height: 15
color: "#ffffff"
text: qsTr("Text")
anchors.top: parent.top
anchors.topMargin: 8
font.pixelSize: 20
}
Button {
id: button
x: 694
y: 10
text: qsTr("Button")
}
}
I want a clean scrollable list with each of these items generated shown in it. I've looked at using a Repeater instead of the list, but I'll have more elements in the list than are desired on the screen. When running the program, everything is garbled into a single incoherent mess:
There are a few larger issues with your code:
You're trying to add a visual item to a ListModel, which expects ListElement objects. You can use append() to add rows to a ListModel.
Your root delegate item is a Rectangle, which doesn't manage the layout of its children. Use a RowLayout or Row instead.
Your delegate should be delegate: LightControls {}.

How to dynamically update a child combobox from parent combobox

I have a QML app that has two combo boxes, one is parent (country) combo box, other one is child (city) combo box. When selecting a country, the child combo box should have cities of the country at the same time.
I have a code that first combo box selects country but it doesn't set the list model of city's combo box. It sets after reopening the app.
import QtQuick 2.11
import QtQuick.Window 2.11
import QtQuick.Controls 2.4
import QtQuick.Layouts 1.3
import Qt.labs.settings 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
property alias comboBox: comboBox2
Settings{
property alias country : comboBox2.currentIndex
}
Dialog {
id: dialog
title: "Select Country"
implicitWidth: parent.width
implicitHeight: parent.height/2
Column{
ComboBox {
id: comboBox2
x: 199
y: 176
width: 277
height: 48
currentIndex: 0
flat: false
model: ["USA","Russia","Iran"]
}
}
}
ColumnLayout{
anchors.horizontalCenter: parent.horizontalCenter
spacing:20
width: parent.width
Button{
anchors.horizontalCenter: parent.horizontalCenter
id:select_country
text:"select country"
onClicked: dialog.open()
}
RowLayout{
anchors.horizontalCenter: parent.horizontalCenter
spacing:5
width: parent.width
Text {
text:"Select City: "
}
ComboBox {
id: comboBox1
x: 199
y: 176
width: 277
height: 48
model: ListModel {
id: model1
dynamicRoles: true
Component.onCompleted: {
coord_combo()
}
function coord_combo(){
var i = comboBox1.currentIndex
if (comboBox2.currentIndex===0){
comboBox1.model = ["New York","Washington","Houston"]
return comboBox1
}
else if (comboBox2.currentIndex === 1){
comboBox1.model = ["Moscow","Saint Petersburg","Novosibirsk"]
return comboBox1
}
else if (comboBox2.currentIndex === 2){
comboBox1.model = ["Tehran","Tabriz","Shiraz"]
return comboBox1
}
}
}
}
}
}
}
I used javascript functions to change Material QML Theme using combo box. But the same logic doesn't work for updating other combo box's list model. Is there any way to update the child combobox dynamically using qml and javascript?
You should use onCurrentIndexChanged signal in parent ComboBox and update the child whenever it is triggered. Something like this;
onCurrentIndexChanged:
{
if(currentIndex === 0)
comboBox1.model = ["New York", "Washington", "Houston"]
...
}
You can go further and write a function to get the cities of current country. Then you can update the model of child ComboBox whenever the currentIndex of parent ComboBox changes.

QML reference errors

I have a small QML-project and I'm facing a problem with qml component references. So I'm trying to start the NumComponent.qml's numberTimer from startButton in the main.qml.
main.qml
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
NumComponent{} //my component written in NumComponent.qml
Rectangle{
id: startButton
anchors.centerIn: parent
height: parent.height * 0.2
width: height
color: "lightblue"
MouseArea{
anchors.fill: parent
onClicked: {
numberTimer.start();
}
}
}
}
NumComponent.qml
import QtQuick 2.0
Rectangle {
id: numberRect
color: "red"
height: parent.height * 0.4
width: height
Text{
id: numberText
anchors.centerIn: parent
text: ""
}
Timer{
id: numberTimer
interval: 100
repeat: true
onTriggered: {
numberText.text = Math.floor(Math.random() * 8);
}
}
}
I get this error: "qrc:/main.qml:22: ReferenceError: numberRect is not defined"
Give your NumComponent in main.qml an id:
NumComponent{
id: numComponent
} //my component written in NumComponent.qml
change your onClicked handler to:
numComponent.startTimer();
Another variant:
Add to your numberRect a property alias:
property alias timed: numberTimer.running
Change you onClicked handler in main to:
numComponent.timed = !numComponent.timed;
Add to your NumComponent.qml in your root item:
function startTimer() {
numberTimer.start();
}
Now you can start and stop your timer.

Qt - change property from a component in a different qml file

Update 1
The idea is to be able to change the front and back of CardForm from main.qml because i want to be able to use multiple CardForm instances. I tried to do what they did here but it doesnt work.
Here is the code:
CardForm.qml
import QtQuick 2.0
Flipable {
id: sCard
width: 75
height: 200
property bool flipped: false
property string front: "Front"
property string back: "Back"
property alias callFront : front
property alias callBack : back
front: Rectangle{
id: front
anchors.fill: sCard
border.width: 2
border.color: "black"
radius: 5
Text{
anchors.centerIn: parent
text: sCard.front
}
}
back: Column{
Rectangle{
id: back
anchors.fill: sCard
radius: 5
border.width: 2
border.color: "black"
Text{
anchors.centerIn: parent
text: sCard.front
}
Text{
anchors.centerIn: parent
text: sCard.front
}
}
}
transform: Rotation{
id: flip
origin.x: sCard.width
origin.y: sCard.height/2
axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
angle: 0 // the default angle
}
states: State {
name: "back"
PropertyChanges {
target: flip
angle: 180
}
when: sCard.flipped
}
transitions: Transition{
NumberAnimation {
target: flip
property: "angle"
duration: 200
}
}
MouseArea{
anchors.fill: parent
onClicked: sCard.flipped = !sCard.flipped
}
}
main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Neuro Seed")
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Column {
CardForm{
id: test
anchors.centerIn: parent
test.callFront: "Hello World!"
test.callBack: "Bonjour le Monde!
}
}
}
}
Here are the error messages:
SHGetSpecialFolderPath() failed for standard location "Shared Configuration", clsid=0x1c. ()
qrc:/main.qml:17:13: QML CardForm: back is a write-once property
qrc:/main.qml:17:13: QML CardForm: front is a write-once property
qrc:/main.qml:16:9: QML Column: Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column. Column will not function.
the c1.getFront() and getBack() were from a C++ class that I made. I changed these to "Hello World!" and "Bonjour le Monde!"
So after many hours of struggling I figured out that to create a property which is accessible by other .qml files you must create a property alias name: id.property. The id must point towards an existing instance of a object in your code and the property of this instance that you wish to be able to change from the outside. So in my case it would be like so:
CardForm.qml
Flipable {
id: sCard
width: 75
height: 200
property bool flipped: false
property alias frontText : front.text
front: Rectangle{
id: front
anchors.fill: sCard
border.width: 2
border.color: "black"
radius: 5
Text{
anchors.centerIn: parent
text: frontText
}
}
}
and in the main.qml
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Neuro Seed")
Rectangle {
anchors.fill: parent
CardForm{
id: test
anchors.centerIn: parent
frontText: "Hello World!"
}
}
}
}

How to apply Qt Graphical Effect on Delegate of Repeater in QML

I want to apply the QtGraphicalEffect ColorOverlay to an Image in a Repeater delegate. The problem is that I have to set the id of the Image as the source of the ColorOverlay, but I don't know the id, because it is dynamically created by the Repeater.
import QtQuick 2.4
import QtGraphicalEffects 1.0
Item {
id:mainItem
width: 800
height: 400
property string vorneColor: "red"
ListModel {
id: safeRailModel
ListElement {name: "vorne"; imageSource:"images/saferail/ring_vorne.png";}
ListElement {name: "vorneLinks"; imageSource:"images/saferail/ring_vorne_links.png"; }
}
Component {
id: imageDelegate
Image {
source: imageSource
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
fillMode: Image.PreserveAspectFit
opacity: 1
visible: true
}
}
Repeater {
id: safeRailRepeater
model: safeRailModel
delegate: imageDelegate
}
Component {
id: effectsDelegate
Item{
id:effectsItem
ColorOverlay {
anchors.fill: safeRailRepeater.itemAt(index)// <-- This doesn't work
source: safeRailRepeater.itemAt(index)// <-- This doesn't work
color: vorneColor
}
}
}
Repeater {
id: safeRailEffectsRepeater
model: safeRailModel
delegate: effectsDelegate
}
}
How can I set source and anchors.fill properties?
I searched everywhere, but I've only found something along the lines of safeRailRepeater.itemAt(index) or safeRailRepeater.itemAt(index).item but neither the former nor the latter works.
Side note: the ColorOverlay doesn't need to be in a seperate delegate and Repeater.
It would be great if somebody has a solution for this problem or could point me in the right direction.
Thank you very much!
The problem is that the itemAt() function call returns null because the other Repeater hasn't loaded its items yet. Also, the function call won't ever be reevaluated, because none of its arguments ever change, so you'll always get null.
The design is a bit odd though; I'd suggest moving the ColorOverlay into the same delegate, as you mentioned that it doesn't have to be in a separate Repeater:
import QtQuick 2.0
import QtQuick.Window 2.0
import QtGraphicalEffects 1.0
Window {
id: mainItem
width: 800
height: 400
visible: true
property string vorneColor: "red"
ListModel {
id: safeRailModel
ListElement { name: "vorne"; vorneColor: "salmon"; }
ListElement { name: "vorneLinks"; vorneColor: "steelblue"; }
}
Component {
id: imageDelegateComponent
Rectangle {
id: delegate
color: "grey"
opacity: 1
visible: true
width: 64
height: 64
layer.enabled: true
layer.effect: ColorOverlay {
color: vorneColor
}
}
}
Row {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
Repeater {
id: safeRailRepeater
model: safeRailModel
delegate: imageDelegateComponent
}
}
}
Using the layer API of Item is a convenient way of specifying graphical effects.
I also changed the Image to a Rectangle, since we don't have access to those images, and put the Repeater within a row, so that you can see all of the delegates.

Resources