【问题标题】:Finding / removing a row from a QStandardItemModel by item data按项目数据从 QStandardItemModel 中查找/删除一行
【发布时间】:2019-06-30 21:24:56
【问题描述】:

我有一个QStandardItemModel,只有一列(代表一个列表)。列表中的每个项目都有一个唯一的整数 ID,该 ID 存储为 QStandardItem 的数据(通过 QStandardItem::setData 我猜默认是 Qt::UserRole+1)。

给定其中一个 ID,我想从模型中查找并删除相应的行。现在我正在这样做:

void NetworkManager::removeSessionFromModel (QStandardItemModel *model, int sessionId) {

    foreach (const QStandardItem *item, model->findItems("*", Qt::MatchWildcard)) {
        if (item->data() == sessionId) {
            model->removeRow(item->index().row());
            break;
        }
    }

}

它运行良好,但该函数的每一行都让我畏缩。有没有更清洁的方法来做到这一点?

【问题讨论】:

标签: c++ qt5 qstandarditemmodel qstandarditem


【解决方案1】:

直接遍历QStandardItemModel怎么样?像这样的:

void NetworkManager::removeSessionFromModel (QStandardItemModel *model, int sessionId) 
{
    for (int i = 0; i < model->rowCount(); ++i)
    {
        if (model->item(i)->data() == sessionId)
        {
            model->removeRow(i);
            break;
        }
    } 
}

不确定QStandardItemModel 在随机访问中的表现如何,也许你的方法更有效。

编辑:

其实有一个函数可以做你想做的:QAbstractItemModel::match

它返回一个QModelIndexList,其中包含在给定角色中具有匹配数据的所有条目。

void NetworkManager::removeSessionFromModel (QStandardItemModel *model, int sessionId)
{
    QModelIndexList list = model->match(model->index(0, 0), Qt::UserRole + 1, sessionId);

    if (!list.empty())
        model->removeRow(list .first().row());
}

将数据设置为特定角色可以如下完成:

model->setData(model->index(row, col), QVariant(13), Qt::UserRole + 1);

【讨论】:

  • OP 可能使用Qt::DisplayRole 作为其sessionId 值,否则findItems 将不起作用,因为实现在内部使用该角色调用match
  • 现在我想到了 QStandardItem::data() 默认返回 Qt::UserRole + 1 的值,并且 OP 与之匹配,而不是 Qt::DisplayRole,这可能是 OP 以这种方式使用 findItems 的原因确实如此。
  • @SilvanoCerza 我确实在Qt::UserRole+1上匹配;我使用findItems("*", ...) 只是为了获取我可以迭代的项目列表,并且没有使用findItems 进行搜索,因为它使用DisplayRole。但实际上直到我看到你的回答我才意识到match 的存在。
  • @RickPat 对不起,伙计,我可能正在打乱答案复选标记。这个问答是一个艰难的问答,因为我觉得到目前为止每个人都是赢家,呵呵。
  • 我和match一起去了;我选择了另一个答案,因为我发现在这一点上更清楚。也就是说,尽管match 是最直接的代码,但我发现您对int 索引的建议是最清晰易读的。所以这是另一个不错的选择。
【解决方案2】:

您需要从您的项目 ID 中获取行索引。

更有效的方法是使用 QMap,其中行索引作为值,项目 ID 作为键。

在这种情况下,您还需要在每次添加/删除行时维护映射值。

如果您的列表中没有 300 万个项目,请保持简单并使用您的代码。 通过优化此代码,您可能还会增加复杂性并降低可维护性,您得到的是 0.05 毫秒而不是 0.06 毫秒。

在 GUI 代码中,我经常有这样的代码:它很简单,每个人都立即得到它并且它完成了工作。它也足够快。

【讨论】:

  • 尽管这不是一个直接的答案,但这是最好的建议。读完之后,我对我的代码非常满意,而且当时的变化只是学术性的。
  • 如果你有很多项目,最好使用QHash,我建议你阅读QMap和QHash之间的this comparison
【解决方案3】:

您使用findItems 错误,它已经可以通过传递您正在搜索的值返回您想要的项目。如果您像现在一样调用它,那么您至少要遍历您的项目两次,因为findItems 必须遍历所有项目以找到与您的模式匹配的项目,在您的情况下,所有项目都匹配,那么您再次迭代返回的项目以找到sessionId

void NetworkManager::removeSessionFromModel (QStandardItemModel *model, int sessionId) {

    auto items = model->findItems(QString::number(sessionId));
    if (!items.empty()) {
        auto row = items.first()->index().row();
        model->removeRow(row);
    }
}

您也可以使用match 方法,因为findItems 在内部使用该方法,因此您避免分配StandardItem 只是为了获取其索引。同样match 在匹配模式的项目数之后立即返回,在这种情况下,sessionId 的值被发现,因此它并不总是迭代所有项目;这样更有效率。显然,如果在迭代所有项目后未找到该值,它将返回一个空列表。

auto start = model->index(0, 0);
auto indexes = model->match(start, Qt::UserRole + 1, QString::number(sessionId), 1, Qt::MatchExactly);
if (!indexes.empty()) {
    auto row = indexes.first().row();
    model->removeRow(row);
}

【讨论】:

  • 嘿好主意; findItems 直接在我的情况下实际上不起作用,因为它 calls match with Qt::DisplayRole 但我认为建议使用 matchQt::UserRole+1 是要走的路。我今天会试一试。
  • 像魅力一样工作。 foreach (auto index, model-&gt;match(model-&gt;index(0, 0), Qt::UserRole + 1, sessionId, 1, Qt::MatchExactly)) model-&gt;removeRow(index.row()); 如果你想简洁的话。只要 hits 设置为 1,在循环期间索引就不会从您下方更改。此外,由于匹配参数是 QVariant,我跳过了 QString::number
猜你喜欢
  • 2014-06-09
  • 2013-06-02
  • 1970-01-01
  • 2012-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多