Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Add read-only diff column to QSortFilterProxyModel that calculates on model data
Forum Updated to NodeBB v4.3 + New Features

Add read-only diff column to QSortFilterProxyModel that calculates on model data

Scheduled Pinned Locked Moved General and Desktop
25 Posts 4 Posters 12.6k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    swivelhead
    wrote on last edited by
    #1

    I'm trying to use an instance of QSortFilterProxyModel to add a read-only column that takes a difference of two columns existing in the source model.

    The code that I am using to add the column attempt to alter the its value:
    @
    balanceProxyModel->insertColumns(6, 1);
    balanceProxyModel->setHeaderData(6, Qt::Horizontal, QObject::tr("Difference"));

    for (int i = 0; i < balanceProxyModel->rowCount(); ++i) {
    float budget = balanceProxyModel->index(i, 4).data().toFloat();
    float actual = balanceProxyModel->index(i, 5).data().toFloat();
    float difference = budget - actual;

    QModelIndex diffModelIndex = balanceProxyModel->index(i, 6);

    if (balanceProxyModel->setData(diffModelIndex, QVariant(difference)) == false) {
    qDebug() << ATLINE << ":" << "diff not added!!!";
    }
    ...
    }
    @

    Unfortunately in the above example, it streams "diff not added!!!" for every iteration. The diff column shows up, but it is empty.

    I have asked at "StackOverflow.com":http://stackoverflow.com/questions/7314128/why-would-qsortfilterproxymodel-setdata-return-false but the only thing we could find is that the flags for that index are not set to Qt::ItemIsEditable.

    The only thing is, I don't want it to be editable by the user, I just want to edit it programatically.

    It has been suggested that I should subclass QSortFilterProxyModel and set the column there. I know how to subclass Qt class, minimally (that is without changes), but not how to reimplement setData to allow an update.

    Thanks.

    1 Reply Last reply
    0
    • O Offline
      O Offline
      octal
      wrote on last edited by
      #2

      If you don't want your item to be editable, why don't you just unset the Qt::ItemIsEditable flag ?

      I don't think you need a ProxyModel here, for such an easy thing. What is your source model ?

      1 Reply Last reply
      0
      • S Offline
        S Offline
        swivelhead
        wrote on last edited by
        #3

        The Qt::ItemIsEditable is unset. What I meant was I don't think my problem stems from the fact that the Qt::ItemIsEditable is unset for that index.

        My source model is QSqlTableModel. It is attached to a SQLite database. The diff column that I wish to create will be a difference between to fields on that table. I see it as redundant data to have a difference field in my database table since it can (maybe) be readily created by the interface. Also, if I did create the field in the database table, I would have to have the view refresh after every change to the model (and I don't know how to do that yet.)

        The main issue, though, is when I try to setData on the proxy model, I get false. How should I set the column values on the proxy model?

        Thanks.

        1 Reply Last reply
        0
        • S Offline
          S Offline
          swivelhead
          wrote on last edited by
          #4

          Since it was suggested prior to posting here, I set the Qt::ItemIsEditable flag via instancing the following class instead of QSortFilterProxyModel:

          @
          #include <QSortFilterProxyModel>

          class SortFilterProxyModelWithMetaColumns : public QSortFilterProxyModel {
          Q_OBJECT
          public:
          SortFilterProxyModelWithMetaColumns(QObject *parent = 0)
          : QSortFilterProxyModel(parent) {
          }

          Qt::ItemFlags flags(const QModelIndex &index) const {
          Qt::ItemFlags itemFlags =
          (QSortFilterProxyModel::flags(index) | Qt::ItemIsEditable);

          return itemFlags;
          

          }
          };
          @

          However, this did not affect the return results from the calls to setData(). It still returns false.

          1 Reply Last reply
          0
          • O Offline
            O Offline
            octal
            wrote on last edited by
            #5

            Well, ok.

            Since QSortFilterProxyModel just usually forwards the calls to the SourceModel, I think you will have to handle it yourself in your custom proxy model. What I would do is to reimplement the following methods in my ProxyModel :

            • flags() for the additional column ;
            • columnCount() to "add" your column ;
            • data() to retrieve the additionnal data ;
            • headerData()

            Let's give it a try :

            @
            class SortFilterProxyModelWithMetaColumns : public QSortFilterProxyModel {
            Q_OBJECT

            static const int DifferenceColumn = 6;

            public:
            SortFilterProxyModelWithMetaColumns(QObject *parent = 0)
            : QSortFilterProxyModel(parent) {
            }

            int columnCount(const QModelIndex &parent = QModelIndex())
            {
            return QSortFilterProxyModel::columnCount(parent) + 1;
            }

            Qt::ItemFlags flags(const QModelIndex &index)
            {
            if (index.column() == DifferenceColumn)
            {
            return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
            }
            return QSortFilterProxyModel::flags(index);
            }

            QVariant data(const QModelIndex &index, int role)
            {
            if (index.column() == DifferenceColumn && role == Qt::DisplayRole)
            {
            const QSqlTableModel *model = qobject_cast<const QSqlTableModel *>(sourceModel());
            Q_ASSERT(model);
            const int firstColumnIndex = model->fieldIndex("MyFirstColumn");
            const int secondColumnIndex = model->fieldIndex("MySecondColumn");
            const QModelIndex dataIndex1 = this->index(index.row(), firstColumnIndex);
            const QModelIndex dataIndex2 = this->index(index.row(), secondColumnIndex);

                   const QVariant data1 = mapToSource(dataIndex1).data(Qt::DisplayRole);
                   const QVariant data2 = mapToSource(dataIndex2).data(Qt::DisplayRole);
            
                   const int difference = data1.toInt() - data2.toInt();
            
                   return difference;
            }
            
            return QSortFilterProxyModel::data(index, role);
            

            }

            QVariant headerData(int section, Qt::Orientation orientation, int role)
            {
            if (orientation == Qt::Horizontal && section == DifferenceColumn)
            {
            return tr("Difference");
            }

              return QSortFilterProxyModel::data(section, orientation, role);
            

            }
            };
            @

            However, I didn't try it, so you might have to adjust it.

            1 Reply Last reply
            0
            • S Offline
              S Offline
              swivelhead
              wrote on last edited by
              #6

              Wow, thanks for all of the effort, octal! I'm still sketchy on subclassing Qt classes as far as what works and what to pass forward.

              I tried the above, but didn't see any difference in the Diff column (its still blank.) I tried commenting out the insertColumns line, but that only removed the column from the view.

              Also, the Diff column has no header displayed, which is weird since I entered your headerData function above.

              Another weird thing is that I am counting the number of fields and when I include the insertColumns and your code for the subclass, it actually shows 8 columns numbered (0 - 7.)

              Is this a common task to ask of the Qt (adding a meta-field that isn't really in the source table?)

              Thanks again.

              1 Reply Last reply
              0
              • O Offline
                O Offline
                octal
                wrote on last edited by
                #7

                Damn, I forgot one important thing : the index.

                Because of that, I think it would actually be better to directly subclass QSqlTableModel. I'm a little bit tired, so I'll let you with this code sample :

                @
                class MyCustomModel : public QSqlTableModel {

                static const int DifferenceColumn = 2;

                Q_OBJECT

                public:
                MyCustomModel(QObject *parent = 0)
                : QSqlTableModel(parent) {
                setTable("datas");
                select();
                }

                int columnCount(const QModelIndex &parent) const
                {
                const int count = QSqlTableModel::columnCount(parent);
                return count + 1;
                }

                Qt::ItemFlags flags(const QModelIndex &index) const
                {
                if (index.column() == DifferenceColumn)
                {
                return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
                }
                return QSqlTableModel::flags(index);
                }

                QVariant data(const QModelIndex &index, int role) const
                {
                if (index.column() == DifferenceColumn && role == Qt::DisplayRole)
                {
                const int firstColumnIndex = fieldIndex("data1");
                const int secondColumnIndex = fieldIndex("data2");
                const QModelIndex dataIndex1 = this->index(index.row(), firstColumnIndex);
                const QModelIndex dataIndex2 = this->index(index.row(), secondColumnIndex);

                       const QVariant data1 = dataIndex1.data(Qt::DisplayRole);
                       const QVariant data2 = dataIndex2.data(Qt::DisplayRole);
                
                       const int difference = data1.toInt() - data2.toInt();
                
                       return difference;
                    }
                
                    return QSqlTableModel::data(index, role);
                

                }

                QVariant headerData(int section, Qt::Orientation orientation, int role) const
                {
                if (orientation == Qt::Horizontal && section == DifferenceColumn)
                {
                return tr("Difference");
                }

                  return QSqlTableModel::headerData(section, orientation, role);
                

                }
                };

                Dialog::Dialog(QWidget *parent) :
                QDialog(parent)
                {
                QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
                db.setDatabaseName(":memory:");
                db.open();

                QSqlQuery query;
                query.exec&#40;"create table datas (data1 int, data2 int&#41;");
                
                for (int i = 0; i < 10; ++i)
                {
                    query.prepare("INSERT INTO datas VALUES (:data1, :data2)");
                    query.bindValue(":data1", qrand() % 100);
                    query.bindValue(":data2", qrand() % 100);
                    query.exec&#40;&#41;;
                }
                
                
                QHBoxLayout *layout = new QHBoxLayout(this);
                QTableView *view = new QTableView;
                
                MyCustomModel *model = new MyCustomModel(this);
                view->setModel(model);
                
                layout->addWidget(view);
                setLayout(layout);
                

                }
                @

                When I was subclassing QSortFilterProxyModel, all the indexes refering to the additional column we created were invalid.

                1 Reply Last reply
                0
                • S Offline
                  S Offline
                  swivelhead
                  wrote on last edited by
                  #8

                  Awesome!

                  Thanks so much for working that out, octal. I just finished getting the example above working and it does so perfectly!

                  Next, I will give it a shot with the real code.

                  Thanks again for your help.

                  1 Reply Last reply
                  0
                  • A Offline
                    A Offline
                    andre
                    wrote on last edited by
                    #9

                    Still, as a side note, it is very much possible to do this using a proxy model. That makes the code reusable with any underlying model that you may wish to use with it.

                    Note: /me is a fan of proxy models :-)

                    1 Reply Last reply
                    0
                    • S Offline
                      S Offline
                      swivelhead
                      wrote on last edited by
                      #10

                      @Andre, thanks for the tip!

                      I've almost gotten it to work using the proxy model.

                      The weird thing is, that the new column shows and displays the correct field name and value (the difference,) but after I update one of the values in the table the difference column disappears.

                      Any ideas? It removes the column if I use either QSortFilterProxyModel or my subclass.

                      Thanks.

                      1 Reply Last reply
                      0
                      • A Offline
                        A Offline
                        andre
                        wrote on last edited by
                        #11

                        Using the modeltest class to test your (proxy) model is usually a great help to find the problems that underly symptoms like these. Did you use it?

                        1 Reply Last reply
                        0
                        • S Offline
                          S Offline
                          swivelhead
                          wrote on last edited by
                          #12

                          No, I didn't. I have not heard of that before.

                          I just found it mentioned "here":http://developer.qt.nokia.com/wiki/Model_Test, though:

                          I'll give that a shot and see what happens.

                          Thanks!

                          1 Reply Last reply
                          0
                          • S Offline
                            S Offline
                            swivelhead
                            wrote on last edited by
                            #13

                            Hmm. Do I have to sign up with gitorious to get these files?

                            I can click in them and copy out the text and create my own files, since there's not many. For some reason it doesn't give me the choice to just download them in Opera on Linux. This is the first time I've dealt with files on a gitorious (or git) server.

                            1 Reply Last reply
                            0
                            • S Offline
                              S Offline
                              swivelhead
                              wrote on last edited by
                              #14

                              I cut and pasted the modeltest source files, this morning. I'll get to test with them tonight, hopefully.

                              Update --20110913_1249--
                              I just figured out the download thing. I just need to click on the "Raw blob data". Heh, wish I would have seen this sooner. I'm an idiot.

                              Also, the link to "Model Test":http://developer.qt.nokia.com/wiki/Model_Test says to include the modeltest.pri file inside of my project's pro file, but there was no .pri file in the repository. I'm going to assume that means to rename modeltest.pro to modeltest.pri and include that.

                              1 Reply Last reply
                              0
                              • O Offline
                                O Offline
                                octal
                                wrote on last edited by
                                #15

                                bq. I’m going to assume that means to rename modeltest.pro to modeltest.pri and include that.

                                Personally, that's what I do when I use the ModelTest. Now have fun debugging your model :)

                                1 Reply Last reply
                                0
                                • S Offline
                                  S Offline
                                  swivelhead
                                  wrote on last edited by
                                  #16

                                  Will do. Thanks!!!

                                  1 Reply Last reply
                                  0
                                  • S Offline
                                    S Offline
                                    swivelhead
                                    wrote on last edited by
                                    #17

                                    Well, I didn't have any asserts occur with ModelTest, but maybe I used it wrong.

                                    To use it I:

                                    copied the files to a seperate folder.

                                    modified the .pro to be .pri

                                    included the new .pri file into my .pro file via:

                                    @include(/seperate-folder/modeltest.pri)@

                                    removed all sources and headers from the .pri file except modeltest.h and modeltest.cpp

                                    added a line for QT += test

                                    comment out the load(qttest_p4) line because of the following warning:

                                    @WARNING: /home/jetimms/code/Qt/cashflow_01.nix/modeltest.pri:1: Unable to find file for inclusion qttest_p4@

                                    in the same code that I instantiated my proxy model, I included modeltest.h and added the following line just after creating its instance:

                                    @new ModelTest(balanceProxyModel, this);@

                                    I did see these debug messages, but I don't recognize them from my stuff. They occur when a change to the view is entered.
                                    @
                                    Debug: ratbr QModelIndex(-1,-1,0x0,QObject(0x0) ) 0 128
                                    Debug: rr QModelIndex(-1,-1,0x0,QObject(0x0) ) 0 128
                                    @

                                    1 Reply Last reply
                                    0
                                    • S Offline
                                      S Offline
                                      swivelhead
                                      wrote on last edited by
                                      #18

                                      I had so many Debug messages coming through, that I didn't realize that the two mentioned above were not one of mine :)

                                      The source file modeltest.cpp clued me into what those messages meant:
                                      ratbr - rowsAboutToBeRemoved() pushes onto a QStack the index info in rows 0 through 128 that are about to be removed
                                      rr - rowsRemoved() compares rows 0 through 128 that were removed to those on top of QStack and asserts if they differ

                                      What is puzzling to me is why it would be removing any rows, much less 129 of them. By the way, my table contains 129 records.

                                      The view still shows all of my rows so none are missing after the debug message. This removal could be a function of the sort that defaults to the first column, though. I'll try the ModelTools using QSortFilterProxyFilter and see what happens.

                                      However, the two functions mentioned above only look at only column 0 (see the following snippet from rowsAboutToBeRemoved()):

                                      The below is with start = 0, end = 128, and Changing is a simple structure
                                      @
                                      Changing c;
                                      c.parent = parent;
                                      c.oldSize = model->rowCount ( parent );
                                      c.last = model->data ( model->index ( start - 1, 0, parent ) );
                                      c.next = model->data ( model->index ( end + 1, 0, parent ) );
                                      @

                                      This is strange because my difference column (the one I added) is the 7th column of the view.

                                      Perhaps the QModelIndex that is referred to is just an index for my difference column and not the entire model's scope. Maybe its the proxy model's scope since it would only contain a column 0, the difference column. It is the model that I added the column to, after all.

                                      One more mystery is that the ratbr and rr messages occur after each edit submission, not just the first one.

                                      I'll keep digging.

                                      1 Reply Last reply
                                      0
                                      • S Offline
                                        S Offline
                                        swivelhead
                                        wrote on last edited by
                                        #19

                                        octal,
                                        While trying to debug my model, I move line 29 and 30 from your first example:

                                        @
                                        const QSqlTableModel *model = qobject_cast<const QSqlTableModel *>(sourceModel());
                                        Q_ASSERT(model);
                                        @

                                        to just above the if block that it was in and got an assert on the model being NULL. I've tried in the other functions provided to copy the same code, but the only other one it works in is headerData.

                                        Any idea of why that happens? Just curious because I saw no reason.

                                        1 Reply Last reply
                                        0
                                        • S Offline
                                          S Offline
                                          swivelhead
                                          wrote on last edited by
                                          #20

                                          I also found "this bug report":https://bugreports.qt.nokia.com/browse/QTBUG-12626 which wasn't fixed until 4.7.2. The report specifies QSqlTableModel, but says nothing about QSortFilterProxyModel.

                                          I suspect that its related, anyhow, to my disappearing columns.

                                          Mepis 11 (my Linux distro of choice) only has 4.7.1-2 of libqtcore4 available in its repository. I'm thinking about downloading the source and configuring the libraries myself.

                                          [EDIT: fixed link, Volker]

                                          1 Reply Last reply
                                          0

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved