Results 1 to 12 of 12

Thread: Group / Aggregate QAbstractItemModel

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Jul 2009
    Posts
    139
    Thanks
    13
    Thanked 59 Times in 52 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Group / Aggregate QAbstractItemModel

    OK, here is my attempt at a proxy model that does aggregation. Some notes:
    - It assumes the data is unsorted. With sorted data a more efficient implementation could be created.
    - It is static. It doesn't update with changes to the source model. This could be fixed by connecting to the appropriate slots.
    - The groupby is done as a string. If only grouping by integers, it could be made more efficient.
    - It only works on flat models.
    - The setup is done in the constructor. It takes a function pointer to an aggregate function (Total and Average are supplied), the groupby column and the aggregate column.
    So here is the code:

    Header:
    Qt Code:
    1. #include <QDebug>
    2. #include <QAbstractProxyModel>
    3. #include <QMap>
    4. #include <QVariantList>
    5. #include <QStringList>
    6.  
    7. class AggregateItem
    8. {
    9. public:
    10. QVariantList items;
    11. QVariant result;
    12. int sourceRow;
    13. };
    14.  
    15. class Aggregator : public QAbstractProxyModel
    16. {
    17. Q_OBJECT
    18.  
    19. public:
    20. Aggregator(QVariant (*aggFunction)(const QVariantList&),
    21. int colGroup,
    22. int colAgg,
    23. int roleGroup = Qt::DisplayRole,
    24. int roleAgg = Qt::DisplayRole,
    25. QObject * parent = 0);
    26.  
    27. virtual int columnCount (const QModelIndex& parent = QModelIndex()) const ;
    28. virtual int rowCount(const QModelIndex& parent) const ;
    29. virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const ;
    30. virtual QModelIndex parent (const QModelIndex& index) const ;
    31. virtual QModelIndex mapFromSource(const QModelIndex&) const ;
    32. virtual QModelIndex mapToSource(const QModelIndex&) const ;
    33. virtual bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const;
    34. virtual QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
    35. virtual Qt::ItemFlags flags ( const QModelIndex & index ) const;
    36.  
    37. void transformData();
    38.  
    39. private:
    40. QMap< QString, AggregateItem > map;
    41.  
    42. int m_colGroup, m_colAgg, m_roleGroup, m_roleAgg;
    43. QVariant (*m_aggFunction)(const QVariantList&);
    44. };
    To copy to clipboard, switch view to plain text mode 
    Source:
    Qt Code:
    1. #include <QtGui/QApplication>
    2. #include <QStandardItemModel>
    3. #include <QTreeView>
    4. #include <QObject>
    5.  
    6. #include "aggregator.h"
    7.  
    8.  
    9.  
    10. Aggregator::Aggregator(QVariant (*aggFunction)(const QVariantList&),
    11. int colGroup, int colAgg,
    12. int roleGroup, int roleAgg, QObject *parent) :
    13. m_colGroup(colGroup), m_colAgg(colAgg),
    14. m_roleGroup(roleGroup), m_roleAgg(roleAgg),
    15. m_aggFunction(aggFunction)
    16. { }
    17.  
    18. QVariant Total(const QVariantList & vars)
    19. {
    20. int total = 0;
    21.  
    22. foreach(QVariant var, vars)
    23. {
    24. total += var.toInt();
    25. }
    26.  
    27. return QVariant(total);
    28. }
    29.  
    30. QVariant Average(const QVariantList & vars)
    31. {
    32. int total = 0;
    33.  
    34. foreach(QVariant var, vars)
    35. {
    36. total += var.toInt();
    37. }
    38.  
    39. return QVariant(total / vars.count());
    40. }
    41.  
    42. void Aggregator::transformData()
    43. {
    44. QAbstractItemModel * m = sourceModel();
    45. int rowCount = m->rowCount();
    46.  
    47. for (int i = 0; i < rowCount; i++)
    48. {
    49. QString group = m->data(m->index(i, m_colGroup), m_roleGroup).toString();
    50. QVariant agg = m->data(m->index(i, m_colAgg), m_roleAgg);
    51.  
    52. if (!map.contains(group))
    53. {
    54. AggregateItem item;
    55. item.sourceRow = i;
    56. map.insert(group, item);
    57. rows.push_back(group);
    58. }
    59.  
    60. map[group].items.push_back(agg);
    61. }
    62.  
    63. foreach (QString str, map.keys())
    64. {
    65. map[str].result = m_aggFunction(map.value(str).items);
    66. map[str].items.clear(); /* No longer needed. */
    67. }
    68. }
    69.  
    70. QVariant Aggregator::data ( const QModelIndex & index, int role ) const
    71. {
    72. if (index.column() == columnCount() - 1)
    73. {
    74. if ((index.row() < rows.count() && index.row() >= 0) &&
    75. (role == Qt::DisplayRole || role == Qt::EditRole))
    76. return map.value(rows.at(index.row())).result;
    77. else
    78. return QVariant();
    79. }
    80.  
    81. return QAbstractProxyModel::data(index, role);
    82. }
    83.  
    84. Qt::ItemFlags Aggregator::flags ( const QModelIndex & index ) const
    85. {
    86. if (index.column() == columnCount() - 1)
    87. return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    88. else
    89. return QAbstractProxyModel::flags(index);
    90. }
    91.  
    92. int Aggregator::columnCount(const QModelIndex& parent) const
    93. {
    94. return sourceModel()->columnCount(QModelIndex()) + 1;
    95. }
    96.  
    97. int Aggregator::rowCount(const QModelIndex& parent) const
    98. {
    99. return rows.count();
    100. }
    101.  
    102. QModelIndex Aggregator::index(int row, int column, const QModelIndex& parent) const
    103. {
    104. if (row >= rowCount(QModelIndex()) || row < 0)
    105. return QModelIndex();
    106.  
    107. return createIndex(row, column, map[rows.at(row)].sourceRow);
    108. }
    109.  
    110. QModelIndex Aggregator::parent(const QModelIndex& index) const
    111. {
    112. return QModelIndex();
    113. }
    114.  
    115. QModelIndex Aggregator::mapFromSource(const QModelIndex& index) const
    116. {
    117. if (index.isValid())
    118. {
    119. QString groupby = sourceModel()->data(sourceModel()->index(index.row(), m_colGroup)).toString();
    120. for (int i = 0; i < rows.count(); i++)
    121. {
    122. if (rows.at(i) == groupby)
    123. return createIndex(i, index.column(), map[groupby].sourceRow);
    124. }
    125. }
    126. return QModelIndex();
    127. }
    128.  
    129. QModelIndex Aggregator::mapToSource(const QModelIndex& index) const
    130. {
    131. if (index.isValid() && index.column() != columnCount(QModelIndex()) - 1)
    132. return sourceModel()->index(index.internalId(), index.column(), QModelIndex()) ;
    133. else
    134. return QModelIndex();
    135. }
    136.  
    137. bool Aggregator::hasChildren(const QModelIndex &parent) const
    138. {
    139. return !parent.isValid();
    140. }
    To copy to clipboard, switch view to plain text mode 
    and usage:
    Qt Code:
    1. int main(int argc, char *argv[])
    2.  
    3. {
    4. QApplication app(argc, argv);
    5.  
    6. QStandardItemModel model(8, 4);
    7. for (int row = 0; row < 4; ++row) {
    8.  
    9. QStandardItem *item = new QStandardItem(QString("test"));
    10. model.setItem(row, 0, item);
    11.  
    12. item = new QStandardItem(QString("%1").arg(1));
    13. model.setItem(row, 1, item);
    14.  
    15. for (int column = 2; column < 4; ++column) {
    16. QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
    17. model.setItem(row, column, item);
    18. }
    19. }
    20.  
    21.  
    22. for (int row = 4; row < 8; ++row) {
    23.  
    24. QStandardItem *item = new QStandardItem(QString("test2"));
    25. model.setItem(row, 0, item);
    26.  
    27. item = new QStandardItem(QString("%1").arg(3));
    28. model.setItem(row, 1, item);
    29.  
    30. for (int column = 2; column < 4; ++column) {
    31. QStandardItem *item = new QStandardItem(QString("row %0, column %1").arg(row).arg(column));
    32. model.setItem(row, column, item);
    33. }
    34. }
    35.  
    36.  
    37.  
    38. QTreeView * tv = new QTreeView;
    39. Aggregator * ag = new Aggregator(Total, 0, 1);
    40.  
    41. ag->setSourceModel(&model);
    42. ag->transformData();
    43. tv->setModel(ag);
    44. tv->show();
    45.  
    46. return app.exec();
    47. }
    To copy to clipboard, switch view to plain text mode 

  2. #2
    Join Date
    May 2009
    Location
    Copenhagen
    Posts
    50
    Thanks
    6
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Group / Aggregate QAbstractItemModel

    Hi numbat

    This looks exiting. I'm getting an error though when trying to compile. It's in the declaration of ag (see above line 39 in main.cpp) and the error description is: 'Total' was not declared in this scope. Are you passing the right argument to the constructor?

  3. #3
    Join Date
    Jul 2009
    Posts
    139
    Thanks
    13
    Thanked 59 Times in 52 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Group / Aggregate QAbstractItemModel

    Sorry, add the following two lines to the end of the header file:
    Qt Code:
    1. QVariant Average(const QVariantList & vars);
    2. QVariant Total(const QVariantList & vars);
    To copy to clipboard, switch view to plain text mode 

  4. #4
    Join Date
    May 2009
    Location
    Copenhagen
    Posts
    50
    Thanks
    6
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Group / Aggregate QAbstractItemModel

    I'm still getting the same error after pasting the two function prototypes to the class. The error message is the same as before: 'Total' was not declared in this scope. I tried to work around it but I must admit that I don't understand the following argument for the constructor: QVariant (*aggFunction)(const QVariantList&) and you have it later in the class under the private section as QVariant (*m_aggFunction)(const QVariantList&). Does this mean casting QVariantList Items as pointer to m_aggFunction or what? I don't really follow?!

  5. #5
    Join Date
    Jul 2009
    Posts
    139
    Thanks
    13
    Thanked 59 Times in 52 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Group / Aggregate QAbstractItemModel

    It works for me. I'll attach a zip.
    I tried to work around it but I must admit that I don't understand the following argument for the constructor: QVariant (*aggFunction)(const QVariantList&) and you have it later in the class under the private section as QVariant (*m_aggFunction)(const QVariantList&).
    That is a function pointer. Specifically, it is a pointer to a function that takes a const QVariantList& as argument and returns a QVariant. I use function pointers so that the programmer can provide another function as an argument to the constructor and this function will be used as the aggregate function. This probably signals my C heritage, in C++ we should probably use a class.
    Attached Files Attached Files

  6. The following user says thank you to numbat for this useful post:

    Nightfox (10th January 2010)

  7. #6
    Join Date
    Jul 2009
    Posts
    139
    Thanks
    13
    Thanked 59 Times in 52 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Group / Aggregate QAbstractItemModel

    Here is another simpler, although far less efficient way of doing it:
    Qt Code:
    1. #include <QApplication>
    2. #include <QSqlDatabase>
    3. #include <QDebug>
    4. #include <QSqlTableModel>
    5. #include <QAbstractItemModel>
    6. #include <QStringList>
    7. #include <QSqlQuery>
    8. #include <QSqlQueryModel>
    9. #include <QStandardItemModel>
    10. #include <QTreeView>
    11. #include <QHeaderView>
    12.  
    13.  
    14. /* Given a data model, a column to group by, a column to aggregate and an
    15.   aggregate function, this function will return a model with the given
    16.   transformation. Available aggregate function include SUM, AVG, COUNT, MAX
    17.   and MIN.
    18.  */
    19. unsigned int groupByColumn,
    20. unsigned int aggregateColumn,
    21. const QString & aggregateFunction = "SUM")
    22. {
    23. /* Open an in-memory database. */
    24. QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    25. db.setDatabaseName(":memory:");
    26. bool ok = db.open();
    27.  
    28. /* Create a table with the correct number of columns. */
    29. QStringList columns;
    30. for (int i = 0; i < m.columnCount(); i++)
    31. columns.push_back(QString("F%1").arg(i));
    32.  
    33. QString sql = QString("CREATE TABLE T1(%1)").arg(columns.join(","));
    34. QSqlQuery query(db);
    35. ok = query.exec(sql);
    36.  
    37. /* Set up a table model of our newly created table. */
    38. QSqlTableModel tbl(0, db);
    39. tbl.setTable("T1");
    40. tbl.setEditStrategy(QSqlTableModel::OnManualSubmit);
    41. tbl.insertRows(0, m.rowCount());
    42.  
    43. /* Now copy accross our model to the newly created table. */
    44. for (int i = 0; i < m.rowCount(); i++)
    45. for (int j = 0; j < m.columnCount(); j++)
    46. tbl.setData(tbl.index(i, j), m.data(m.index(i, j)));
    47.  
    48. /* Submit changes to database. */
    49. ok = tbl.submitAll();
    50.  
    51. /* Finally, query our database for the grouped by table. */
    52. QString sql2 = QString("SELECT %3(F%2), * FROM T1 GROUP BY F%1").
    53. arg(groupByColumn).
    54. arg(aggregateColumn).
    55. arg(aggregateFunction);
    56.  
    57. out->setQuery(sql2, db);
    58. return out;
    59. }
    60.  
    61.  
    62.  
    63.  
    64. int main(int argc, char * argv[])
    65. {
    66. QApplication a(argc, argv);
    67.  
    68.  
    69. QStandardItemModel model(8, 3);
    70. for (int row = 0; row < 4; ++row)
    71. {
    72. QStandardItem *item = new QStandardItem(QString("test"));
    73. model.setItem(row, 0, item);
    74.  
    75. item = new QStandardItem(QString("%1").arg(1));
    76. model.setItem(row, 1, item);
    77.  
    78. item = new QStandardItem(QString("row %0").arg(row));
    79. model.setItem(row, 2, item);
    80. }
    81.  
    82. for (int row = 4; row < 8; ++row)
    83. {
    84. QStandardItem *item = new QStandardItem(QString("test2"));
    85. model.setItem(row, 0, item);
    86.  
    87. item = new QStandardItem(QString("%1").arg(5));
    88. model.setItem(row, 1, item);
    89.  
    90. item = new QStandardItem(QString("row %0").arg(row));
    91. model.setItem(row, 2, item);
    92. }
    93.  
    94. QAbstractItemModel * tbl = doGroupby(model, 0, 1, "SUM");
    95. tbl->setHeaderData(0, Qt::Horizontal, "Total");
    96. tv.setModel(tbl);
    97. tv.header()->moveSection(0, 3);
    98. tv.show();
    99.  
    100. return a.exec();
    101. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by numbat; 10th January 2010 at 18:51.

  8. #7
    Join Date
    May 2009
    Location
    Copenhagen
    Posts
    50
    Thanks
    6
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Group / Aggregate QAbstractItemModel

    Hi numbat
    It just compiled the zip and it works like a charm! Last time I added the two lines inside the class scope so that's why I could''t get it to work before. I must say I'm impressed with your post - so little code and yet so powerfull. This is exactly what I wanted to do. Thanks!

  9. #8
    Join Date
    May 2009
    Location
    Copenhagen
    Posts
    50
    Thanks
    6
    Thanked 2 Times in 2 Posts
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Group / Aggregate QAbstractItemModel

    Hi numbat
    It just compiled the zip and it works like a charm! Last time I added the two lines inside the class scope so that's why I could''t get it to work before. I must say I'm impressed with your post - so little code and yet so powerfull. This is exactly what I wanted to do. Thanks!

Similar Threads

  1. How to make QAbstractItemModel 's data checkable
    By nifei in forum Qt Programming
    Replies: 12
    Last Post: 1st April 2013, 19:52
  2. Replies: 2
    Last Post: 22nd September 2008, 09:22

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Qt is a trademark of The Qt Company.