自定义listModel不会通知视图

时间:2015-08-18 21:29:31

标签: 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);
    }
}

模型重置的实现:

void BrowsingModelBase::resetModel()
{
    beginResetModel();
    m_items.clear();
    endResetModel();
}

addItem()的实施:

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

最后我的QML文件:

MyScreen {

    Column {
        id: myFlowList
        y: 110
        x: 220

        ListView {
            height:1000
            spacing: 35;

            model: myListModelRoot.myListModel

            delegate: Text {
                text: text1
            }
        }
    }
}

奇怪的是循环后用行

myListModel->addItem(key + SEPARATOR + val);

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

2 个答案:

答案 0 :(得分:5)

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

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

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

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

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

    你可能也应该有一个名为clear而不是resetModel的方法,因为重置一个模型有一个更普遍的含义:&#34;改变它以至于它是如此不值得发出个别变化信号&#34;。要重置模型意味着&#34;清除它&#34;!因此:

    void BrowsingModelBase::clear()
    {
      beginResetModel();
      m_items.clear();
      endResetModel();
    }
    

    最后,按照安全调用模型方法的第二种方法,fillListModel变为如下。请参阅this answer for discussion of 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);
      });
    }
    

    或者,按照第一种方法,您可以使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();
      });
    }
    

    您不需要对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);
    }
    

答案 1 :(得分:0)

问题很可能是你不是从主GUI线程调用fillListModel()方法。您可以从其他线程更新模型,但必须在主GUI线程中调用beginResetModel();endResetModel();beginInsertRows(QModelIndex(), count, count); ...方法。

在GUI线程中调用这些方法的一种方法(可能不是最有效的)是:

  1. 为您要调用的每种方法创建信号:
  2.   signals:
        //these signals are emitted from worker thread
        void requestBeginResetModel();
        void requestEndResetModel();
    
    1. 创建实际调用方法的插槽:
    2.   private slots:
          //these slots execute the model reset operations in main thread
          void callBeginResetModel();
          void callEndResetModel();
      
      1. 连接信号和插槽:
      2.   //connect the appropriate signals
          connect(this, SIGNAL(requestBeginResetModel()),
                  this, SLOT(callBeginResetModel()));
          connect(this, SIGNAL(requestEndResetModel()),
                  this, SLOT(callEndResetModel()));
        
        1. 您的重置模型将是:
        2. void BrowsingModelBase::resetModel()
          {
              emit requestBeginResetModel();
              m_items.clear();
              emit requestEndResetModel();
          }
          
          1. 最后,这些插槽实现为:
          2. void ObjectModel::callBeginResetModel()
            {
              beginResetModel();
            }
            
            void ObjectModel::callEndResetModel()
            {
              endResetModel();
            }
            

            请注意,您也必须对行插入方法执行相同操作。或者,您可以在发射信号之间用resetModel()方法填充模型。