自定义listModel不通知视图

hus*_*hus 2 qt qml qtquick2

我有我的自定义列表模型,我放置了应该在QML视图上显示的数据.但由于某种原因,QML中的视图有时会正常更新,有时会使用以前的数据,有时不会执行更新.

这是我填充模型的函数 - 从其他一些线程调用此函数.

void MyScreen::fillListModel()
{
    const QString SEPARATOR = " ";   
    myListModel->resetModel();

    for (int i = 0; i < MAX_ROWS; ++i)
    {
        QString key = QString::fromUtf16(MyData::getParameterKey(i).c_str());
        QString val = QString::fromUtf16(MyData::getParameterVal(i).c_str());       
        myListModel->addItem(key + SEPARATOR + val);
    }
}
Run Code Online (Sandbox Code Playgroud)

模型重置的实现:

void BrowsingModelBase::resetModel()
{
    beginResetModel();
    m_items.clear();
    endResetModel();
}
Run Code Online (Sandbox Code Playgroud)

执行addItem():

void BrowsingModelBase::addItem(const BrowsingItemModelBase &item)
{
    int count = m_items.size();
    beginInsertRows(QModelIndex(), count, count);
    m_items.append(item);
    endInsertRows();
}
Run Code Online (Sandbox Code Playgroud)

最后我的QML文件:

MyScreen {

    Column {
        id: myFlowList
        y: 110
        x: 220

        ListView {
            height:1000
            spacing: 35;

            model: myListModelRoot.myListModel

            delegate: Text {
                text: text1
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

奇怪的是,循环后用线

myListModel->addItem(key + SEPARATOR + val);
Run Code Online (Sandbox Code Playgroud)

当我打印带有数据的日志时myListModel,会填充适当的数据,但视图通常会使用以前的数据进行更新.数据更改信号是否可能卡在某处?不知道解决方案是什么?

Rei*_*ica 5

假设您从另一个线程调用模型的方法,并且模型基本上不是线程安全的,您有两个选择:

  1. 使您的模型的某些方法线程安全,或

  2. 以线程安全的方式显式调用方法.

但首先,您可以通过一次性添加所有项目来获得一些性能.这样,模型将只为所有行发出一个信号,而不是每行发出一个信号.观点将非常感激.

class BrowsingModelBase {
  ...
};
Q_DECLARE_METATYPE(QList<BrowsingItemModelBase>)

void BrowsingModelBase::addItems(const QList<BrowsingItemModelBase> & items)
{
  beginInsertRows(QModelIndex(), m_items.size(), m_items.size() + items.size() - 1);
  m_items.append(items);
  endInsertRows();
}
Run Code Online (Sandbox Code Playgroud)

你可能也应该有一个名为clear而不是的方法resetModel,因为重置一个模型有一个更普遍的含义:"改变它以至于不值得发出个别的变化信号".要重置模型并不能意味着"明确它"!从而:

void BrowsingModelBase::clear()
{
  beginResetModel();
  m_items.clear();
  endResetModel();
}
Run Code Online (Sandbox Code Playgroud)

最后,按照安全调用模型方法的第二种方法,fillListModel如下所示.请参阅此答案以供讨论postTo.

template <typename F>
void postTo(QObject * obj, F && fun) {
  if (obj->thread() != QThread::currentThread()) {
    QObject signalSource;
    QObject::connect(&signalSource, &QObject::destroyed, obj, std::forward<F>(fun));
  } else 
    fun();
}

void MyScreen::fillListModel()
{
  auto separator = QStringLiteral(" ");
  QList<BrowserItemModelBase> items;
  for (int i = 0; i < MAX_ROWS; ++i) {
    auto key = QString::fromUtf16(MyData::getParameterKey(i).c_str());
    auto val = QString::fromUtf16(MyData::getParameterVal(i).c_str());
    items << BrowserItemModelBase(key + separator + val);
  }
  postTo(myListModel, [this, items]{ 
    myListModel->clear();
    myListModel->addItems(items);
  });
}
Run Code Online (Sandbox Code Playgroud)

或者,按照第一种方法,您可以使clearaddItems方法线程安全:

/// This method is thread-safe.
void BrowsingModelBase::addItems(const QList<BrowsingItemModelBase> & items)
{
  postTo(this, [this, items]{
    beginInsertRows(QModelIndex(), m_items.size(), m_items.size() + items.size() - 1);
    m_items.append(items);
    endInsertRows();
  });
}

/// This method is thread-safe.
void BrowsingModelBase::clear()
{
  postTo(this, [this]{
    beginResetModel();
    m_items.clear();
    endResetModel();
  });
}
Run Code Online (Sandbox Code Playgroud)

然后您不需要更改fillListModel,除了使其使用addItems:

void MyScreen::fillListModel()
{
  auto separator = QStringLiteral(" ");
  myListModel->clear();
  QList<BrowserItemModelBase> items;
  for (int i = 0; i < MAX_ROWS; ++i) {
    auto key = QString::fromUtf16(MyData::getParameterKey(i).c_str());
    auto val = QString::fromUtf16(MyData::getParameterVal(i).c_str());
    items << BrowserItemModelBase(key + separator + val);
  }
  myListModel->addItems(items);
}
Run Code Online (Sandbox Code Playgroud)