Results 1 to 19 of 19

Thread: 2 subclasses of QSortFilterProxyModel behind each other.

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Join Date
    Apr 2013
    Posts
    27
    Thanks
    9
    Qt products
    Qt5
    Platforms
    Windows

    Default 2 subclasses of QSortFilterProxyModel behind each other.

    Hi,

    currently I am using 2 subclasses of QSortFilterProxyModel

    Some of the data needs to be editable, some data not. So i made a QSortFilterProxyModelDisableEdit that does only remove the editable flag from a column.
    On some tables I need an extra column that is not in the database. There for i used a ProxyModel described in this threat.

    The objects are connected in the following way:
    Qt Code:
    1. QSqlTableModel* DataTable = new QSqlTableModel();
    2.  
    3. SqlProxyModelAddColumn* AddColumn = new SqlProxyModelAddColumn();
    4. AddColumn->setSourceModel(DataTable);
    5.  
    6. SqlProxyModelDisalbeEdit* DisableEdit = new SqlProxyModelDisalbeEdit();
    7. DisableEdit->setSourceModel(AddColumn);
    To copy to clipboard, switch view to plain text mode 


    Noting is set in DisableEdit so it acts like a QSqlProxyModel,
    AddColumn implementation is descriped in ProxyModel

    When i try to edit some value for the DataTable all works fine.
    but when i try to edit a value for the added column set data is never called. So i think there is something going wrong with the indexes, but i cant find the problem.

    Any help is appreciated, thanks.

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,368
    Thanks
    3
    Thanked 5,018 Times in 4,794 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: 2 subclasses of QSortFilterProxyModel behind each other.

    Why don't you subclass QIdentityProxyModel instead?
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


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

    Delphi (29th May 2013)

  4. #3
    Join Date
    Apr 2013
    Posts
    27
    Thanks
    9
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: 2 subclasses of QSortFilterProxyModel behind each other.

    I needed to sort something as well on that place,

    I found the root of the problem: ProxyModel.

    ProxyModel with a QsqlQueryModel as source works.
    When using a QSqlTablemodel or a other ProxyModel it stops working.

    has someome a neat solution for the index problem from ProxyModel

  5. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,368
    Thanks
    3
    Thanked 5,018 Times in 4,794 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: 2 subclasses of QSortFilterProxyModel behind each other.

    Don't do 10 tasks at once. Have one proxy for modifying item flags, another for adding/removing columns and one more for sorting. Assemble them all to get the final solution.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  6. #5
    Join Date
    Apr 2013
    Posts
    27
    Thanks
    9
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: 2 subclasses of QSortFilterProxyModel behind each other.

    I we do not quite understand each other,
    that's what I try to do exactly.

    a model that changes flags and can filter
    and a model that adds a column.


    the problem lies with the ProxyModel

    when it is having a QsqlQueryModel as source model it works correctly.
    when it is having a QsqlTableModel or ProxyModel as sourceModel it is displaying correct but setdata never gets called.
    the flags are working correctly.

    So i think it is caused by the Indexes. As previous post said.
    Is there someone that can help me get a neat solution for the index problem in ProxyModel

    Any help is appreciated, thanks.

  7. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,368
    Thanks
    3
    Thanked 5,018 Times in 4,794 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: 2 subclasses of QSortFilterProxyModel behind each other.

    Quote Originally Posted by Delphi View Post
    the problem lies with the ProxyModel
    Get rid of that model and use a subclass of QIdentityProxyModel as advised. There you don't need to reimplement mapFromSource() and mapToSource() which are the source of your trouble.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  8. #7
    Join Date
    Apr 2013
    Posts
    27
    Thanks
    9
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: 2 subclasses of QSortFilterProxyModel behind each other.

    Why would be a QIdentityProxyModel easier?

    I still will have problems with an index.
    because (set)data works with them. Which index will have the extra column? It still needs to be maped by maped to and from source.

    I also think that ToSource and FromSource need to be reimplemented, because QIdentityProxyModel works with 1 source model. My subclass needs to work with 1 source model and an extra column.

  9. #8
    Join Date
    Mar 2011
    Location
    Hyderabad, India
    Posts
    1,882
    Thanks
    3
    Thanked 453 Times in 435 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows
    Wiki edits
    15

    Default Re: 2 subclasses of QSortFilterProxyModel behind each other.

    Ok, I looked at the ProxyModel in the other post, it looks fine and will works for all QStandardItemModel, QSqlTableModel and QSqlQueryModel (only first column is editable, and other columns are redable)

    Here is an example using the similar ProxyModel (with few changes), and it shows all combinations simultaneously.
    ProxyModel_All.jpg
    Qt Code:
    1. #include <QtGui>
    2. #include <QtSql>
    3. #include <QtWidgets>
    4. #include <QApplication>
    5.  
    6. // Add one extra column at the begining
    7. class ProxyModel : public QAbstractProxyModel
    8. {
    9. Q_OBJECT
    10. public:
    11. explicit ProxyModel(QObject * parent = 0)
    12. , mData()
    13. {
    14. connect(this, SIGNAL(sourceModelChanged()), SLOT(slotSourceModelChanged()));
    15. }
    16.  
    17. protected slots:
    18. void slotSourceModelChanged(void)
    19. {
    20. disconnect(this, SLOT(slotDataChanged(QModelIndex,QModelIndex)));
    21. connect(sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(slotDataChanged(QModelIndex,QModelIndex)));
    22. }
    23.  
    24. void slotDataChanged(const QModelIndex first, QModelIndex last)
    25. {
    26. emit dataChanged(mapFromSource(first), mapFromSource(last));
    27. }
    28.  
    29. QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const
    30. {
    31. if(!parent.isValid())
    32. return createIndex(row, column);
    33. return QModelIndex();
    34. }
    35.  
    36. QModelIndex parent(const QModelIndex & /*child*/) const
    37. {
    38. return QModelIndex();
    39. }
    40.  
    41. int rowCount(const QModelIndex & parent = QModelIndex()) const
    42. {
    43. if(!parent.isValid())
    44. return sourceModel()->rowCount(QModelIndex());
    45. return 0;
    46. }
    47.  
    48. int columnCount(const QModelIndex & parent = QModelIndex()) const
    49. {
    50. if(!parent.isValid())
    51. return sourceModel()->columnCount() + 1;
    52. return 0;
    53. }
    54.  
    55. QModelIndex mapToSource(const QModelIndex & proxyIndex) const
    56. {
    57. if(!proxyIndex.isValid())
    58. return QModelIndex();
    59.  
    60. if(proxyIndex.column() == 0)
    61. return createIndex(proxyIndex.row(), -1, (quintptr)-1); //<<<<<<<<<<<<<<<<<<<<<< This is a workaround. Note it is weird to create a index of proxy model and return as if it were an index of sourceModel(), it is not a common way but will work (at-least in this case)
    62.  
    63. return sourceModel()->index(proxyIndex.row(), proxyIndex.column() - 1);
    64. }
    65.  
    66. QModelIndex mapFromSource(const QModelIndex & sourceIndex) const
    67. {
    68. if(!sourceIndex.isValid())
    69. {
    70. if((sourceIndex.row() > -1) and //<<<<<<<<<<<<<<<<<<<<<< This is a workaround.
    71. (sourceIndex.column() == -1) and
    72. (sourceIndex.internalId() == (quintptr)-1) )
    73. return index(sourceIndex.row(), 0);
    74. else
    75. return QModelIndex();
    76. }
    77.  
    78. return index(sourceIndex.row(), sourceIndex.column() + 1);
    79. }
    80.  
    81. QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const
    82. {
    83. if(orientation == Qt::Horizontal)
    84. {
    85. if(section == 0)
    86. return QString("Proxy Column");
    87. return sourceModel()->headerData(section - 1, orientation, role);
    88. }
    89. return sourceModel()->headerData(section, orientation, role);
    90. }
    91.  
    92. QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
    93. {
    94. if(index.column() == 0)
    95. {
    96. if((role == Qt::DisplayRole) or (role == Qt::EditRole))
    97. if(index.row() < mData.size())
    98. return mData.at(index.row());
    99. return QVariant();
    100. }
    101.  
    102. return sourceModel()->data(mapToSource(index), role);
    103. }
    104.  
    105. bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole)
    106. {
    107. if(index.column() == 0)
    108. {
    109. if((role == Qt::DisplayRole) or (role == Qt::EditRole))
    110. {
    111. while((index.row() + 1) > mData.size())
    112. mData.append(QString());
    113. mData[index.row()] = value.toString();
    114. emit dataChanged(index, index);
    115. return true;
    116. }
    117. return false;
    118. }
    119.  
    120. return sourceModel()->setData(mapToSource(index), value, role);
    121. }
    122.  
    123. Qt::ItemFlags flags(const QModelIndex & index) const
    124. {
    125. if(index.column() == 0)
    126. return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
    127.  
    128. return sourceModel()->flags(mapToSource(index));
    129. }
    130.  
    131. private:
    132. QStringList mData;
    133. };
    134.  
    135. class Widget : public QWidget
    136. {
    137. public:
    138. explicit Widget(QWidget * parent = 0)
    139. : QWidget(parent)
    140. , mGridLayout(new QGridLayout(this))
    141. { }
    142.  
    143. void addWidget(int row, int col, QWidget * widget, const QString & title)
    144. {
    145. mGridLayout->addWidget(new QLabel(title), row * 2, col, 1, 1, Qt::AlignCenter);
    146. mGridLayout->addWidget(widget, (row * 2) + 1, col, 1, 1);
    147. }
    148.  
    149. private:
    150. QGridLayout * mGridLayout;
    151. };
    152.  
    153. const QString TableName = "Locations";
    154. const QString Column1 = "Country";
    155. const QString Column2 = "Location";
    156. const QStringList countries = QStringList() << "Norway" << "Australia" << "USA" << "China" << "Germany";
    157. const QStringList locations = QStringList() << "Oslo" << "Brisbane" << "Palo Alto" << "Beijing" << "Berlin";
    158.  
    159. void addSampleTableData(QAbstractItemModel * model)
    160. {
    161. if(model->rowCount() == 0)
    162. model->insertRows(0, countries.size());
    163.  
    164. if(model->columnCount() == 0)
    165. model->insertColumns(0, 2);
    166.  
    167. for(int r = 0; r < model->rowCount(); r++)
    168. {
    169. model->setData(model->index(r, 0), countries.at(r));
    170. model->setData(model->index(r, 1), locations.at(r));
    171. }
    172.  
    173. model->setHeaderData(0, Qt::Horizontal, Column1);
    174. model->setHeaderData(1, Qt::Horizontal, Column2);
    175. model->submit();
    176. }
    177.  
    178. QTableView * createView(QAbstractItemModel * model)
    179. {
    180. QTableView * tableView = new QTableView;
    181. tableView->setModel(model);
    182. tableView->setEditTriggers(QTableView::DoubleClicked);
    183. tableView->resizeColumnsToContents();
    184. return tableView;
    185. }
    186.  
    187. int main(int argc, char *argv[])
    188. {
    189. QApplication app(argc, argv);
    190.  
    191. const QString fileName = "Qt.sqlite";
    192. QDir dir;
    193. if(dir.exists(fileName))
    194. if(!dir.remove(fileName))
    195. {
    196. qDebug() << "Unable to delete old Database file " << fileName;
    197. return -1;
    198. }
    199.  
    200. QSqlDatabase sqlDatabase = QSqlDatabase::addDatabase("QSQLITE");
    201. sqlDatabase.setDatabaseName(fileName);
    202.  
    203. if(!sqlDatabase.open())
    204. {
    205. qDebug() << sqlDatabase.lastError().text();
    206. return -2;
    207. }
    208.  
    209. QStandardItemModel standardItemModel;
    210. addSampleTableData(&standardItemModel);
    211.  
    212. QSqlQuery sqlQuery(sqlDatabase);
    213. if(!sqlQuery.exec(QString("CREATE TABLE %1 ( %2 TEXT, %3 TEXT)").arg(TableName).arg(Column1).arg(Column2)))
    214. {
    215. qDebug() << sqlQuery.lastError().text();
    216. return -3;
    217. }
    218.  
    219. QSqlTableModel sqlTableModel(0, sqlDatabase);
    220. sqlTableModel.setTable("Locations");
    221. sqlTableModel.setEditStrategy(QSqlTableModel::OnManualSubmit);
    222. sqlTableModel.select();
    223. addSampleTableData(&sqlTableModel);
    224. sqlTableModel.submitAll();
    225.  
    226. QSqlQueryModel sqlQueryModel;
    227. sqlQueryModel.setQuery(QString("SELECT %2, %3 FROM %1").arg(TableName).arg(Column1).arg(Column2), sqlDatabase);
    228.  
    229. ProxyModel standardItemModelProxy;
    230. standardItemModelProxy.setSourceModel(&standardItemModel);
    231.  
    232. ProxyModel sqlTableModelProxy;
    233. sqlTableModelProxy.setSourceModel(&sqlTableModel);
    234.  
    235. ProxyModel sqlQueryModelProxy;
    236. sqlQueryModelProxy.setSourceModel(&sqlQueryModel);
    237.  
    238. Widget widget;
    239.  
    240. widget.addWidget(0, 0, createView(&standardItemModel), "QStandardItemModel View");
    241. widget.addWidget(0, 1, createView(&sqlTableModel), "QSqlTableModel View");
    242. widget.addWidget(0, 2, createView(&sqlQueryModel), "QSqlQueryModel View");
    243.  
    244. widget.addWidget(1, 0, createView(&standardItemModelProxy), "QStandardItemModel Proxy View");
    245. widget.addWidget(1, 1, createView(&sqlTableModelProxy), "QSqlTableModel Proxy View");
    246. widget.addWidget(1, 2, createView(&sqlQueryModelProxy), "QSqlQueryModel Proxy View");
    247.  
    248. widget.showMaximized();
    249.  
    250. return app.exec();
    251. }
    252.  
    253. #include "main.moc"
    To copy to clipboard, switch view to plain text mode 
    When you know how to do it then you may do it wrong.
    When you don't know how to do it then it is not that you may do it wrong but you may not do it right.

  10. The following user says thank you to Santosh Reddy for this useful post:

    Delphi (29th May 2013)

  11. #9
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,368
    Thanks
    3
    Thanked 5,018 Times in 4,794 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: 2 subclasses of QSortFilterProxyModel behind each other.

    Quote Originally Posted by Santosh Reddy View Post
    Ok, I looked at the ProxyModel in the other post, it looks fine and will works for all QStandardItemModel, QSqlTableModel and QSqlQueryModel (only first column is editable, and other columns are redable)
    I think it won't work with QSortFilterProxyModel because the latter requires proper internal pointers to be present which will not be the case with your implementation. The proper approach is to use QIdentityProxyModel which is able to return proper internal pointers for the source model.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  12. #10
    Join Date
    Mar 2011
    Location
    Hyderabad, India
    Posts
    1,882
    Thanks
    3
    Thanked 453 Times in 435 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Unix/X11 Windows
    Wiki edits
    15

    Default Re: 2 subclasses of QSortFilterProxyModel behind each other.

    Quote Originally Posted by wysota
    Quote Originally Posted by Santosh Reddy
    Ok, I looked at the ProxyModel in the other post, it looks fine and will works for all QStandardItemModel, QSqlTableModel and QSqlQueryModel (only first column is editable, and other columns are redable)
    I think it won't work with QSortFilterProxyModel because the latter requires proper internal pointers to be present which will not be the case with your implementation. The proper approach is to use QIdentityProxyModel which is able to return proper internal pointers for the source model.
    I don't think using QIdentityProxyModel will help in any way adding an extra column to the source model. Why do you think it is better than QAbstractItemModel?

    As I see it, as far as adding a virtual column is concerned both QIdentityProxyModel and QAbstractItemModel just the same, you will need to implement index(), columnCount(), mapToSource(), mapFromSource(), data() and setData(). I don't think you can rely on the QIdentityProxyModel's mapToSource() and mapFromSource() as they not aware of the extra column we added, and infact will crash the program as view will try to access the index for the column which does not exist in QIdentityProxyModel nor in sourceModel().

    I agree that the proposed ProxyModel approach will not work when installed on QSortFilterProxyModel, but the other way round will work.
    Ok: QStandardItemModel->ProxyModel->QIdentityModel->QSortFilterProxyModel->QTreeView
    Ok: QSqlTableModel->ProxyModel->QIdentityModel->QSortFilterProxyModel->QTreeView
    Ok: QSqlQueryModel->ProxyModel->QIdentityModel->QSortFilterProxyModel->QTreeView
    FAIL: *->QSortFilterProxyModel->ProxyModel->*
    FAIL: *->QIdentityModel->ProxyModel->*

    Bottom line is that ProxyModel cannot have source model as QSortFilterProxyModel / QIdentityModel, but QSortFilterProxyModel / QIdentityModel can have ProxyModel as source model.

    Here is an example and source.
    ProxyModel_4_Level.jpg
    Attached Files Attached Files
    When you know how to do it then you may do it wrong.
    When you don't know how to do it then it is not that you may do it wrong but you may not do it right.

  13. The following user says thank you to Santosh Reddy for this useful post:

    Delphi (29th May 2013)

  14. #11
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,368
    Thanks
    3
    Thanked 5,018 Times in 4,794 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: 2 subclasses of QSortFilterProxyModel behind each other.

    Quote Originally Posted by Santosh Reddy View Post
    As I see it, as far as adding a virtual column is concerned both QIdentityProxyModel and QAbstractItemModel just the same, you will need to implement index(), columnCount(), mapToSource(), mapFromSource(), data() and setData().
    The point is your ProxyModel implementation returns invalid indices for the source model. QIdentityProxyModel is made friend of QAbstractItemModel which lets it call createIndex() on its behalf.

    I don't think you can rely on the QIdentityProxyModel's mapToSource() and mapFromSource() as they not aware of the extra column we added
    You can extend the existing implementation. The point is what I wrote above -- QIdentityProxyModel is friend of QAbstractItemModel so it can return proper indexes for the source model. QAbstractProxyModel can't thus a subclass of QAbstractProxyModel will not either but a subclass of QIdentityProxyModel will.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


Similar Threads

  1. Replies: 3
    Last Post: 12th October 2012, 22:42
  2. Creating signals in subclasses
    By Cotlone in forum Newbie
    Replies: 2
    Last Post: 22nd June 2010, 00:16
  3. Why slots in QThread subclasses are unsafe?
    By AlphaWolf in forum Qt Programming
    Replies: 8
    Last Post: 30th May 2010, 15:39
  4. Error creating subclasses
    By agerlach in forum Qt Programming
    Replies: 2
    Last Post: 25th May 2010, 13:49
  5. declaring subclasses in C++
    By Paat in forum Newbie
    Replies: 4
    Last Post: 23rd October 2009, 08:40

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
  •  
Qt is a trademark of The Qt Company.