Change property of delegate on signal - qt

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";
}
}
}

Related

ReferenceError: "Item" is not defined

I want to check if an item of component is vissible or not but I don't know how to do this.
Here is my code but error is "ReferenceError: tagFilterPlaylist is not defined" how to solve this?
Repeater {
id: tagRepeater
model: main.playListBrowseModel
Component {
id: componentTagFilterPlaylist
BasicUI.Tag {
id: tagFilterPlaylist
tag: "playlist"
selected: true
}
}
Loader {
id: filterLoader
sourceComponent:
if (item_type === "playlist"){
console.debug("check"+tagFilterPlaylist)
if (!tagFilterPlaylist) {
tagFilterPlaylist.visible = true;
return componentTagFilterPlaylist
}
}
}
}
You can't refer to an id within a Component because you first need to have an instance of that Component. What you should do is simply keep a boolean property outside the Repeater that keeps track of whether or not you're displaying your one and only tagFilterPlaylist.
property bool playlistVisible: false
Component {
id: componentTagFilterPlaylist
...
}
Repeater {
id: tagRepeater
model: main.playListBrowseModel
Loader {
id: filterLoader
sourceComponent:
if (item_type === "playlist"){
if (!playlistVisible) {
playlistVisible = true;
return componentTagFilterPlaylist
}
}
}
}

How to dynamically append elements to ListModel from exterior scope

Suppose I had the Component
Component {
id: myComp1
Item {
id: item
ListView {
id: listView
model : ListModel { id: listModel }
delegate : RowLayout { /* display model data*/ }
Component.onCompleted {
// get data from server ...
model.append(dataFromServer)
}
}
}
}
Then I have a second Component, which is another page in the stack, and I want to use this component to update mycomp1, i.e:
Component {
id: myComp2
Button {
onClicked: {
myComp1.item.listView.listModel.append(someNewData) // want to be able to do this
}
}
}
And these components are tied together in a StackView
Now, this doesnt seem to work since myComp2 cant seem to access the necessary scope to update the model of myComp1. Is there any way around this?
Thanks for the help.
The problem is that a Component is like a type declaration. It does not define an instance of an object, so you cannot access its members.
You could pull the ListModel outside of that Component so that both Components can access it.
ListModel {
id: listModel
}
Component {
id: comp1
ListView { model: listModel }
}
Component {
id: comp2
Button {
onClicked: { listModel.append(someNewData) }
}
}

QML property binding for repeater item

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

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
}
}
}
}

QML/QtQuick Binding delegate's property with ListView's currentIndex

Inside the delegate, I bind Image's source property to ListView's currentIndex which determines which image to load. This works great:
ListView {
id: answerListView
model: 5
currentIndex: -1
delegate: answerDelegate
}
Component {
id: answerDelegate
Item {
width: 100
height: 100
Image {
source: answerListView.currentIndex === index
? "selected.png" : "not_selected.png"
}
MouseArea {
anchors.fill: parent
onClicked: {
answerListView.currentIndex = index
}
}
Component.onCompleted: {
answerListView.currentIndex = 1; // doesn't work!!
}
}
}
Since currentIndex: -1, it will always show not_selected.png. To show selected.png, I change currentIndex in Component.onLoaded inside delegate.
I was expecting image to load selected.png since currentIndex was updated.
What is the correct way and what am I misunderstanding here?
Ok, new guess:
You want to have the posibility to select multiple Items. As currentIndex only stores one value, which is the value you assigned it last, you can use it to mark only one Item.
Therefore you need to find another way to store your selection. You might for example have a property in the delegate: property bool selected: false which you set to true upon selection.
The problem with this solution is, that it only works if all Items are instantiated at all times. As soon as one Item will be destroyed again, the information will be lost, and uppon the next creation, the selection/unselection is undone.
The better way would be to introduce a role in your model, that stores the selection outside of the non-persistant delegates for you:
ListView {
id: answerListView
model: lm
delegate: answerDelegate
width: 100
height: 220
}
ListModel {
id: lm
ListElement { selected: false }
ListElement { selected: false }
ListElement { selected: false }
ListElement { selected: false }
ListElement { selected: false }
}
Component {
id: answerDelegate
Item {
width: 100
height: 100
Image {
anchors.fill: parent
source: model.selected ? "selected.png" : "notselected.png"
}
Text {
text: (model.selected ? 'selected ' : 'notselected ')
}
Component.onCompleted: {
model.selected = true // doesn't work!!
}
MouseArea {
anchors.fill: parent
onClicked: {
model.selected = !model.selected
}
}
}
}
Another option would probably be a ItemSelectionModel, but I don't know atm, how it works.
Otherwise your example works as expected:
The Item with index 1 is shown, and displays the Image selected.png. All other Items are not shown (for the ListView is to small) but if the would be shown, they would show notselected.png for the answerListView.currentIndex is not equal to their index.

Resources