Add a way for QgsSingleItemModel to have multiple column items

This commit is contained in:
Nyall Dawson 2022-05-13 14:06:06 +10:00
parent 1f63112e6d
commit f5f552aa61
5 changed files with 206 additions and 9 deletions

View File

@ -22,13 +22,26 @@ A :py:class:`QgsSingleItemModel` subclass which contains a single read-only item
%End
public:
explicit QgsSingleItemModel( QObject *parent /TransferThis/ = 0, const QString &text = QString(), const QMap< int, QVariant > &data = QMap< int, QVariant >(), Qt::ItemFlags flags = Qt::NoItemFlags );
explicit QgsSingleItemModel( QObject *parent /TransferThis/ = 0, const QString &text = QString(),
const QMap< int, QVariant > &data = QMap< int, QVariant >(), Qt::ItemFlags flags = Qt::NoItemFlags );
%Docstring
Constructor for QgsSingleItemModel with the specified ``parent`` object and display ``text``.
The ``data`` map specifies the data which should be returned for the specified roles, where
map keys are Qt item roles.
Custom ``flags`` can also be specified for the item.
%End
explicit QgsSingleItemModel( QObject *parent /TransferThis/,
const QList< QMap< int, QVariant > > &columnData,
Qt::ItemFlags flags = Qt::NoItemFlags );
%Docstring
Constructor for a multi-column QgsSingleItemModel with the specified ``parent`` object.
The ``columnData`` list specifies the data which should be returned for the specified roles for each column in the model, where
each entry in the list must be a QMap of Qt item role to value.
Custom ``flags`` can also be specified for the item.
%End

View File

@ -765,6 +765,134 @@ template<TYPE>
%End
};
template<TYPE>
%MappedType QList<QMap<int, TYPE>>
{
%TypeHeaderCode
#include <qmap.h>
#include <qlist.h>
%End
%ConvertFromTypeCode
// Create the list
PyObject *l = PyList_New(sipCpp->size());
if (!l)
return NULL;
// Set the list elements.
QList<QMap<int, TYPE>>::const_iterator it = sipCpp->constBegin();
int i = 0;
while (it != sipCpp->constEnd())
{
// Create the dictionary.
PyObject *d = PyDict_New();
if (!d)
{
Py_DECREF(l);
return NULL;
}
QMap<int, TYPE>::const_iterator mapIt = it->constBegin();
while (mapIt != it->constEnd())
{
TYPE *t = new TYPE(mapIt.value());
PyObject *kobj = PyLong_FromLong(mapIt.key());
PyObject *tobj = sipConvertFromNewType(t, sipType_TYPE, sipTransferObj);
if (kobj == NULL || tobj == NULL || PyDict_SetItem(d, kobj, tobj) < 0)
{
Py_DECREF(d);
if (kobj)
{
Py_DECREF(kobj);
}
if (tobj)
{
Py_DECREF(tobj);
}
else
{
delete t;
}
Py_DECREF(l);
return NULL;
}
Py_DECREF(kobj);
Py_DECREF(tobj);
++mapIt;
}
PyList_SET_ITEM(l, i, d);
++i;
++it;
}
return l;
%End
%ConvertToTypeCode
PyObject *kobj, *tobj;
SIP_SSIZE_T i = 0;
// Check the type if that is all that is required.
if (sipIsErr == NULL)
{
if (!PyList_Check(sipPy))
return 0;
for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i)
{
if (!PyDict_Check(PyList_GET_ITEM(sipPy, i)))
return 0;
Py_ssize_t j = 0;
while (PyDict_Next(PyList_GET_ITEM(sipPy, i), &j, &kobj, &tobj))
if (!sipCanConvertToType(tobj, sipType_TYPE, SIP_NOT_NONE))
return 0;
}
return 1;
}
QList< QMap<int, TYPE> >* ql = new QList< QMap<int, TYPE> >;
for (int i = 0; i < PyList_GET_SIZE(sipPy); ++i)
{
Py_ssize_t j = 0;
QMap<int, TYPE> qm;
while (PyDict_Next(PyList_GET_ITEM(sipPy, i), &j, &kobj, &tobj))
{
int state;
int k = PyLong_AsLong(kobj);
TYPE *t = reinterpret_cast<TYPE *>(sipConvertToType(tobj, sipType_TYPE, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr));
if (*sipIsErr)
{
sipReleaseType(t, sipType_TYPE, state);
delete ql;
return 0;
}
qm.insert(k, *t);
sipReleaseType(t, sipType_TYPE, state);
}
ql->append( qm );
}
*sipCppPtr = ql;
return sipGetState(sipTransferObj);
%End
};
%MappedType QMap<QString, int>
{

View File

@ -25,21 +25,38 @@ QgsSingleItemModel::QgsSingleItemModel( QObject *parent, const QString &text, co
}
QgsSingleItemModel::QgsSingleItemModel( QObject *parent, const QList<QMap<int, QVariant> > &columnData, Qt::ItemFlags flags )
: QAbstractItemModel( parent )
, mColumnData( columnData )
, mFlags( flags )
{
}
QVariant QgsSingleItemModel::data( const QModelIndex &index, int role ) const
{
if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) )
return QVariant();
switch ( role )
if ( index.column() < 0 || index.column() >= columnCount( QModelIndex() ) )
return QVariant();
if ( !mColumnData.isEmpty() )
{
case Qt::DisplayRole:
return mText;
return mColumnData.value( index.column() ).value( role );
}
else
{
switch ( role )
{
case Qt::DisplayRole:
return mText;
case Qt::ToolTipRole:
return mData.value( Qt::ToolTipRole, mText );
case Qt::ToolTipRole:
return mData.value( Qt::ToolTipRole, mText );
default:
return mData.value( role );
default:
return mData.value( role );
}
}
}
@ -84,5 +101,8 @@ int QgsSingleItemModel::rowCount( const QModelIndex &parent ) const
int QgsSingleItemModel::columnCount( const QModelIndex & ) const
{
if ( !mColumnData.empty() )
return mColumnData.size();
return 1;
}

View File

@ -42,7 +42,20 @@ class CORE_EXPORT QgsSingleItemModel: public QAbstractItemModel
*
* Custom \a flags can also be specified for the item.
*/
explicit QgsSingleItemModel( QObject *parent SIP_TRANSFERTHIS = nullptr, const QString &text = QString(), const QMap< int, QVariant > &data = QMap< int, QVariant >(), Qt::ItemFlags flags = Qt::NoItemFlags );
explicit QgsSingleItemModel( QObject *parent SIP_TRANSFERTHIS = nullptr, const QString &text = QString(),
const QMap< int, QVariant > &data = QMap< int, QVariant >(), Qt::ItemFlags flags = Qt::NoItemFlags );
/**
* Constructor for a multi-column QgsSingleItemModel with the specified \a parent object.
*
* The \a columnData list specifies the data which should be returned for the specified roles for each column in the model, where
* each entry in the list must be a QMap of Qt item role to value.
*
* Custom \a flags can also be specified for the item.
*/
explicit QgsSingleItemModel( QObject *parent SIP_TRANSFERTHIS,
const QList< QMap< int, QVariant > > &columnData,
Qt::ItemFlags flags = Qt::NoItemFlags );
QVariant data( const QModelIndex &index, int role ) const override;
Qt::ItemFlags flags( const QModelIndex &index ) const override;
@ -56,6 +69,7 @@ class CORE_EXPORT QgsSingleItemModel: public QAbstractItemModel
QString mText;
QMap< int, QVariant > mData;
QList< QMap< int, QVariant > > mColumnData;
Qt::ItemFlags mFlags = Qt::NoItemFlags;
};

View File

@ -65,6 +65,28 @@ class TestQgsSingleItemModel(unittest.TestCase):
# manually specified tooltip should take precedence
self.assertEqual(model.data(index, Qt.ToolTipRole), 'abc')
def testModelWithColumns(self):
model = QgsSingleItemModel(None, [
{Qt.DisplayRole: 'col 1', Qt.ToolTipRole: 'column 1'},
{Qt.DisplayRole: 'col 2', Qt.ToolTipRole: 'column 2'},
{Qt.DisplayRole: 'col 3'},
], Qt.ItemIsSelectable | Qt.ItemIsDropEnabled)
self.assertEqual(model.rowCount(), 1)
self.assertEqual(model.columnCount(), 3)
index = model.index(0, 0)
self.assertEqual(model.data(index, Qt.DisplayRole), 'col 1')
self.assertEqual(model.data(index, Qt.ToolTipRole), 'column 1')
index = model.index(0, 1)
self.assertEqual(model.data(index, Qt.DisplayRole), 'col 2')
self.assertEqual(model.data(index, Qt.ToolTipRole), 'column 2')
index = model.index(0, 2)
self.assertEqual(model.data(index, Qt.DisplayRole), 'col 3')
self.assertFalse(model.data(index, Qt.ToolTipRole))
self.assertEqual(model.flags(index), Qt.ItemIsSelectable | Qt.ItemIsDropEnabled)
if __name__ == '__main__':
unittest.main()