I have a ListView
which is initialized with a custom C++ model named rootModel
inside Qml.
The model inherits QAbstractListModel
and defines a QVector<customType>
as a private member to populate the model.
In my ApplicationWindow
I have created a Dialog
in which I change the model and call the setList()
function to update it. This works fine.
I also want to connect the model's size to a ScrollView
's int
property. This property will define the children
of a RowLayout
.
The problem is that when I try to bind this property to the model's size, the application crashes.
FYI, all the modifications of the model follow the Qt's rules. rowCount()
is Q_INVOKABLE
. I have also tried using the onModelChanged
handler but this didn't work (I have checked in the documentation that this signal is emitted when modelReset()
is emitted, which is taking place inside setList()
through endResetModel()
I believe this is a straightforward procedure (have already performed property bindings many times in my project) but doesn't work as expected.
I quote some sample code of my project.
//main.qml
ConfiguredChannels{
id: configuredList
anchors{
left: parent.left
top: devices.bottom
right: tabs.left
bottom: parent.bottom
}
}
TabArea {
id: tabs
y: toolBar.height
x: parent.width / 8
anchors {
top: toolBar.bottom
}
width: 3 * parent.width / 4
height: 3 * parent.height / 4
countPWM: configuredList.model.rowCount() //This is where I want to bind.
}
//ConfiguredChannels.qml
id: confChanView
header: confChanHeader
model: ChannelModel{
id: rootModel
list: channelList
}
//TabArea.qml
Item{
id: tabAreaRoot
property alias channelsPWM: channelsPWM
property int countPWM
ScrollView{
id: scrollPWM
anchors.fill: parent
contentItem: channelsPWM.children
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOn
RowLayout{
id: channelsPWM
spacing: 0
Layout.fillHeight: true
Layout.fillWidth: true
Component.onCompleted: {
var namesPWM = [];
for (var i=0; i<countPWM; i++){
namesPWM.push("Channel"+(i+1));
}
createChannels(countPWM, "PWM", channelsPWM, namesPWM);
}
}
}
[EDIT 1]
After looking closer I realized that with my current implementation, even if I bind properly to the model's size I would still not be able to create the desired amount of RowLayout
's children
on demand (after I change the model in the Dialog
Configuration.qml
).
That is because I have put their creation inside RowLayout
's Component.onCompleted
handler. The content of this handler will be executed once the Configuration.qml
will be initialized inside main.qml
for the first time. After that, any other change to the countPWM
will not make a difference since the Component is already Completed! Please correct me if I am wrong at this point.
Based on that, I have followed another implementation. I have created a "wrapper" function of createChannels
, named createStrips(countPWM)
. This way, to update properly the RowLayout
's children
I have to call this function.
\\Configuration.qml
\\more code
currentModel.setList(newList)
tabs.createStrips(tableModel.count) \\tableModel is used to populate the newList that will be set to the model
newList.clear()
\\more code
\\TabArea.qml
function createStrips(countPWM){
var namesPWM = [];
for (var i=0; i<countPWM; i++){
namesPWM.push("Channel"+(i+1));
}
createChannels(countPWM, "PWM", channelsPWM, namesPWM);
}
function createChannels(counter, channelType, channelParent, channelMapping){
if ( channelParent.children.length !== 0){
console.log("destroying");
for ( var j = channelParent.children.length; j > 0 ; j--){
channelParent.children[j-1].destroy();
}
}
for (var i=0;i<counter;i++){
var component = Qt.createComponent(channelType+".qml");
if( component.status !== Component.Ready )
{
if( component.status === Component.Error )
console.debug("Error:"+ component.errorString() );
return; // or maybe throw
}
var channels =component.createObject(channelParent, { "id": channelType+(i+1), "channelText.text": channelMapping[i]});
}
[EDIT 2]
Although the solution in EDIT 1 works and produces the right children
for my ScrollView
I don't think it is good enough and I believe the best implementation would be to bind the model's size change with the call to the createStrips(countPWM)
function. Something like:
\\main.qml
ConfiguredChannels{
id: configuredList
anchors{
left: parent.left
top: devices.bottom
right: tabs.left
bottom: parent.bottom
}
onModelChanged: tabs.createStrips(model.rowCount) //or an appropriate signal handler defined on C++ side
}
And perhaps even better, make the creation of the children
as a custom qml
signal handler that will be emitted every time the model's size is changed. (I tried the onModelChanged
as above but didn't work. Probably I am missing in which case is this signal emitted)
[SOLUTION]
I followed the instructions of the accepted answer as well as this link.
I added a Q_PROPERTY
to my model's definition inside the header file named rowCount
with the NOTIFY
rowCountChanged
as well as the signal void rowCountChanged();
. Also, inside the function setList(newList)
which I use to update the model, I added at the end of its implementation the emit rowCountChanged();
. Last I connected this signal with my function createStrips(count)
inside QML. Now every time the model's size is changed, my ScrollView
will update automatically the strips shown as the RowLayout
's children.
\\ChannelModel.h
...
Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged)
...
signals:
void rowCountChanged();
\\ChannelModel.cpp
void ChannelModel::setList(ChannelList *list)
{
beginResetModel();
...
endRestModel();
emit rowCountChanged();
}
\\main.qml
Connections {
target: configuredList.model
onRowCountChanged: tabs.createStrips(configuredList.model.rowCount)
}
Only the q-property allow a binding, in your case the Q_INVOKABLE
is not, so you will have to create it, for this we use the signal rowsInserted
and rowsRemoved
as shown below:
*.h
Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged)
public:
...
signals:
void rowCountChanged();
*.cpp
//constructor
connect(this, &QAbstractListModel::rowsInserted, this, &YourModel::rowCountChanged);
connect(this, &QAbstractListModel::rowsRemoved, this, &YourModel::rowCountChanged);
*.qml
countPWM: configuredList.model.rowCount // without ()
Note:
I'm assuming that when you add an element or remove it, you're using:
beginInsertRows(QModelIndex(), rowCount(), rowCount());
//append data
endInsertRows();
Or:
beginRemoveRows(QModelIndex(), from, to)
// remove
endRemoveRows();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With