model.rowCount() 不会绑定到项目的 属性
model.rowCount() won't bind to Item's property
我有一个 ListView
,它在 Qml 中使用名为 rootModel
的自定义 C++ 模型进行了初始化。
该模型继承 QAbstractListModel
并定义一个 QVector<customType>
作为私有成员来填充模型。
在我的 ApplicationWindow
中,我创建了一个 Dialog
,我在其中更改模型并调用 setList()
函数来更新它。这很好用。
我还想将模型的尺寸连接到 ScrollView
的 int
属性。 属性 将定义 RowLayout
的 children
。
问题是,当我尝试将此 属性 绑定到模型的大小时,应用程序崩溃了。
仅供参考,模型的所有修改都遵循 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
的 children
(在我更改之后Dialog
Configuration.qml
中的模型)。
那是因为我已经将它们的创建放在 RowLayout
的 Component.onCompleted
处理程序中。一旦 Configuration.qml
首次在 main.qml
中初始化,将执行此处理程序的内容。之后,对 countPWM
的任何其他更改都不会产生影响,因为组件已经完成!如果我在这一点上有误,请纠正我。
基于此,我遵循了另一种实现方式。我创建了一个 createChannels
的 "wrapper" 函数,命名为 createStrips(countPWM)
。这样,要正确更新 RowLayout
的 children
,我必须调用此函数。
\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_PROPERTY
到我的模型定义中,其中包含 NOTIFY
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)
}
您无法绑定到 Q_INVOKABLE
,因为与更改信号没有关联。
创建一个 Q_PROPERTY(count READ rowCount NOTIFY rowCountChanged)
或类似的东西。确保在插入或删除行时发出信号 rowCountChanged
。
当然如果你有
会更规范
Q_PROPERTY(count READ count NOTIFY countChanged)
int count() { return rowCount(); }
并确保发出 countChanged
.
然后就可以绑定到属性count
.
只有 q-属性 允许绑定,在您的情况下 Q_INVOKABLE
不允许,因此您必须创建它,为此我们使用信号 rowsInserted
和rowsRemoved
如下图:
*.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();
我有一个 ListView
,它在 Qml 中使用名为 rootModel
的自定义 C++ 模型进行了初始化。
该模型继承 QAbstractListModel
并定义一个 QVector<customType>
作为私有成员来填充模型。
在我的 ApplicationWindow
中,我创建了一个 Dialog
,我在其中更改模型并调用 setList()
函数来更新它。这很好用。
我还想将模型的尺寸连接到 ScrollView
的 int
属性。 属性 将定义 RowLayout
的 children
。
问题是,当我尝试将此 属性 绑定到模型的大小时,应用程序崩溃了。
仅供参考,模型的所有修改都遵循 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
的 children
(在我更改之后Dialog
Configuration.qml
中的模型)。
那是因为我已经将它们的创建放在 RowLayout
的 Component.onCompleted
处理程序中。一旦 Configuration.qml
首次在 main.qml
中初始化,将执行此处理程序的内容。之后,对 countPWM
的任何其他更改都不会产生影响,因为组件已经完成!如果我在这一点上有误,请纠正我。
基于此,我遵循了另一种实现方式。我创建了一个 createChannels
的 "wrapper" 函数,命名为 createStrips(countPWM)
。这样,要正确更新 RowLayout
的 children
,我必须调用此函数。
\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_PROPERTY
到我的模型定义中,其中包含 NOTIFY
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)
}
您无法绑定到 Q_INVOKABLE
,因为与更改信号没有关联。
创建一个 Q_PROPERTY(count READ rowCount NOTIFY rowCountChanged)
或类似的东西。确保在插入或删除行时发出信号 rowCountChanged
。
当然如果你有
会更规范 Q_PROPERTY(count READ count NOTIFY countChanged)
int count() { return rowCount(); }
并确保发出 countChanged
.
然后就可以绑定到属性count
.
只有 q-属性 允许绑定,在您的情况下 Q_INVOKABLE
不允许,因此您必须创建它,为此我们使用信号 rowsInserted
和rowsRemoved
如下图:
*.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();