model.rowCount() 不会绑定到 Item 的属性

Posted

技术标签:

【中文标题】model.rowCount() 不会绑定到 Item 的属性【英文标题】:model.rowCount() won't bind to Item's property 【发布时间】:2018-08-07 13:41:21 【问题描述】:

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

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

我还想将模型的大小连接到ScrollViewint 属性。此属性将定义RowLayoutchildren

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

仅供参考,模型的所有修改都遵循 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] 仔细观察后,我意识到在我当前的实现中,即使我正确绑定到模型的大小,我仍然无法按需创建所需数量的RowLayoutchildren(在我更改模型中的DialogConfiguration.qml)。

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

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

\\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] 尽管 EDIT 1 中的解决方案有效并为我的 ScrollView 生成了正确的 children,但我认为这还不够好,我相信最好的实现是将模型的大小更改与对 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,但没有用。可能我错过了在这种情况下发出的信号)

[解决方案]

我遵循了已接受答案的说明以及此link。

我在名为 rowCount 的头文件内的模型定义中添加了 Q_PROPERTYNOTIFY rowCountChanged 以及信号 void rowCountChanged(); 。此外,在我用来更新模型的函数 setList(newList) 中,我在其实现的末尾添加了 emit rowCountChanged(); 。最后,我将此信号与 QML 中的函数 createStrips(count) 连接起来。现在,每次更改模型的大小时,我的ScrollView 都会自动更新显示为RowLayout 的孩子的条带。

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

【问题讨论】:

您是否试图找出导致调试器崩溃的错误?应用程序崩溃的行是什么?什么是错误信息?请提供所有相关代码,包括型号。 糟糕,我忽略了问题中的崩溃部分。请提供您调用rowCount() 的模型的最小示例,如果这是导致您的应用程序崩溃的原因。绑定到Q_INVOKABLEs 不是真正的绑定,因为没有更改通知。 【参考方案1】:

只有 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();

【讨论】:

我听从了您的建议,但应用程序仍然崩溃。在 setList() 内部,我确实使用了您提到的信号,前面是 beginResetModel() ,后面是 endResetModel() 。我尝试将这些与rowCountChanged 连接起来,但它们受到保护。 @K.Tsakalis 不是你有选举,而是必须要做的事情,Qt 强制要求,beginXXX 和 endXXX 立即通知,它们不是信号,它们是函数。另一方面,rowCountChanged 是我创建的信号,它不是方法。 @K.Tsakalis rowsInserted 和 rowsRemoved 是信号 doc.qt.io/qt-5/qabstractitemmodel.html#rowsInserted doc.qt.io/qt-5/qabstractitemmodel.html#rowsRemoved @K.Tsakalis 要了解 beginXXX 和 endXXX 的使用,请检查以下内容:doc.qt.io/qt-5/model-view-programming.html#an-editable-model,我强调 beginXXX 和 endXXX 是强制性的 @K.Tsakalis 可能您的应用程序因错误的实现而崩溃,所以如果您不显示minimal reproducible example 帮助,那将是不可能的。【参考方案2】:

您无法绑定到 Q_INVOKABLE,因为与更改信号没有关联。

创建一个Q_PROPERTY(count READ rowCount NOTIFY rowCountChanged) 或类似的东西。确保在插入或删除行时发出信号rowCountChanged

当然,如果你有一个会更规范

 Q_PROPERTY(count READ count NOTIFY countChanged)

 int count()  return rowCount(); 

并确保发出countChanged

然后你可以绑定到属性count

【讨论】:

以上是关于model.rowCount() 不会绑定到 Item 的属性的主要内容,如果未能解决你的问题,请参考以下文章

Apache Zeppelin python到角度绑定不会一直发生,取消绑定会出错

table.dataChanged(index, index).connect(someFunction) 失败

将属性绑定到 Xamarin 中的 ListView,不会更新视图

2012 中的 ReportViewer 不会绑定到 SQL Server Compact 数据源

Kendo UI Grid 不会绑定到数据

IT之路