首页 > 解决方案 > model.rowCount() 不会绑定到 Item 的属性

问题描述

我有一个用Qml 中ListView命名的自定义 C++ 模型初始化的。rootModel模型继承QAbstractListModel并将 a 定义QVector<customType>为私有成员以填充模型。

在我的中,我ApplicationWindow创建了一个Dialog我更改模型并调用setList()函数来更新它的函数。这工作正常。

我还想将模型的大小连接到 aScrollViewint属性。该属性将定义childrena 的RowLayout

问题是当我尝试将此属性绑定到模型的大小时,应用程序会崩溃。

仅供参考,模型的所有修改都遵循 Qt 的规则。rowCount()Q_INVOKABLE。我也尝试过使用onModelChanged处理程序,但这不起作用(我在文档中检查了这个信号是在发出时modelReset()发出的,这是setList()通过内部发生的endResetModel()

我相信这是一个简单的过程(在我的项目中已经多次执行属性绑定)但没有按预期工作。

我引用了我项目的一些示例代码。

//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);
                }
            }
}

[编辑 1] 仔细观察后,我意识到在我当前的实现中,即使我正确绑定到模型的大小,我仍然无法按需创建所需数量的RowLayout' (在我更改 ' 中的模型之后)。childrenDialog Configuration.qml

那是因为我把他们的创作放在了RowLayout'sComponent.onCompleted处理程序中。Configuration.qml一旦第一次在里面初始化,这个处理程序的内容就会被执行main.qml。之后,对 的任何其他更改countPWM都不会产生影响,因为组件已经完成!如果我在这一点上错了,请纠正我。

基于此,我遵循了另一个实现。我创建了一个createChannels名为的“包装器”函数createStrips(countPWM)。这样,要正确更新RowLayout'schildren我必须调用此函数。

\\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]});
    }

[编辑 2]children虽然 EDIT 1 中的解决方案有效并为我提供 了正确的解决方案,但ScrollView我认为这还不够好,我相信最好的实现是将模型的大小更改与对createStrips(countPWM)函数的调用绑定。就像是:

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

也许更好的是,将创建的children自定义qml信号处理程序创建为每次更改模型大小时都会发出的信号处理程序。(我尝试了onModelChanged上述方法但没有用。可能我错过了在这种情况下发出的信号)

[解决方案]

我按照已接受答案的说明以及此链接进行操作。

我在以 the和 signal命名的头文件中添加了一个Q_PROPERTY到我的模型定义中。此外,在我用来更新模型的函数中,我在其实现的最后添加了. 最后,我将此信号与我在 QML 中的函数连接起来。现在每次更改模型的大小时,我都会自动更新显示为 的子项的条带。rowCountNOTIFY rowCountChangedvoid rowCountChanged();setList(newList)emit rowCountChanged();createStrips(count)ScrollViewRowLayout

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

标签: qtqmlscrollviewqtquickcontrols2

解决方案


只有 q-property 允许绑定,在您的情况下不允许绑定,Q_INVOKABLE因此您必须创建它,为此我们使用信号rowsInsertedrowsRemoved如下所示:

*。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 ()

笔记:

我假设当您添加或删除元素时,您正在使用:

beginInsertRows(QModelIndex(), rowCount(), rowCount());
//append data
endInsertRows();

或者:

beginRemoveRows(QModelIndex(), from, to)
// remove
endRemoveRows();

推荐阅读