c++ 使QSortFilterProxyModel过滤器无效会在选择项目时导致崩溃

5jvtdoz2  于 2021-03-05  发布在  其他
关注(0)|答案(1)|浏览(188)

我尝试在Qt 5.15.3中为一个游戏文件系统制作一个文件浏览器,左边是目录树视图,右边是文件表视图,当在树中选中一个目录时,表中应该显示该目录的子目录,但只有文件,没有其他嵌套目录。
我已经实现了两个QSortFilterProxyModels,每个视图一个,对于树视图,代理模型的filterAcceptsRow()实现只是检查模型索引是否代表一个目录,但是表视图的代理模型要稍微复杂一些,因为当选择不同的目录时,表使用的活动根索引会发生变化:

void FileSystemBrowserWidget::onDirectoryActivated(const QModelIndex& index)
{
  // Since the proxy model filters based on the source model, we get
  // the source index to use for filtering. The index we have
  // been passed is from the tree view's proxy model.
  const QModelIndex sourceIndex = m_treeProxyModel->mapToSource(index);

  // Tell the table's proxy model about this new root.
  m_tableProxyModel->setRootForFiltering(sourceIndex);

  // Set the table view's root to display from.
  // The table view shows the proxy model, so we map to this model.
  m_fileSystemTableView->setRootIndex(m_tableProxyModel->mapFromSource(sourceIndex));
}

表代理模型的setRootForFiltering()函数存储这个新的根,并使过滤器无效,我发现我必须这样做,因为如果表视图的根索引改变了,过滤器需要重新评估,这是因为通常目录不会通过过滤器显示在表中,所以如果它被选为表视图的根,该过滤将分层地应用,并且将不显示目录的子文件。

void FileSystemBrowserTableProxyModel::setRootForFiltering(const QModelIndex& sourceIndex)
{
  if (m_rootForFiltering == sourceIndex)
  {
    return;
  }

  m_rootForFiltering = sourceIndex;
  invalidateFilter();
}

为了解决过滤问题,表代理模型的filterAcceptsRow()函数允许与已设置的根索引匹配的项:

bool FileSystemBrowserTableProxyModel::filterAcceptsRow(
  int sourceRow,
  const QModelIndex& sourceParent) const
{
  QAbstractItemModel* src = sourceModel();
  const QModelIndex srcIndex = src->index(sourceRow, 0, sourceParent);

  if (srcIndex == m_rootForFiltering)
  {
    // Always allowed, or children don't show up.
    return true;
  }

  // Allow if this index represents a file and not a directory.
  return itemAtIndexIsAFile(srcIndex);
}

这似乎允许过滤正常工作。然而,如果我在表格视图中选择一个项目,然后在树视图中选择一个不同的目录,会出现一个奇怪的问题。QSortFilterProxyModel: index from wrong model passed to mapFromSource被垃圾邮件发送到我的终端9次,然后我得到如下崩溃:

___lldb_unnamed_symbol11407 (@___lldb_unnamed_symbol11407:41)
QSortFilterProxyModel::flags(QModelIndex const&) const (@QSortFilterProxyModel::flags(QModelIndex const&) const:43)
QAbstractItemView::focusInEvent(QFocusEvent*) (@QAbstractItemView::focusInEvent(QFocusEvent*):41)
QWidget::event(QEvent*) (@QWidget::event(QEvent*):763)
QFrame::event(QEvent*) (@QFrame::event(QEvent*):14)
QApplicationPrivate::notify_helper(QObject*, QEvent*) (@QApplicationPrivate::notify_helper(QObject*, QEvent*):42)

... call stack continues ...

我已经检查了代码,关于索引来自错误模型的错误似乎不是由我自己进行的调用触发的。我只能假设我在某种程度上误用了代理模型,但我不确定具体是如何误用的。我没有向模型添加或删除项,因此底层数据应该不会更改。
如果我删除了对invalidateFilter()的调用,这个问题就不会发生。还应该注意的是,我不能简单地打开recursiveFilteringEnabled:这意味着任何可见的子目录也将使其父目录可见,这将导致任何包含文件的目录通过过滤器并显示在表格视图中.如果它有用,我正在Kubuntu上开发和测试这个实现.

taor4pac

taor4pac1#

经过进一步的研究,我想我已经解决了这个问题。如果一个嵌套目录显示在表视图中,那么链中父目录的所有模型索引也需要通过过滤器,否则什么都不会显示。我不太清楚确切的问题是什么,但我怀疑表视图期望一定数量的索引,但由于我没有满足前面提到的规则,它无法获得这些索引。在此状态下与视图交互会导致某种未定义的行为。
在对上述规则执行检查之后,似乎又可以正常工作了。

相关问题