Results 1 to 3 of 3

Thread: QAbstractProxyModel for 'Group By' -- works but arrow keys don't !?

  1. #1
    Join Date
    Sep 2009
    Posts
    57
    Thanks
    7
    Thanked 5 Times in 4 Posts

    Question QAbstractProxyModel for 'Group By' -- works but arrow keys don't !?

    I've worked on a QAbstractProxyModel to group source model data into a tree, based upon specifying a column to group by.

    It works a charm, and all in all I'm happy.

    BUT...

    Arrow keys no longer work and I really cannot work out why?

    Any hints?

    Qt Code:
    1. // Proxy model for doing groupBy
    2. class GroupByModel : public QAbstractProxyModel
    3. {
    4. Q_OBJECT
    5.  
    6. private:
    7. int groupBy;
    8.  
    9. QList<QString> groups;
    10. QList<QModelIndex> groupIndexes;
    11. QMap<QString, QVector<int>*> groupToSourceRow;
    12. QVector<int> sourceRowToGroupRow;
    13.  
    14. public:
    15.  
    16. GroupByModel(QObject *parent = NULL) : QAbstractProxyModel(parent), groupBy(-1) {
    17. setParent(parent);
    18. }
    19. ~GroupByModel() {}
    20.  
    21. void setSourceModel(QAbstractItemModel *model) {
    22. QAbstractProxyModel::setSourceModel(model);
    23. setGroupBy(groupBy);
    24. }
    25.  
    26. QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const {
    27. if (parent.isValid()) {
    28. return createIndex(row,column, (void*)&groupIndexes[parent.row()]);
    29. } else {
    30. if (column == 0)
    31. return groupIndexes[row];
    32. else {
    33. return createIndex(row,column,NULL);
    34. }
    35. }
    36. }
    37.  
    38. QModelIndex parent(const QModelIndex &index) const {
    39. // parent should be encoded in the index if we supplied it, if
    40. // we didn't then return a duffer
    41. if (index == QModelIndex() || index.internalPointer() == NULL) {
    42. return QModelIndex();
    43. } else if (index.column()) {
    44. return QModelIndex();
    45. } else {
    46. return *static_cast<QModelIndex*>(index.internalPointer());
    47. }
    48. }
    49.  
    50. QModelIndex mapToSource(const QModelIndex &proxyIndex) const {
    51.  
    52. if (proxyIndex.internalPointer() != NULL) {
    53.  
    54. int groupNo = ((QModelIndex*)proxyIndex.internalPointer())->row();
    55. if (groupNo < 0 || groupNo >= groups.count() || proxyIndex.column() == 0) {
    56. return QModelIndex();
    57. }
    58.  
    59. return sourceModel()->index(groupToSourceRow.value(groups[groupNo])->at(proxyIndex.row()),
    60. proxyIndex.column()-1, // accomodate virtual column
    61. }
    62. return QModelIndex();
    63. }
    64.  
    65. QModelIndex mapFromSource(const QModelIndex &sourceIndex) const {
    66.  
    67. // which group did we put this row into?
    68. QString group = whichGroup(sourceIndex.row());
    69. int groupNo = groups.indexOf(group);
    70.  
    71. if (groupNo < 0) {
    72. return QModelIndex();
    73. } else {
    74. QModelIndex *p = new QModelIndex(createIndex(groupNo, 0, NULL));
    75. return createIndex(sourceRowToGroupRow[sourceIndex.row()], sourceIndex.column()+1, &p); // accomodate virtual column
    76. }
    77. }
    78.  
    79. QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const {
    80.  
    81. if (!proxyIndex.isValid()) return QVariant();
    82.  
    83. QVariant returning;
    84.  
    85. // if we are not at column 0 or we have a parent
    86. //if (proxyIndex.internalPointer() != NULL || proxyIndex.column() > 0) {
    87. if (proxyIndex.column() > 0) {
    88.  
    89. returning = sourceModel()->data(mapToSource(proxyIndex), role);
    90.  
    91. } else if (proxyIndex.internalPointer() == NULL) {
    92.  
    93. // its our group by!
    94. if (proxyIndex.row() < groups.count()) {
    95.  
    96. // blank values become "(blank)"
    97. QString group = groups[proxyIndex.row()];
    98. if (group == "") group = QString("(blank)");
    99.  
    100. // format the group by with ride count etc
    101. if (groupBy != -1) {
    102. QString returnString = QString("%1: %2 (%3 rides)")
    103. .arg(sourceModel()->headerData(groupBy, Qt::Horizontal).toString())
    104. .arg(group)
    105. .arg(groupToSourceRow.value(groups[proxyIndex.row()])->count());
    106. returning = QVariant(returnString);
    107. } else {
    108. QString returnString = QString("All %1 rides")
    109. .arg(groupToSourceRow.value(groups[proxyIndex.row()])->count());
    110. returning = QVariant(returnString);
    111. }
    112. }
    113. }
    114.  
    115. return returning;
    116.  
    117. }
    118.  
    119. QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const {
    120. if (section)
    121. return sourceModel()->headerData(section-1, orientation, role);
    122. else
    123. return QVariant("*");
    124. }
    125.  
    126. bool setHeaderData (int section, Qt::Orientation orientation, const QVariant & value, int role = Qt::EditRole) {
    127. if (section)
    128. return sourceModel()->setHeaderData(section-1, orientation, value, role);
    129. else
    130. return true;
    131. }
    132.  
    133. int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const {
    134. return sourceModel()->columnCount(QModelIndex())+1; // accomodate virtual group column
    135. }
    136.  
    137. int rowCount(const QModelIndex &parent = QModelIndex()) const {
    138. if (parent == QModelIndex()) {
    139.  
    140. // top level return count of groups
    141. return groups.count();
    142.  
    143. } else if (parent.column() == 0 && parent.internalPointer() == NULL) {
    144.  
    145. // second level return count of rows for group
    146. return groupToSourceRow.value(groups[parent.row()])->count();
    147.  
    148. } else {
    149.  
    150. // no children any lower
    151. return 0;
    152. }
    153. }
    154.  
    155. // does this index have children?
    156. bool hasChildren(const QModelIndex &index) const {
    157.  
    158. if (index == QModelIndex()) {
    159.  
    160. // at top
    161. return (groups.count() > 0);
    162.  
    163. } else if (index.column() == 0 && index.internalPointer() == NULL) {
    164.  
    165. // first column - the group bys
    166. return (groupToSourceRow.value(groups[index.row()])->count() > 0);
    167.  
    168. } else {
    169.  
    170. return false;
    171.  
    172. }
    173. }
    174.  
    175. //
    176. // GroupBy features
    177. //
    178. void setGroupBy(int column) {
    179.  
    180. // shift down
    181. if (column >= 0) column -= 1;
    182.  
    183. groupBy = column; // accomodate virtual column
    184. setGroups();
    185. }
    186.  
    187. QString whichGroup(int row) const {
    188.  
    189. if (groupBy == -1) return tr("All Rides");
    190. else return groupFromValue(headerData(groupBy+1, Qt::Horizontal).toString(),
    191. sourceModel()->data(sourceModel()->index(row,groupBy)).toString());
    192.  
    193. }
    194.  
    195. // implemented in RideNavigator.cpp, to avoid developers
    196. // from working out how this QAbstractProxy works, or
    197. // perhaps breaking it by accident ;-)
    198. QString groupFromValue(QString, QString) const;
    199.  
    200. void clearGroups() {
    201. // Wipe current
    202. QMapIterator<QString, QVector<int>*> i(groupToSourceRow);
    203. while (i.hasNext()) {
    204. i.next();
    205. delete i.value();
    206. }
    207. groups.clear();
    208. groupIndexes.clear();
    209. groupToSourceRow.clear();
    210. sourceRowToGroupRow.clear();
    211. }
    212.  
    213. int groupCount() {
    214. return groups.count();
    215. }
    216.  
    217. void setGroups() {
    218.  
    219. // let the views know we're doing this
    220. beginResetModel();
    221.  
    222. // wipe whatever is there first
    223. clearGroups();
    224.  
    225. if (groupBy >= 0) {
    226.  
    227. // create a QMap from 'group' string to list of rows in that group
    228. for (int i=0; i<sourceModel()->rowCount(QModelIndex()); i++) {
    229. QString value = whichGroup(i);
    230.  
    231. QVector<int> *rows;
    232. if ((rows=groupToSourceRow.value(value,NULL)) == NULL) {
    233. // add to list of groups
    234. rows = new QVector<int>;
    235. groupToSourceRow.insert(value, rows);
    236. }
    237.  
    238. // rowmap is an array corresponding to each row in the
    239. // source model, and maps to its row # within the group
    240. sourceRowToGroupRow.append(rows->count());
    241.  
    242. // add to this groups rows
    243. rows->append(i);
    244. }
    245.  
    246. } else {
    247.  
    248. // Just one group by 'All Rides'
    249. QVector<int> *rows = new QVector<int>;
    250. for (int i=0; i<sourceModel()->rowCount(QModelIndex()); i++) {
    251. rows->append(i);
    252. sourceRowToGroupRow.append(i);
    253. }
    254. groupToSourceRow.insert("All Rides", rows);
    255.  
    256. }
    257.  
    258. // Update list of groups
    259. int group=0;
    260. QMapIterator<QString, QVector<int>*> j(groupToSourceRow);
    261. while (j.hasNext()) {
    262. j.next();
    263. groups << j.key();
    264. groupIndexes << createIndex(group++,0,NULL);
    265. }
    266.  
    267. // all done. let the views know everything changed
    268. endResetModel();
    269. }
    270. };
    To copy to clipboard, switch view to plain text mode 

    Many thanks for taking time to look at this! it really is appreciated.

    Cheers,
    Mark

  2. #2
    Join Date
    Sep 2009
    Posts
    57
    Thanks
    7
    Thanked 5 Times in 4 Posts

    Default Re: QAbstractProxyModel for 'Group By' -- works but arrow keys don't !?

    I should have mentioned that the arrow keys don't move up and down in a QTreeView when this proxymodel is between the view and the source sqltable.

    Cheers,
    Mark

  3. #3
    Join Date
    Sep 2009
    Posts
    57
    Thanks
    7
    Thanked 5 Times in 4 Posts

    Default Re: QAbstractProxyModel for 'Group By' -- works but arrow keys don't !?

    Fixed it ha!

    Because I have a virtual column in column zero, but mapToSource returns an invalid QModelIndex for column zero (because it doesn't exist in the source) then the flags for column zero are empty -- and makes it non-selectable.

    If I provide my own version of ::flags() and make it selectable the arrow keys spring into life. yay!

    here is the bit of code I needed to add, I make the groupby's unselectable since they are just 'headings' not data (if that makes any sense).

    Qt Code:
    1. Qt::ItemFlags flags ( const QModelIndex & index ) const {
    2. if (index.internalPointer() == NULL) {
    3. return Qt::ItemIsEnabled;
    4. } else {
    5. return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    6. }
    7. }
    To copy to clipboard, switch view to plain text mode 

Similar Threads

  1. Detect Arrow Keys
    By Ebonair in forum Qt Programming
    Replies: 4
    Last Post: 1st October 2010, 03:55
  2. Replies: 1
    Last Post: 6th July 2009, 13:48
  3. QAbstractProxyModel to do a Tree
    By xgoan in forum Qt Programming
    Replies: 3
    Last Post: 18th November 2008, 17:31
  4. Help with QAbstractProxyModel
    By killerwookie99 in forum Qt Programming
    Replies: 9
    Last Post: 12th September 2008, 22:13
  5. How can I associate arrow keys?
    By Mariane in forum Newbie
    Replies: 2
    Last Post: 20th January 2006, 18:31

Tags for this Thread

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
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.