当 QDialog 中有 3 个 QListView 时,QListView 刷新缓慢

问题描述 投票:0回答:1

我自定义了我的数据模型

AnnotationListModel
继承自QStringListModel。

AnnotationListModel
类的目的,是为数据项添加复选框,并实现数据项之间的排除。

现在,我在 QDialog 中创建三个 QListView 和

AnnotationListModel
对象。模型中的数据项作为复选框添加,并且是独占的。

我可以在QListView中勾选复选框,但是勾选状态无法快速显示。

如何让QListView快速刷新?

class AnnotationListModel :public QStringListModel
{
    Q_OBJECT
public:
    AnnotationListModel(AnnotationType annType, const QStringList& stringList, QObject* parent = nullptr);
    ~AnnotationListModel() {};

protected:
    Qt::ItemFlags flags(const QModelIndex& index) const override;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
    bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;

signals:
    void sendAnnotation(const AnnotationType& annoType, const QString& annotation);

private:
    AnnotationType m_annoType;
    QStringList m_stringList;
    QMap<QString, bool> m_stringList_map; //<annotationName,checked>
};

AnnotationListModel::AnnotationListModel(AnnotationType annType, const QStringList& stringList, QObject* parent)
    :QStringListModel(parent), m_stringList(stringList), m_annoType(annType)
{
    setStringList(m_stringList);
    for (auto& value : m_stringList)
    {
        m_stringList_map[value] = false;
    }
}

Qt::ItemFlags AnnotationListModel::flags(const QModelIndex& index) const
{
    if (!index.isValid())
    {
        return Qt::ItemIsEnabled;
    }
    return Qt::ItemIsUserCheckable | QStringListModel::flags(index);
}

QVariant AnnotationListModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid())
    {
        return QVariant();
    }

    if (role == Qt::CheckStateRole)
    {
        if (m_stringList_map[m_stringList.at(index.row())] == true)
        {
            return Qt::Checked;
        }
        else
        {
            return Qt::Unchecked;
        }
    }
    else if (role == Qt::DisplayRole)
    {
        return m_stringList.at(index.row());
    }

    return QStringListModel::data(index, role);
}

bool AnnotationListModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (!index.isValid())
    {
        return false;
    }

    if (role == Qt::CheckStateRole)
    {
        if (value == Qt::Checked)
        {
            for (int i{ 0 }; i < m_stringList.size(); ++i)
            {
                if (i != index.row())
                {
                    m_stringList_map[m_stringList.at(i)] = false;
                }
                else
                {
                    m_stringList_map[m_stringList.at(i)] = true;
                    sendAnnotation(m_annoType, m_stringList.at(i));
                }
            }
        }
        else if (value == Qt::Unchecked)
        {
            m_stringList_map[m_stringList.at(index.row())] = false;
        }
    }

    return QStringListModel::setData(index, value, role);
}

创建三个

AnnotationListModel
对象:

switch (itr_kind.key())
{
case AnnotationType::CounCode:
    m_counCodeModel = new AnnotationListModel(AnnotationType::CounCode, l_annoStrList);
    m_uiDialog->listView_country->setModel(m_counCodeModel);
    connect(m_counCodeModel, &AnnotationListModel::sendAnnotation, this, &AnnotationDialog::sendAnnotation);
    break;

case AnnotationType::DriSide:
    m_driSideModel = new AnnotationListModel(AnnotationType::DriSide, l_annoStrList);
    m_uiDialog->listView_driving->setModel(m_driSideModel);
    connect(m_driSideModel, &AnnotationListModel::sendAnnotation, this, &AnnotationDialog::sendAnnotation);
    break;

case AnnotationType::RoadType:
    m_roadTypeModel = new AnnotationListModel(AnnotationType::RoadType, l_annoStrList);
    m_uiDialog->listView_road->setModel(m_roadTypeModel);
    connect(m_roadTypeModel, &AnnotationListModel::sendAnnotation, this, &AnnotationDialog::sendAnnotation);
}

通过

mainwindow
打开对话框。

c++ qt qt5 qlistview qstringlistmodel
1个回答
0
投票

IMO,你犯了 4 个错误,但重要的是前 2 个:

  • 向现有模型添加功能的正确方法(在您的情况下是
    QStringListModel
    )是使用代理模型(
    QAbstractProxyModel
    的子类)。它的工作原理同样好,但更容易实现,而且更灵活,因为它可以与任何模型类型一起使用。
  • 由于您希望复选框是独占的,因此您只需要记住选中的复选框,无需为此拥有整个
    QMap<...>
    容器。
    我相信这是浪费了大部分计算时间的地方在你的代码中,特别是在调试模式下测试对话框时(容器有很多很多检查,这些检查仅在调试中完成,这使得它们的使用速度比发布时慢)。
    至少,不保存检查的索引是正确的作为
    QModelIndex
  • 您忘记从
    dataChanged
    方法发出
    setData
    信号。
  • 您忘记从
    false
    方法返回
    true
    /
    setData
    。准确地说,确实返回了一个布尔值,但只是由
    QStringListModel::setData(...)
    返回,这不是你想要做的。

下面的

AnnotationProxyModel
为您想要实现的任何模型实现了专属复选框。它派生
QIdentityProxyModel
并通过
QPersistentModelIndex
记住选中的索引(与
QModelIndex
不同,可以保存)。
我让您检查是否确实需要在下面的定义中保留
Q_OBJECT
宏和信号,如果是,请自行将它们添加回来。

#include <QtCore/QIdentityProxyModel>
class AnnotationProxyModel : public QIdentityProxyModel {
public:
    AnnotationProxyModel(QObject* parent = nullptr);
    QVariant data(const QModelIndex& index, int role) const override;
    Qt::ItemFlags flags(const QModelIndex& index) const override;
    bool setData(const QModelIndex& index, const QVariant& value, int role) override;
private:
    QPersistentModelIndex checkedIndex;
};

AnnotationProxyModel::AnnotationProxyModel(QObject* parent) : 
    QIdentityProxyModel(parent),
    checkedIndex()
{
}

QVariant AnnotationProxyModel::data(const QModelIndex& index, int role) const
{
    switch (role) {
    case Qt::CheckStateRole: return QVariant((checkedIndex == index) ? Qt::Checked : Qt::Unchecked);
    default: return QIdentityProxyModel::data(index, role);
    }
}

Qt::ItemFlags AnnotationProxyModel::flags(const QModelIndex& index) const
{
    return QIdentityProxyModel::flags(index) | Qt::ItemIsUserCheckable;
}

bool AnnotationProxyModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    switch (role) {
    case Qt::CheckStateRole: {
        if (checkedIndex == index) {
            if (value.value<Qt::CheckState>() != Qt::Unchecked) // Trying to check the already checked index -> return false
                return false;
            else {
                checkedIndex = QPersistentModelIndex(); // Unchecking the already checked index -> return true;
                emit dataChanged(index, index, { Qt::CheckStateRole });
                return true;
            }
        }
        else {
            if (value.value<Qt::CheckState>() == Qt::Unchecked) // Trying to uncheck an index that is already unchecked -> return false
                return false;
            else {
                QModelIndex uncheckedIndex = checkedIndex;
                checkedIndex = QPersistentModelIndex(index);
                emit dataChanged(uncheckedIndex, uncheckedIndex, { Qt::CheckStateRole });
                emit dataChanged(index, index, { Qt::CheckStateRole });
                return true;
            }
        }
    }
    default: return QIdentityProxyModel::setData(index, value, role);
    }
}

与任何其他代理模型一样,它是通过用您的

setSourceModel(...)
调用
QStringListModel
来使用的,即以这种方式:

AnnotationProxyModel* checkModel = new AnnotationProxyModel(view);
checkModel->setSourceModel(countryModel);
view->setModel(checkModel);
© www.soinside.com 2019 - 2024. All rights reserved.