Results 1 to 9 of 9

Thread: Problem with ProxyModel merging models with QSortFilterProxyModel.

  1. #1
    Join Date
    Jul 2014
    Location
    Germany
    Posts
    14
    Thanks
    3
    Qt products
    Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Problem with ProxyModel merging models with QSortFilterProxyModel.

    Hello!

    I'm trying to implement a ProxyModel that combines up to 3 existing models (based on QAbstractTableModel). It works mostly, but I have some small problems.

    If I use my proxy model directly in a QTableView anything works perfectly. I can add, change and delete entries to any of the models and the QableView updates correctly.

    But if I try to use my ProxyModel indirectly through a QSortFilterProxyModel then only adding and changing works correctly. If I delete an entry, the QTableView is not updated correctly.

    I'm not sure, if the signals for row removal are passed correctly?

    Maybe somebody could take a look at my current implementation?!
    What did I wrong?

    Any help is appreciated...

    Qt Code:
    1. #include <QAbstractProxyModel>
    2. class MergeProxyModel : public QAbstractProxyModel
    3. {
    4. Q_OBJECT
    5. public:
    6. MergeProxyModel(QAbstractTableModel* first_model=0, QAbstractTableModel* second_model=0, QAbstractTableModel* third_model=0, QObject* parent=0);
    7. ~MergeProxyModel();
    8.  
    9. void setColumnCount(int count) { column_count=count; };
    10.  
    11. Qt::ItemFlags flags(const QModelIndex &index) const;
    12. QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
    13.  
    14. int rowCount(const QModelIndex &index=QModelIndex()) const;
    15. int columnCount(const QModelIndex &index=QModelIndex()) const;
    16.  
    17. QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
    18. QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
    19.  
    20. QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
    21. QModelIndex parent(const QModelIndex &proxyChild) const;
    22.  
    23. bool indexCheck(const QModelIndex &index) const;
    24.  
    25. int getListNo(const QModelIndex &index) const;
    26. int getListPos(int n) const;
    27. int getListPos(const QModelIndex &index) const;
    28.  
    29. QAbstractTableModel* sourceModel(const QModelIndex &index) const;
    30.  
    31. protected:
    32. int column_count;
    33. int listsCount() const;
    34. int getOffset(const QAbstractItemModel* smodel) const;
    35.  
    36. QAbstractTableModel* first_model;
    37. QAbstractTableModel* second_model;
    38. QAbstractTableModel* third_model;
    39.  
    40. private:
    41. void connectModels(QAbstractTableModel* model);
    42.  
    43. private slots:
    44.  
    45. void dataChangedSlot(const QModelIndex &topLeft, const QModelIndex &bottomRight);
    46.  
    47. void rowsAboutToBeInsertedSlot(const QModelIndex &parent, int first, int last);
    48. void rowsInsertedSlot(const QModelIndex &parent, int first, int last);
    49.  
    50. void rowsAboutToBeRemovedSlot(const QModelIndex &parent, int first, int last);
    51. void rowsRemovedSlot(const QModelIndex &parent, int first, int last);
    52.  
    53. void modelAboutToBeResetSlot();
    54. void modelResetSlot();
    55. };
    56.  
    57. MergeProxyModel::MergeProxyModel(QAbstractTableModel* _first_model,
    58. QAbstractTableModel* _second_model,
    59. QAbstractTableModel* _third_model,
    60. QObject *_parent)
    61. : QAbstractProxyModel(_parent),
    62. column_count(1),
    63. first_model(_first_model),
    64. second_model(_second_model),
    65. third_model(_third_model)
    66. {
    67. if (first_model) connectModels(first_model);
    68. if (second_model) connectModels(second_model);
    69. if (third_model) connectModels(third_model);
    70. }
    71.  
    72. MergeProxyModel::~MergeProxyModel()
    73. {
    74. }
    75.  
    76. void MergeProxyModel::connectModels(QAbstractTableModel* model)
    77. {
    78. Q_ASSERT(model);
    79.  
    80. connect(model, SIGNAL(layoutAboutToBeChanged()), this, SIGNAL(layoutAboutToBeChanged()), Qt::DirectConnection);
    81. connect(model, SIGNAL(layoutChanged()), this, SIGNAL(layoutChanged()), Qt::DirectConnection);
    82.  
    83. connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChangedSlot(QModelIndex,QModelIndex)), Qt::DirectConnection);
    84.  
    85. connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(rowsAboutToBeInsertedSlot(QModelIndex,int,int)), Qt::DirectConnection);
    86. connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInsertedSlot(QModelIndex,int,int)), Qt::DirectConnection);
    87.  
    88. connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemovedSlot(QModelIndex,int,int)), Qt::DirectConnection);
    89. connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemovedSlot(QModelIndex,int,int)), Qt::DirectConnection);
    90.  
    91. connect(model, SIGNAL(modelAboutToBeReset()), this, SLOT(modelAboutToBeResetSlot()), Qt::DirectConnection);
    92. connect(model, SIGNAL(modelReset()), this, SLOT(modelResetSlot()), Qt::DirectConnection);
    93. }
    94.  
    95. int MergeProxyModel::listsCount() const
    96. {
    97. int totalCount = 0;
    98. if (first_model) totalCount += first_model->rowCount();
    99. if (second_model) totalCount += second_model->rowCount();
    100. if (third_model) totalCount += third_model->rowCount();
    101. return totalCount;
    102. }
    103.  
    104. bool MergeProxyModel::indexCheck(const QModelIndex &index) const
    105. {
    106. return (index.isValid() && index.row() >= 0 && index.row() < rowCount() && index.column() >= 0 && index.column() < columnCount());
    107. }
    108.  
    109. Qt::ItemFlags MergeProxyModel::flags(const QModelIndex &index) const
    110. {
    111. Qt::ItemFlags theFlags = Qt::NoItemFlags;
    112. if (index.isValid()) return QAbstractProxyModel::flags(index);
    113. return theFlags;
    114. }
    115.  
    116. QVariant MergeProxyModel::data(const QModelIndex &index, int role) const
    117. {
    118. if (!indexCheck(index)) return QVariant();
    119.  
    120. switch (getListNo(index))
    121. {
    122. case 0: return first_model->data(mapToSource(index), role);
    123. case 1: return second_model->data(mapToSource(index), role);
    124. case 2: return third_model->data(mapToSource(index), role);
    125. default: return QVariant();
    126. }
    127. return QVariant();
    128. }
    129.  
    130. int MergeProxyModel::rowCount(const QModelIndex &index) const
    131. {
    132. return index.isValid() ? 0 : listsCount();
    133. }
    134.  
    135. int MergeProxyModel::columnCount(const QModelIndex &index) const
    136. {
    137. return index.isValid() ? 0 : column_count;
    138. }
    139.  
    140. int MergeProxyModel::getOffset(const QAbstractItemModel* smodel) const
    141. {
    142. int offset = 0;
    143.  
    144. if (smodel != first_model) {
    145. if (first_model) offset += first_model->rowCount();
    146. if (smodel != second_model) {
    147. if (second_model) offset += second_model->rowCount();
    148. }
    149. }
    150. return offset;
    151. }
    152.  
    153. QModelIndex MergeProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
    154. {
    155. if (!sourceIndex.isValid()) return QModelIndex();
    156. else if (sourceIndex.parent().isValid()) return QModelIndex();
    157.  
    158. int offset = getOffset(sourceIndex.model());
    159. return createIndex(sourceIndex.row() + offset, sourceIndex.column());
    160. }
    161.  
    162. QModelIndex MergeProxyModel::mapToSource(const QModelIndex &proxyIndex) const
    163. {
    164. if (!proxyIndex.isValid()) return QModelIndex();
    165.  
    166. int pos = getListPos(proxyIndex);
    167. QAbstractTableModel* smodel = sourceModel(proxyIndex);
    168. if (!smodel) return QModelIndex();
    169.  
    170. return smodel->index(pos, proxyIndex.column());
    171. }
    172.  
    173. QModelIndex MergeProxyModel::index(int row, int column, const QModelIndex &parent) const
    174. {
    175. if (row >= rowCount()) return QModelIndex();
    176. return createIndex(row, column, getListPos(row));
    177. }
    178.  
    179. QModelIndex MergeProxyModel::parent(const QModelIndex &proxyChild) const
    180. {
    181. return QModelIndex();
    182. }
    183.  
    184. int MergeProxyModel::getListNo(const QModelIndex &index) const
    185. {
    186. if (index.isValid())
    187. {
    188. int n = index.row();
    189. int offset = 0;
    190.  
    191. if (first_model) {
    192. if (n < first_model->rowCount()) return 0;
    193. offset += first_model->rowCount();
    194. }
    195.  
    196. if (second_model) {
    197. if (n-offset < second_model->rowCount()) return 1;
    198. offset += second_model->rowCount();
    199. }
    200.  
    201. if (third_model) {
    202. if (n-offset < third_model->rowCount()) return 2;
    203. }
    204. }
    205. return -1;
    206. }
    207.  
    208. int MergeProxyModel::getListPos(int n) const
    209. {
    210. if (first_model) {
    211. if (n < first_model->rowCount()) return n;
    212. n -= first_model->rowCount();
    213. }
    214.  
    215. if (second_model) {
    216. if (n < second_model->rowCount()) return n;
    217. n -= second_model->rowCount();
    218. }
    219.  
    220. if (third_model) {
    221. if (n < third_model->rowCount()) return n;
    222. }
    223. return -1;
    224. }
    225.  
    226. int MergeProxyModel::getListPos(const QModelIndex &index) const
    227. {
    228. if (index.isValid())
    229. {
    230. int n = index.row();
    231.  
    232. if (first_model) {
    233. if (n < first_model->rowCount()) return n;
    234. n -= first_model->rowCount();
    235. }
    236.  
    237. if (second_model) {
    238. if (n < second_model->rowCount()) return n;
    239. n -= second_model->rowCount();
    240. }
    241.  
    242. if (third_model) {
    243. if (n < third_model->rowCount()) return n;
    244. }
    245. }
    246. return -1;
    247. }
    248.  
    249. QAbstractTableModel* MergeProxyModel::sourceModel(const QModelIndex &index) const
    250. {
    251. switch (getListNo(index))
    252. {
    253. case 0: return first_model;
    254. case 1: return second_model;
    255. case 2: return third_model;
    256. default: return 0;
    257. }
    258. }
    259.  
    260. void MergeProxyModel::dataChangedSlot(const QModelIndex &topLeft, const QModelIndex &bottomRight)
    261. {
    262. const QModelIndex &proxyTopLeft = mapFromSource(topLeft);
    263. const QModelIndex &proxyBottomRight = mapFromSource(bottomRight);
    264. emit dataChanged(proxyTopLeft, proxyBottomRight);
    265. }
    266.  
    267. void MergeProxyModel::rowsAboutToBeInsertedSlot(const QModelIndex &parent, int first, int last)
    268. {
    269. beginInsertRows(parent, first, last);
    270. }
    271.  
    272. void MergeProxyModel::rowsInsertedSlot(const QModelIndex &parent, int first, int last)
    273. {
    274. endInsertRows();
    275. }
    276.  
    277. void MergeProxyModel::rowsAboutToBeRemovedSlot(const QModelIndex &parent, int first, int last)
    278. {
    279. QAbstractTableModel *smodel = qobject_cast<QAbstractTableModel *>(sender());
    280. const int offset = getOffset(smodel);
    281. beginRemoveRows(parent, first + offset, last + offset);
    282. }
    283.  
    284. void MergeProxyModel::rowsRemovedSlot(const QModelIndex &parent, int first, int last)
    285. {
    286. endRemoveRows();
    287. }
    288.  
    289. void MergeProxyModel::modelAboutToBeResetSlot()
    290. {
    291. beginResetModel();
    292. }
    293.  
    294. void MergeProxyModel::modelResetSlot()
    295. {
    296. endResetModel();
    297. }
    To copy to clipboard, switch view to plain text mode 

    Regards
    Sven

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Problem with ProxyModel merging models with QSortFilterProxyModel.

    Maybe running the model test suite can give you some input on what might be wrong: http://wiki.qt.io/Model_Test

    Cheers,
    _

  3. The following user says thank you to anda_skoa for this useful post:

    SvenA (14th April 2015)

  4. #3
    Join Date
    Jul 2014
    Location
    Germany
    Posts
    14
    Thanks
    3
    Qt products
    Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Problem with ProxyModel merging models with QSortFilterProxyModel.

    Thanks for the tip!
    I tried it, but it reported no obvious error.

    But I recognized the following:

    1. In the simple case (without using QSortFilterProxyModel), I saw this output from the ModelTest:

    ratbr QModelIndex(-1,-1,0x0,QObject(0x0) ) 1 0
    rr QModelIndex(-1,-1,0x0,QObject(0x0) ) 1 0


    2. In the problematic second case (with using QSortFilterProxyModel) these output are missing!

    Are the signals are not passed correctly?

    Any other ideas?

    Regards
    Sven

  5. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Problem with ProxyModel merging models with QSortFilterProxyModel.

    One thing I am seeing is that you are mapping the rows from the source model into the proxy when handling rowsAboutToBeRemoved but do not do so in the case of handling rowsAboutToBeInserted.

    Cheers,
    _

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

    SvenA (14th April 2015)

  7. #5
    Join Date
    Jul 2014
    Location
    Germany
    Posts
    14
    Thanks
    3
    Qt products
    Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Problem with ProxyModel merging models with QSortFilterProxyModel.

    Hmmm, that's correct. But this changes nothing. It's still does not work.
    If I understand it correctly, I have to do the mapping in both slots, but interestingly it works without this mapping the the rowsAboutToBeInserted() slot. I tried to remove the mapping from rowsAboutToBeRemoved() temporarily, but no change again.

    Because I think it is correct to map it in both functions, I added the missing mapping to rowsAboutToBeInserted() too.

    Now my rowsAboutToBeInsertedSlot() slot looks like:

    Qt Code:
    1. void MergeProxyModel::rowsAboutToBeInsertedSlot(const QModelIndex &parent, int first, int last)
    2. {
    3. QAbstractTableModel *smodel = qobject_cast<QAbstractTableModel *>(sender());
    4. const int offset = getOffset(smodel);
    5. beginInsertRows(parent, first + offset, last + offset);
    6. }
    To copy to clipboard, switch view to plain text mode 

    Would a small example application to test it, help?

  8. #6
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Problem with ProxyModel merging models with QSortFilterProxyModel.

    Quote Originally Posted by SvenA View Post
    Would a small example application to test it, help?
    Can't hurt. I don't have time today unfortunately, but someone else might.
    A small test application often also leads to discovering the bug yourself

    Cheers,
    _

  9. #7
    Join Date
    Jul 2014
    Location
    Germany
    Posts
    14
    Thanks
    3
    Qt products
    Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Problem with ProxyModel merging models with QSortFilterProxyModel.

    Thanks for your help!
    I found the bug.

    The model itself was right. The bug was in the source model which was used by my proxy model.
    In the beginRemoveRows() call I had an offset by one. Was hard to find.

    Nevertheless, thanks for your time and input!

    Cheers!

  10. #8
    Join Date
    Apr 2010
    Posts
    15
    Thanks
    4
    Thanked 1 Time in 1 Post
    Qt products
    Qt4
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: Problem with ProxyModel merging models with QSortFilterProxyModel.

    Could you please share the final version of MergeProxyModel class?

  11. #9
    Join Date
    Dec 2009
    Location
    New Orleans, Louisiana
    Posts
    791
    Thanks
    13
    Thanked 153 Times in 150 Posts
    Qt products
    Qt5
    Platforms
    MacOS X

    Default Re: Problem with ProxyModel merging models with QSortFilterProxyModel.

    Quote Originally Posted by anda_skoa View Post
    A small test application often also leads to discovering the bug yourself
    Dang @anda_skoa, you're good!
    I write the best type of code possible, code that I want to write, not code that someone tells me to write!

Similar Threads

  1. TableView ProxyModel problem
    By poporacer in forum Newbie
    Replies: 11
    Last Post: 23rd August 2011, 05:08
  2. Custom ProxyModel or Two Sync'd Models ?
    By SSurgnier in forum Qt Programming
    Replies: 2
    Last Post: 2nd August 2011, 19:44
  3. Replies: 9
    Last Post: 11th April 2011, 11:05
  4. ProxyModel problem
    By waynew in forum Qt Programming
    Replies: 1
    Last Post: 14th February 2010, 07:40
  5. QSortFilterProxyModel with multiple source models
    By hoshy in forum Qt Programming
    Replies: 1
    Last Post: 26th August 2009, 21:59

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.