Reputation: 354
My application consists of many Lists that I display in QML-ListViews by using QAbstractListModel derived model-classes. It's always the same, with the difference of the Item-Type. That's why I want to know how to build a class-template for this approach.
Upvotes: 2
Views: 1283
Reputation: 354
I figured out, that it is not possible to use the Q_OBJECT-Macro in a class-template. That's why my GenericListModel consists of two part.
1. GenericListModelData
The first part is the Model itself that derives from QAbstractListModel and implements the basic functions data(), rowCount() and roleNames().
2. GenericListModel
The second part is the class-template that is used as a wrapper to provide functions similar to a QListView.
If you have any suggestions or questions please let me know. It would be really nice to improve this solution.
I uploaded the full sourcecode here: https://github.com/sebabebibobu/QGenericListModel/
1. GenericListModelData
QVariant GenericListModelData::data(const QModelIndex &index, int role) const
{
QObject *item = m_itemList.at(index.row());
return item->property(item->metaObject()->property(role).name());
}
/*
* Returns the number of items attached to the list.
*/
int GenericListModelData::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_itemList.size();
}
/*
* Generates a hash out of QMetaObject property-index and property-name.
*/
QHash<int, QByteArray> GenericListModelData::roleNames() const
{
QHash<int, QByteArray> roles;
if (!m_itemList.isEmpty()) {
for(int i = 0; i < m_itemList.at(0)->metaObject()->propertyCount(); i++) {
roles[i] = m_itemList.at(0)->metaObject()->property(i).name();
}
}
return roles;
}
/*
* Append Item to List.
*/
void GenericListModelData::appendItem(QObject *item)
{
/* map the notify()-signal-index with the property-index when the first item get's inserted */
if (m_itemList.isEmpty()) {
for(int i = 0; i < item->metaObject()->propertyCount(); i++) {
m_propertySignalIndexHash.insert(item->metaObject()->property(i).notifySignalIndex(), i);
}
}
/* connect each notify()-signals to the onDataChanged()-slot which call's the dataChanged()-signal */
for(int i = 0; i < item->metaObject()->propertyCount(); i++) {
connect(item, "2" + item->metaObject()->property(i).notifySignal().methodSignature(), this, SLOT(onDataChanged()));
}
/* finally append the item the list */
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_itemList.append(item);
endInsertRows();
}
/*
* Helper-Slot that emit's the dataChanged()-signal of QAbstractListModel.
*/
void GenericListModelData::onDataChanged()
{
QModelIndex index = createIndex(m_itemList.indexOf(sender()),0);
QVector<int> roles;
roles.append(m_propertySignalIndexHash.value(senderSignalIndex()));
emit dataChanged(index, index, roles);
}
2. GenericListModel
template <typename T>
class GenericListModel : public GenericListModelData
{
public:
explicit GenericListModel(QObject *parent) : GenericListModelData(parent) {
}
void append(T *item) {
appendItem(item);
}
T *at(int i) {
return qobject_cast<T *>(m_itemList.at(i));
}
};
Update 01.05.2016
GrecKo posted in the comments, that a project like mine already exists. That's why I decided to share the link of this project here too:
http://gitlab.unique-conception.org/qt-qml-tricks/qt-qml-models
Upvotes: 3