Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // mergeproxy.h
- #ifndef MERGEPROXY_H
- #define MERGEPROXY_H
- #include <QSortFilterProxyModel>
- class MergeProxyPrivate;
- class MergeProxy : public QSortFilterProxyModel
- {
- Q_OBJECT
- Q_PROPERTY(int mergeKeyColumn READ mergeKeyColumn WRITE setMergeKeyColumn NOTIFY mergeKeyColumnChanged)
- Q_PROPERTY(int mergeKeyRole READ mergeKeyRole WRITE setMergeKeyRole NOTIFY mergeKeyRoleChanged)
- Q_DECLARE_PRIVATE(MergeProxy)
- public:
- explicit MergeProxy(QObject* parent = nullptr);
- virtual ~MergeProxy();
- int mergeKeyColumn() const;
- void setMergeKeyColumn(int val);
- int mergeKeyRole() const;
- void setMergeKeyRole(int val);
- virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
- signals:
- void mergeKeyColumnChanged();
- void mergeKeyRoleChanged();
- protected:
- virtual QVariant mergeValues(const QVariant& a, const QVariant& b) const;
- virtual bool uniqueCompare(const QVariant& a, const QVariant& b) const;
- virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
- private:
- MergeProxyPrivate* d_ptr;
- };
- #endif // MERGEPROXY_H
- /////////////////////////////////////////////////////////////////////////////
- // mergeproxy.cpp
- #include "mergeproxy.h"
- #include <QDateTime>
- class MergeProxyPrivate{
- Q_DECLARE_PUBLIC(MergeProxy)
- MergeProxyPrivate(MergeProxy* q);
- virtual ~MergeProxyPrivate()=default;
- int mergeKeyColumn;
- int mergeKeyRole;
- MergeProxy* q_ptr;
- };
- MergeProxyPrivate::MergeProxyPrivate(MergeProxy *q)
- :q_ptr(q)
- ,mergeKeyColumn(-1)
- ,mergeKeyRole(Qt::EditRole)
- {}
- /*!
- \class MergeProxy
- \brief MergeProxy is a proxy model that uses a key to determine if 2 rows should be merged
- MergeProxy can be used to merge different rows based on a key.
- The model will be treated as read only.
- You can use setMergeKeyColumn and setMergeKeyRole to determine what column should be used as key to the merge.
- Rows with the same data in this column will be merged together.
- Rows with the same key but different parent index in a tree will not be aggregated
- All non-key columns will be aggragated using mergeValues.
- All the filtering functionalities of QSortFilterProxyModel are still available
- */
- /*!
- Constructs a merged sorting filter model with the given \a parent.
- */
- MergeProxy::MergeProxy(QObject* parent)
- :QSortFilterProxyModel(parent)
- ,d_ptr(new MergeProxyPrivate(this))
- {
- connect(this,&MergeProxy::mergeKeyColumnChanged,this,&MergeProxy::invalidateFilter);
- connect(this,&MergeProxy::mergeKeyRoleChanged,this,&MergeProxy::invalidateFilter);
- }
- //! Destructor
- MergeProxy::~MergeProxy()
- {
- delete d_ptr;
- }
- /*!
- \property MergeProxy::mergeKeyColumn
- \brief the column where the key used to merge the contents of the source model is read from.
- The default value is -1. If the value is -1, no merging will be performed
- */
- int MergeProxy::mergeKeyColumn() const
- {
- Q_D(const MergeProxy);
- return d->mergeKeyColumn;
- }
- void MergeProxy::setMergeKeyColumn(int val)
- {
- Q_D(MergeProxy);
- if(val<0)
- val=-1;
- if(d->mergeKeyColumn!=val){
- d->mergeKeyColumn=val;
- emit mergeKeyColumnChanged();
- }
- }
- /*!
- \property MergeProxy::mergeKeyRole
- \brief the item role that is used to query the source model's data when merging items
- The default value is Qt::EditRole.
- */
- int MergeProxy::mergeKeyRole() const
- {
- Q_D(const MergeProxy);
- return d->mergeKeyRole;
- }
- void MergeProxy::setMergeKeyRole(int val)
- {
- if(val<0)
- return;
- Q_D(MergeProxy);
- if(d->mergeKeyRole!=val){
- d->mergeKeyRole=val;
- emit mergeKeyRoleChanged();
- }
- }
- /*!
- \reimp
- */
- QVariant MergeProxy::data(const QModelIndex &index, int role) const
- {
- Q_D(const MergeProxy);
- QVariant baseData= QSortFilterProxyModel::data(index,role);
- const QModelIndex mappedIdx= mapToSource(index);
- if(d->mergeKeyColumn<0 || d->mergeKeyColumn==mappedIdx.column())
- return baseData;
- const int rowCount=sourceModel()->rowCount(mappedIdx.parent());
- for(int i=0;i<rowCount;++i){
- if(i==mappedIdx.row())
- continue;
- if(!QSortFilterProxyModel::filterAcceptsRow(i,mappedIdx.parent()))
- continue;
- if(uniqueCompare(
- sourceModel()->index(mappedIdx.row(),d->mergeKeyColumn,mappedIdx.parent()).data(d->mergeKeyRole)
- , sourceModel()->index(i,d->mergeKeyColumn,mappedIdx.parent()).data(d->mergeKeyRole)
- ))
- baseData=mergeValues(baseData,sourceModel()->index(i,mappedIdx.column(),mappedIdx.parent()).data(role));
- }
- return baseData;
- }
- /*!
- \reimp
- */
- Qt::ItemFlags MergeProxy::flags(const QModelIndex &index) const
- {
- return QSortFilterProxyModel::flags(index) & (~Qt::ItemIsEditable);
- }
- /*!
- \brief Merges data from two rows together
- This method will be used to merge data from different rows together. The default implementation will use:
- for numeric types (including char), addition;
- for booleans the logic or;
- for QChar and QString concatenation;
- for QDate, QTime and QDateTime the difference between the two will be added to the first one.
- \warning The default implementation implicitly assumes that all data in the same colum is of the same type
- */
- QVariant MergeProxy::mergeValues(const QVariant &a, const QVariant &b) const
- {
- if(a.isNull())
- return b;
- if(b.isNull())
- return a;
- switch (a.userType()) {
- case QMetaType::UnknownType:
- Q_ASSERT_X(false, "MergeProxy::mergeValues", "Trying to use unregistered type.");
- return QVariant();
- case QMetaType::Bool: return a.toBool() || b.toBool();
- case QMetaType::Long:
- case QMetaType::Int: return a.toInt() + b.toInt();
- case QMetaType::ULong:
- case QMetaType::UInt: return a.toUInt()+ b.toUInt();
- case QMetaType::LongLong: return a.toLongLong()+ b.toLongLong();
- case QMetaType::ULongLong: return a.toULongLong()+ b.toULongLong();
- case QMetaType::Double: return a.toDouble()+ b.toDouble();
- case QMetaType::Short: return static_cast<short>(a.toInt()+ b.toInt());
- case QMetaType::SChar:
- case QMetaType::Char: return static_cast<char>(a.toInt()+ b.toInt());
- case QMetaType::UShort: return static_cast<unsigned short>(a.toUInt()+ b.toUInt());
- case QMetaType::UChar: return static_cast<unsigned char>(a.toUInt()+ b.toUInt());
- case QMetaType::Float: return a.toFloat()+ b.toFloat();
- case QMetaType::QString: return a.toString() + b.toString();
- case QMetaType::QChar:
- return QString(a.toChar()) + b.toChar();
- case QMetaType::QDate:{
- const QDate aDate=a.toDate();
- return aDate.addDays(qAbs(aDate.daysTo(b.toDate())));
- }
- case QMetaType::QTime: {
- const QTime aTime=a.toTime();
- return aTime.addMSecs(qAbs(aTime.msecsTo(b.toTime())));
- }
- case QMetaType::QDateTime: {
- const QDateTime aTime=a.toDateTime();
- return aTime.addMSecs(qAbs(aTime.msecsTo(b.toDateTime())));
- }
- default:
- qWarning("MergeProxy::mergeValues, Unhandled merge type");
- return a;
- }
- }
- /*!
- \brief Compares merge keys
- This method will be used to compare data in the merge key column to determine if two rows are to be aggragated.
- Rows will be aggragated if this method returns true
- The default implementation uses operator== for comparison
- */
- bool MergeProxy::uniqueCompare(const QVariant &a, const QVariant &b) const
- {
- return a==b;
- }
- /*!
- \reimp
- */
- bool MergeProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
- {
- Q_D(const MergeProxy);
- const bool baseFilter = QSortFilterProxyModel::filterAcceptsRow(source_row,source_parent);
- if(!baseFilter || d->mergeKeyColumn<0 || d->mergeKeyColumn>=sourceModel()->columnCount(source_parent))
- return baseFilter;
- for(int i=0;i<source_row;++i){
- if(uniqueCompare(
- sourceModel()->index(i,d->mergeKeyColumn,source_parent).data(d->mergeKeyRole)
- , sourceModel()->index(source_row,d->mergeKeyColumn,source_parent).data(d->mergeKeyRole)
- ))
- return false;
- }
- return true;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement