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
.
IMO, you have made 4 mistakes but the important ones are the first 2:
QStringListModel
in your case) is with a proxy model (a subclass of QAbstractProxyModel
). It works just as well but is easier to implement and way more flexible since it can be used with any model type.QMap<...>
container for that.QModelIndex
.dataChanged
signals from your setData
method.false
/true
from your setData
method. To be precise, a boolean is indeed returned but only by QStringListModel::setData(...)
, which is not what you want to do.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);