c++qtqt5qlistviewqstringlistmodel

QListView flushes slowly when there are three QListViews in a QDialog


I customized my data model AnnotationListModel inherited from QStringListModel.

The purpose of AnnotationListModel class, is to add checkboxes for data items and achieve exclusion between data items.

Now, I create three QListView and AnnotationListModel objects in a QDialog. The data item in the model is added as a checkbox, and is exclusive.

I can check the checkbox in QListView, but the checked status can't display quickly.

How can I enable QListView to flush quickly?

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

Create three AnnotationListModel objects:

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

The Dialog is opened through mainwindow.

enter image description here


Solution

  • IMO, you have made 4 mistakes but the important ones are the first 2:

    The below AnnotationProxyModel implements exclusive checkboxes for any model you want to implement. It derives QIdentityProxyModel and remember the checked index thanks to a QPersistentModelIndex (which unlike QModelIndex is OK to save).
    I let you check if you really need to keep the Q_OBJECT macro and your signal(s) in the below definition and if yes, add them back yourself.

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

    Like any other proxy model, it is used by calling setSourceModel(...) with your QStringListModel, i.e. in that fashion:

    AnnotationProxyModel* checkModel = new AnnotationProxyModel(view);
    checkModel->setSourceModel(countryModel);
    view->setModel(checkModel);