QML property binding for repeater item - qt

How do property bindings work for repeater items? I am trying to use a property binding on an element generated by a repeater, but the binding doesn't work:
Rectangle
{
id: root
property int currentLocation: 0
Button {
text: "Change location"
onClicked: root.currentLocation = (root.currentLocation + 1) % 2
}
Repeater {
id: locationlRepeater
model: locationModel
Item {
visible: root.currentLocation == model.location // Why doesn't this update on button click?
Text {
id: locationText
text: "Location: " + model.location
}
}
}
ListModel {
id: locationModel
ListElement {
location: 0
// etc.
}
ListElement {
location: 1
// etc.
}
}
Updated: using Qt 5.6.2

Related

How to create different component base on ListModel in QML

There are 9 parameters that I need to use TextField1 to input value.
So I use
ListModel lstPara {
ListElement{
text:"A";value:"123"
}...(9 Elements)
}
Grid{
id: grid
anchors.fill: parent
columns: 3
spacing: 5
Repeater {
id: rpPara
model: lstPara
delegate: TextField1 {
}
}
}
But now there is a parameter that i need to use in another QML type to set the value, all others are used in TextField1.
I tried to define ListModel like this
ListModel lstPara{
ListElement {
text: "A";
type: 1";
value: "123"
}
ListElement {
text: "B";
type: 2";
value: "321"
}
...(9 Elements)
}
Grid{
id: grid
anchors.fill: parent
columns: 3
spacing: 5
Repeater {
id: rpPara
model: lstPara
(some code : like this)
delegate: {
return type === 1 ?
TextField1 {
}
:
another QML type {
}
}
}
}
The code above can not run.
And I don`t want to write 8 TextField1 and 1 another QML type.
So, is there a way to use ListModel?
You can't directly use an Item declaration in a conditional expression like that, but you can do it with a Component. Here's an example of how to do it using a Loader as your delegate, and choosing which Component to load based on the model:
ListModel {
id: lstPara
ListElement {
text: "A"
type: 1
value: "123"
}
ListElement {
text: "B"
type: 2
value: "321"
}
}
Grid {
id:grid
anchors.fill: parent
columns: 3
spacing: 5
Repeater {
id: rpPara
model: lstPara
delegate: Loader {
sourceComponent: type === 1 ? someText : otherText
onLoaded: {
item.text = text
item.value = value
}
}
}
Component {
id: someText
Text {
property string value
color: "blue"
}
}
Component {
id: otherText
Text {
property string value
color: "red"
}
}
}

Qml Get the text of listElement using the mouseAera

I want to get the name of listElement (append dynamicaly with python) when I click for reuse this name with python function. How can I get this name ?
In this example I can only get the index 0 element...
QML part :
Rectangle {
id: listRowDb
anchors.top: toolbar.bottom
width: head_row.width
height: units.gu(100)
ListModel {
id: listDb
ListElement {
name: ""
}
}
Component {
id: listDbDelegate
Row {
spacing: 100
Text { text: 'Nom de la DB : ' + name}
}
}
ListView {
id: listView1
anchors.fill: parent
model: listDb
delegate: listDbDelegate
#Don't work. When I click I get the index 0 name.
MouseArea {
anchors.fill: parent
onClicked: {python.call('example.speak2', [listDb.get(listView1.currentIndex).name], function(returnValue) {console.log(returnValue)});}
}
}
}
Python {
id: python
Component.onCompleted: {
addImportPath(Qt.resolvedUrl('../src/'));
importModule('example', function() {
console.log('module imported');
#Just a litlle test
python.call('example.speak', ['Hello World!'], function(returnValue) {
console.log('example.speak returned ' + returnValue);
})
#Python list all DB and append in listModel
python.call('example.listDb', [], function(returnValue) {
for(var i=0; i<returnValue.length; i++) {
listDb.append({"name":returnValue[i]});
}
});
});
}
The python part work (only print element name)
You have to set the MouseArea on the delegate:
Component {
id: listDbDelegate
Row {
spacing: 100
Text {
text: 'Nom de la DB : ' + name
MouseArea{
anchors.fill: parent
onClicked: console.log(name, index)
}
}
}
}
ListView {
id: listView1
anchors.fill: parent
model: listDb
delegate: listDbDelegate
}

Get value or property from Stackview item when it popped in Qml

Is there any way to get value or property from Stackview item when it popped in Qml?
I want to get the edited name in 'EditProfile.qml' when it popped to 'Profile.qml' in the below project.
main.qml
StackView {
id: stackView
Component.onCompleted: push('Profile.qml', {name : 'David'})
}
Profile.qml
Page {
property string name: ''
Column {
Text {
text: 'name' + name
}
Button {
text: 'Edit'
onClicked: stackView.push('EditProfile.qml', {name : name})
}
}
}
EditProfile.qml
Page {
property alias name: txtName.text
Column {
TextEdit {
id: txtName
text: name
}
Button {
text: 'Back'
onClicked: stackView.pop()
}
}
}
After reading QT manual carefully, I found the answer. The push function returns the item which is pushed. so:
Profile.qml
Page {
id: root
property string name: ''
Column {
Text {
text: 'name' + name
}
Button {
text: 'Edit'
onClicked: {
var item = stackView.push('EditProfile.qml', {name : name})
item.exit.connect(change);
function change(text) {
item.exit.disconnect(change);
root.name = text;
}
}
}
}
}
EditProfile.qml
Page {
signal exit(var text)
property alias name: txtName.text
Column {
TextEdit {
id: txtName
text: name
}
Button {
text: 'Back'
onClicked: {
exit(txtName.text)
stackView.pop()
}
}
}
}

Change property of delegate on signal

I have a TreeView, and on the signal onSortIndicatorChanged I would like to set the checked property of a CheckBox inside the delegate of a TableViewColumn in my TreeView. However, I don't know how to do this.
Component {
id: mycomp
Item {
id: myitm
CheckBox {
id: mycbx
checked: true
}
}
}
TreeView {
TableViewColumn {
delegate: myDelegate
}
onSortIndicatorChanged{
// set the checked property of the delegates to true
}
}
You could try to add a property checked to your tree view and bind the check boxes to this property instead. Then, in the even handler, just set the value of the TreeView's property:
Component {
id: mycomp
Item {
id: myitm
CheckBox {
id: mycbx
checked: view.checked
}
}
}
TreeView {
id: view
property bool checked: false
TableViewColumn {
delegate: mycomp
}
onSortIndicatorChanged {
view.checked = true
}
}
Update:
In case you want to set a per-item checked state, you can use a similar approach where you store a function as a property of the view. In the delegate, you could call this stored function to get an item specific property value:
Component {
id: mycomp
Item {
id: myitm
CheckBox {
id: mycbx
checked: view.checkFunction ? view.checkFunction(modelData) : true
}
}
}
TreeView {
id: view
property var checkFunction: null
TableViewColumn {
delegate: mycomp
}
onSortIndicatorChanged {
view.checkFunction = function(modelData) {
// Calculate the checked state based on the modelData
return modelData.foo == "bar";
}
}
}

How to link from ComboBox to another ComboBox

I try to make a converter app depending on two ComboBoxes using Controls 2.
For example:
// First combobox is input unit:
ComboBox {
id: res_combo1
width: res_comborect1.width
currentIndex: 0
model: ListModel {
id: model1
ListItem { text: qsTr("meter") }
ListItem { text: qsTr("mile") }
ListItem { text: qsTr("km") }
}
}
}
// Second combobox is output unit:
ComboBox {
id: res_combo2
width: res_comborect1.width
currentIndex: 0
model: ListModel {
id: model2
ListItem { text: qsTr("meter") }
ListItem { text: qsTr("mile") }
ListItem { text: qsTr("km") }
}
}
}
When current index is selected from first combobox, second combobox should remove the current index. When another index is selected from first, second should restore previous index and it should remove current index dynamically. For example if meter is selected, second combobox should be {"mile", "km"}
I only know a very long way that can be performed with combination of a few index, but my combobox data includes 20 item so I cannot apply this way. Long way:
function visible1(){
if(res_combo1.currentIndex==0){
return true
}
else{
return false
}
}
function visible2(){
if(res_combo1.currentIndex==1){
return true
}
else{
return false
}
}
function visible3(){
if(res_combo1.currentIndex==2){
return true
}
else{
return false
}
}
// if meter is selected from first combobox, second combobox:
RowLayout{
visible: parent.visible1()
ComboBox {
id: outcombo1
currentIndex: 0
model: ListModel {
id: model_o1
ListElement { text: qsTr("mile") }
ListElement { text: qsTr("km") }
}
}
}
// if mile is selected from first combobox:
RowLayout{
visible: parent.visible2()
ComboBox {
id: outcombo1
currentIndex: 0
model: ListModel {
id: model_o2
ListElement { text: qsTr("meter") }
ListElement { text: qsTr("km") }
}
}
}
// if km is selected from first combobox:
RowLayout{
visible: parent.visible3()
ComboBox {
id: outcombo1
currentIndex: 0
model: ListModel {
id: model_o3
ListElement { text: qsTr("meter") }
ListElement { text: qsTr("mile") }
}
}
}
I think we cannot change ListItem dynamically, so JavaScript with JSON list model is required by finding the current index via:
function find(model, criteria) {
for(var i = 0; i < model.count; ++i) if (criteria(model.get(i))) return i
return null
}
But I couldn't do this in QML. Is there any solution? Thanks
You cannot change a ListItem, but you CAN change the ListModel itself.
ListModel {
id: fromModel
ListElement {text: "m" }
ListElement {text: "mile" }
ListElement {text: "km" }
}
ListModel {
id: toModel
function updateModel()
{
// Update model Here
}
}
RowLayout
{
anchors.centerIn: parent
ComboBox
{
id: fromCombobox
model: fromModel
onCurrentIndexChanged: toModel.updateModel()
}
ComboBox
{
id: toComboBox
model: toModel
}
}
This way, every time the first combo changes, the second combo's model will be updated.
Before you update the model 2, check if the previously selected item in combo 2 will still be available after the update, in order to restore it once the model2 is rebuilt.
function updateModel()
{
// Save old selection
var selectedFrom = fromModel.get(fromCombobox.currentIndex).text
if(toComboBox.currentText != selectedFrom)
var valueToRestore = toComboBox.currentText
else
valueToRestore = ""
// Update model
clear()
for(var i=0; i<fromModel.count; i++)
{
if(i == fromCombobox.currentIndex)
continue
append(fromModel.get(i))
}
//Restore selection
for(var i=0; i<toModel.count; i++)
{
// If no value to restore, select first available
if(valueToRestore == "")
{
if(toModel.get(i).text != selectedFrom)
{
toComboBox.currentIndex = i
break
}
}
// Else, restore previously selected item
else
{
if(toModel.get(i).text == valueToRestore)
{
toComboBox.currentIndex = i
break
}
}
}
}

Resources