Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QStyledItemDelegate::createEditor() not getting called



  • In my scenario, QStyledItemDelegate::createEditor() is not getting called when I double click on certain items in my QTableView. I am looking for a hint as to why not, please....

    Life is not simple, and nor is my scenario. I describe it in abstract, code would be large to reproduce. I am going to be brief as what I need is a clue as to where the issue lies.

    I have a proxy model derived from QIdentityProxyModel. It is a "semi-identity" model! Some columns are mapped to the underlying source model, but some columns are "internal" to my proxy model.

    I have done all the necessary overrides etc. Overridden methods include:

    columnCount();
    rowCount();
    data();
    setData();
    flags();
    mapFromSource();
    mapToSource();
    index();
    

    I have a QTreeView whose model is the proxy model. It has a setItemDelegate() to my delegate derived from QStyledItemDelegate. The delegate is for editing with spin boxes. It is taken from the Qt example for this. Overridden methods include:

    createEditor();
    setEditorData();
    setModelData();
    updateEditorGeometry();
    

    Now, everything works. All my mappings etc, correctly pick out indexes from the underlying source model or internal to the proxy model, as appropriate. data() picks out the right data, setData() sets the right data. And so on.

    If I double-click to edit a cell in a column mapped to the underlying source model it goes into spinbox edit mode on the correct thing, reads/writes it correctly, etc.

    Except only.... My problem is that if I double-click to edit a cell in a column mapped to the internal proxy model's data, nothing happens :( The subclassed QStyledItemDelegate::createEditor() simply does not get called, and that is where I am stuck.

    Now, you will immediately say, "Ah ha, that's because your flags must be wrong, it doesn't know it's editable". But for my proxy-internal editable columns I have:

    return (Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled);
    

    This is what the flags for underlying source model return, I checked that. Note the Qt::ItemIsEditable. That code is indeed hit, and it shows the column in non-edit mode as not grayed for read-only, which it would if I removed that.

    So I need a clue: given that flags() is returning Qt::ItemIsEditable for an item, what else could be wrong/am I missing which would cause QStyledItemDeletegate::createEditor() not to be called??



  • For the sake of completeness, I present as the solution a complete, standalone, working code for this situation. For simplicity, I have removed any use of a QStyledItemDelegate for QTreeView::setItemDelegate() as this is not required.

    It shows the minimal needed to allow for a QAbstractProxyModel, derived from QIdentityProxyModel, where you want to combine data from an underlying genuine source model with additional data held in, or external to, the proxy model layer.

    I am marking this as the solution. Thanks to @Christian-Ehrlicher for all his hard work/solutions!

    #include <QApplication>
    #include <QDebug>
    #include <QIdentityProxyModel>
    #include <QStandardItemModel>
    #include <QTreeView>
    
    class MyProxyModel : public QIdentityProxyModel
    {
        int proxyData[5] = { 5, 6, 7, 8, 9 };
    
        virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_ASSERT(!parent.isValid());
            return 5;
        }
    
        virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_ASSERT(!parent.isValid());
            return 2;
        }
    
        virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
        {
            if (role == Qt::DisplayRole)
            {
                if (orientation == Qt::Horizontal)
                    return QString("Column #%1 (%2)").arg(section).arg(section == 0 ? "source model" : "proxy model");
            }
            return QIdentityProxyModel::headerData(section, orientation, role);
        }
    
        virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
        {
            if (!index.isValid())
                return QVariant();
            if (index.column() == 1)
            {
                if (role == Qt::DisplayRole || role == Qt::EditRole)
                    return proxyData[index.row()];
                return QVariant();
            }
            return QIdentityProxyModel::data(index, role);
        }
    
        virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
        {
            if (index.column() == 1)
            {
                if (role == Qt::DisplayRole || role == Qt::EditRole)
                {
                    proxyData[index.row()] = value.toInt();
                    return true;
                }
                return false;
            }
            return QIdentityProxyModel::setData(index, value, role);
        }
    
        virtual Qt::ItemFlags flags(const QModelIndex &index) const override
        {
            if (!index.isValid())
                return Qt::NoItemFlags;
            if (index.column() == 1)
                return (Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled);
            return QIdentityProxyModel::flags(index);
        }
    
        virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
        {
            if (!sourceIndex.isValid())
                return QModelIndex();
            Q_ASSERT(sourceIndex.column() == 0);
            return createIndex(sourceIndex.row(), 0);
        }
    
        virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
        {
            if (!proxyIndex.isValid())
                return QModelIndex();
            if (proxyIndex.column() == 1)
                return QModelIndex();
            Q_ASSERT(proxyIndex.column() == 0);
            return sourceModel()->index(proxyIndex.row(), 0);
        }
    
        virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
        {
            Q_ASSERT(!parent.isValid());
            QModelIndex proxyIndex = createIndex(row, column);
            if (proxyIndex.column() == 1)
                return proxyIndex;
            return mapFromSource(mapToSource(proxyIndex));
        }
    
        virtual QModelIndex sibling(int row, int column, const QModelIndex &idx) const override
        {
            if (!idx.isValid())
                return QModelIndex();
            Q_ASSERT(!idx.parent().isValid());
            if (column == 1)
                return index(row, column);
            return mapFromSource(sourceModel()->sibling(row, column, mapToSource(idx)));
        }
    
        virtual QModelIndex buddy(const QModelIndex &index) const override
        {
            return index;
        }
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QStandardItemModel *model;
        MyProxyModel *proxy;
        QTreeView *tv;
    
        model = new QStandardItemModel(5, 1);
        for (int row = 0; row < model->rowCount(); row++)
            model->setData(model->index(row, 0), row);
    
        proxy = new MyProxyModel;
        proxy->setSourceModel(model);
    
        tv = new QTreeView;
        tv->setModel(proxy);
        tv->show();
    
        return a.exec();
    }
    

  • Lifetime Qt Champion

    Try to override QStyledItemDelegate::paint() to see if your delegate is really used or maybe replaced by something else later on.



  • @Christian-Ehrlicher
    Hi Christian. I'll do that if you like, but I already know from breakpoints and other code in the delegate that it's being called all the time. In particular, remember that if the cell/item happens to be one mapped to the underlying source model rather than one that is mapped to the proxy's own data, createEditor() is called, is that not enough to prove?

    The delegate is set for the widget as a whole, via this->tableView->setItemDelegate(new MyDelegate(this));

    And it has an initStyleOption in which I alter option->text (for $ or % characters), and they show up fine on the proxy cells when in non-edit mode. So again that shows it's called for these proxy cells?

    I have just seen there is a QStyledItemDelegate::editorEvent, and QAbstractItemDelegate::editorEvent:

    When editing of an item starts, this function is called with the event that triggered the editing, the model, the index of the item, and the option used for rendering the item.

    The base implementation returns false (indicating that it has not handled the event).

    Maybe I should start looking there? I'm trying to understand what happens when it decides whether or not to call createEditor()?


  • Lifetime Qt Champion

    @JonB said in QStyledItemDelegate::createEditor() not getting called:

    when it decides whether or not to call createEditor()?

    See QAIVP::editor() - so when this function gets called it should use your delegate...



  • @Christian-Ehrlicher
    Brill, thanks, I have stuff to look through over weekend!
    I have just had a haircut --- yay!!!!! [ When people read this in many years' time they will wonder how miraculous that was at this date :) ]


  • Lifetime Qt Champion

    Hi,

    One thing you can also do if you did not already is to add the override keyword to your overload declaration just in case there's a spelling or parameter issue.



  • @SGaist
    Thanks, they all have (virtual and) override on them, I like being thorough :) I annotated when pasting here.



  • @Christian-Ehrlicher said in QStyledItemDelegate::createEditor() not getting called:

    Try to override QStyledItemDelegate::paint() to see if your delegate is really used or maybe replaced by something else later on.

    FWIW, I have now had time to test this, and confirm it is indeed called, both on those elements which can be edited successfully and my problem ones which cannot.



  • Right! I have now had a chance to override my delegate's editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index).

    • When double clicking on a cell mapped through the proxy to the underlying source model, which does go into edit mode, I see mouse up/down/click events with index being the correct, valid index into my proxy model. (Note: index has row/column/model being the proxy model, not the underlying source model.)

    • When double clicking on a cell mapped into the proxy's own data, which does not go into edit mode, I see the same mouse events but with index being invalid --- row == -1, column == -1, model == QObject(0x0).

    As I said, the mapFromSource(), mapToSource() & index() methods are all overridden to provide the correct mapping behaviour, e.g. in the non-edit case the correct data is displayed, taken from the underlying source model or from the proxy's own data as appropriate. I now need to understand what is different here, to cause the invalid index, which is obviously why it refuses to go into edit mode....



  • I have spent some time examining code, trying stuff out, etc., but to no avail. I have looked at the woboq source code of QIdentityProxyModel & QTransposeProxyModel, each of which has elements applicable to my model situation. I have come to the conclusion that there is simply not enough information, or I do not not understand enough, to reliably find a way forward. It may be that the necessary logic is there, or I need to override more methods, but I think I will just flounder, unable to identify the precise problem or progress.

    Therefore, sadly, I think I must completely scrap my attempt to build a model based on QIdentity/TransposeProxyModel for my situation. It seems a shame as everything is working with the sole exception of the ability to make QStyledItemDeletegate::createEditor() work for interactive editing, but unless an expert can miraculously come up with the missing link I have little choice.

    So I intend to give up on my model requiring some items to map to an underlying source model while other items have their data stored internally in the proxy model. Instead I will use a QStandardItemModel for the whole thing, which will give me model storage for non-source-model data, and override data() & setData() to access data local to QStandardItemModel for local items but call data()/setData() explicitly on the original source model for non-local data. And explicitly deal with model data change signals etc. At least I (should) know how to do that without issue....


  • Lifetime Qt Champion

    Maybe you can strip it down so we can debug it.



  • @Christian-Ehrlicher
    Dear Christian,

    You asked me to provide an example. I have slaved all morning to do so, so I hope you will very kindly care to examine it and provide feedback! :)

    The code can be copied, compiled & run as-is. I have spent much time cutting it down to a minimum for pasting. there is much missing from my actual case, don't be surprised. I may have made some mistake/difference which is not present in my real, I don't know.

    The intention is that the view/proxy model has 5 rows & 2 columns. Column #0 maps to column #0 of the QStandardItemModel source model. Column #1 maps to the internal proxy[] data array held "locally" in the proxy model.

    The code behaves the same as real example, in that clicking column #0 does go into editor but clicking column #1 does not. That is my question. However, somewhere it is different from real case, but I don't know where. In my real example you would see clicking on column #1 does cause MyProxySpinDelegate::editorEvent() to be called, but with in invalid QIndex. In this cut down version it does not get called at all on column #1 click.

    We will have to deal with that if we get as far as you suggesting how to make this one work to edit column #1 as well as column #0.

    The code, all in main.cpp:

    #include <QApplication>
    #include <QDebug>
    #include <QIdentityProxyModel>
    #include <QSpinBox>
    #include <QStandardItemModel>
    #include <QStyledItemDelegate>
    #include <QTreeView>
    
    class MyProxyModel : public QIdentityProxyModel
    {
        int proxyData[5] = { 5, 6, 7, 8, 9 };
    
        virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_ASSERT(!parent.isValid());
            return 5;
        }
    
        virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_ASSERT(!parent.isValid());
            return 2;
        }
    
        virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
        {
            if (role == Qt::DisplayRole)
            {
                if (orientation == Qt::Horizontal)
                    return QString("Column #%1 (%2)").arg(section).arg(section == 0 ? "source model" : "proxy model");
            }
            return QIdentityProxyModel::headerData(section, orientation, role);
        }
    
        virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
        {
            if (!index.isValid())
                return QVariant();
            if (index.column() == 1)
            {
                if (role == Qt::DisplayRole || role == Qt::EditRole)
                    return proxyData[index.row()];
                return QVariant();
            }
            return QIdentityProxyModel::data(index, role);
        }
    
        virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
        {
            if (index.column() == 1)
            {
                if (role == Qt::DisplayRole || role == Qt::EditRole)
                {
                    proxyData[index.row()] = value.toInt();
                    return true;
                }
                return false;
            }
            return QIdentityProxyModel::setData(index, value, role);
        }
    
        virtual Qt::ItemFlags flags(const QModelIndex &index) const override
        {
            if (!index.isValid())
                return Qt::NoItemFlags;
            if (index.column() == 1)
                return (Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled);
            return QIdentityProxyModel::flags(index);
        }
    
        virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
        {
            if (!sourceIndex.isValid())
                return QModelIndex();
            Q_ASSERT(sourceIndex.column() == 0);
            return createIndex(sourceIndex.row(), 0);
        }
    
        virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
        {
            if (!proxyIndex.isValid())
                return QModelIndex();
    
            // I HAVE A FEELING NEXT 2 LINES MIGHT BE THE ROOT OF THE PROBLEM?
            // For proxy model colum #1, there is no index in the source model
            if (proxyIndex.column() == 1)
                return QModelIndex();
    
            Q_ASSERT(proxyIndex.column() == 0);
            return sourceModel()->index(proxyIndex.row(), 0);
        }
    
        virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
        {
            Q_ASSERT(!parent.isValid());
            QModelIndex proxyIndex = createIndex(row, column);
            if (proxyIndex.column() == 1)
                return proxyIndex;
            return mapFromSource(mapToSource(proxyIndex));
        }
    };
    
        virtual QModelIndex sibling(int row, int column, const QModelIndex &idx) const override
        {
            if (!idx.isValid())
                return QModelIndex();
            Q_ASSERT(!idx.parent().isValid());
            if (column == 1)
                return index(row, column);
            return mapFromSource(sourceModel()->sibling(row, column, mapToSource(idx)));
        }
    
    
    class MyProxySpinDelegate : public QStyledItemDelegate
    {
        virtual bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
        {
            qDebug() << "editorEvent" << event->type() << model << option << index;
            return QStyledItemDelegate::editorEvent(event, model, option, index);
        }
    
        virtual QWidget *createEditor(QWidget* parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
        {
            Q_UNUSED(option);
            Q_UNUSED(index);
            QSpinBox *spin = new QSpinBox(parent);
            return spin;
        }
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QStandardItemModel *model;
        MyProxyModel *proxy;
        QTreeView *tv;
    
        model = new QStandardItemModel(5, 1);
        for (int row = 0; row < model->rowCount(); row++)
            model->setData(model->index(row, 0), row);
    
        proxy = new MyProxyModel;
        proxy->setSourceModel(model);
    
        tv = new QTreeView;
        tv->setModel(proxy);
        tv->setItemDelegate(new MyProxySpinDelegate);
        tv->show();
    
        return a.exec();
    }
    

  • Lifetime Qt Champion

    Looks like the default sibling() implementation is the culprit:

    QModelIndex QIdentityProxyModel::sibling(int row, int column, const QModelIndex &idx) const
    {
        Q_D(const QIdentityProxyModel);
        return mapFromSource(d->model->sibling(row, column, mapToSource(idx)));
    }
    

    Since you don't have a col1 in your source model it will return an invalid index I would guess.



  • @Christian-Ehrlicher
    I'm not certain if you're saying I can correct this with my code?

    I see that sibling() uses mapFromSource() & mapToSource(). I worry about whether my requirement can work.

    Remember, what I have is a genuine source model with some genuine data columns for my special proxy model, but I also have columns of data internally in the proxy model.

    In the case of Qt's QIdentityProxyModel & QTransposeProxyModel, both of them always map to/from data rows/columns in their source model.

    In my case, I'm not worried about my mapFromSource(const QModelIndex &sourceIndex). An index in the source model can always be mapped to an index in my proxy (or invalid if not a mapped column).

    But I am worried about mapToSource(const QModelIndex &proxyIndex). If the proxy index is column #0 I'm OK and can map to the source model. But if it's column #1, its own internal column, there is no source model index. And I think that is where there is a real problem? I can only return the proxyIndex parameter as-is, but that's an index in the proxy and I think may have caused infinite recursion error?, or QModelIndex() invalid. And I'm worried that is a problem either way round for the Qt code?

    I have edited that fragment in my code above to read:

            // I HAVE A FEELING NEXT 2 LINES MIGHT BE THE ROOT OF THE PROBLEM?
            // For proxy model colum #1, there is no index in the source model
            if (proxyIndex.column() == 1)
                return QModelIndex();
    

  • Lifetime Qt Champion

    @JonB said in QStyledItemDelegate::createEditor() not getting called:

    And I think that is where there is a real problem? I

    correct, reimplement sibling and implement a workaround for your special columns.



  • @Christian-Ehrlicher

    correct, reimplement sibling and implement a workaround for your special columns.

    But I don't think I know how to! (As in, what the code should do?)

    When idx is in the proxy model and refers to colum#1, there is no equivalent mapToSource(idx). Hmm, I want mapFromSource() of that .... Maybe in this case I need to just look up row, column in proxy model...?


  • Lifetime Qt Champion

    QModelIndex MyProxyModel::sibling(int row, int column, const QModelIndex &idx) const
    {
        if (column != 1)
          return mapFromSource(d->model->sibling(row, column, mapToSource(idx)));
      return <yourindexfor X,1>
    }
    
    


  • @Christian-Ehrlicher
    Yep, I get it now! The penny clicked as I typed it :) I will certainly try this tomorrow and report back!

    Thanks so much :)



  • @Christian-Ehrlicher
    Dear Christian,

    Thank you for your suggestion. It does indeed make sense, but it still does not alter/solve the behaviour, either in the test code or in my real code :(

    I have incorporated your sibling override suggestion:

        virtual QModelIndex sibling(int row, int column, const QModelIndex &idx) const override
        {
            if (!idx.isValid())
                return QModelIndex();
            Q_ASSERT(!idx.parent().isValid());
            if (column == 1)
                return index(row, column);
            return mapFromSource(sourceModel()->sibling(row, column, mapToSource(idx)));
        }
    

    I have also corrected a slip in my test code:

        virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
    

    (the default for role was wrong), and in editorEvent()

            qDebug() << "editorEvent" << event->type() << model << option << index;
    

    (prints event->type()).

    I have edited my sample code above to include these three changes.

    I have verified that sibling() overrideis indeed called.

    The good news is that with the new sibling() definition the test code now acts like the real code, in that MyProxySpinDelegate::editorEvent() is called when clicking on the proxy-only column.

    However, the bad news is that in both test & real it still does not go into edit mode :( The debug from editorEvent() shows:

    • When clicking on a source-mapped column, which does go into edit mode, I see a bunch of lines starting:
    editorEvent QEvent::MouseButtonPress QIdentityProxyModel(0x5555557c2ef0) QModelIndex(0,0,0x0,QIdentityProxyModel(0x5555557c2ef0))
    
    • But on clicking the proxy-only column, which does not go into edit mode, I see the same bunch but with:
    editorEvent QEvent::MouseButtonPress QIdentityProxyModel(0x5555557c2ef0) QModelIndex(-1,-1,0x0,QObject(0x0))
    

    This invalid QModelIndex() must be the issue preventing going into edit mode successfully.

    Even with the sibling() change you should be finding the same as me, viz. it does not go into edit mode on proxy-only column? I don't know just how you came up with your observation, but can you not take it as far as actually making the test go into edit mode?


  • Lifetime Qt Champion

    The problem seems to be the QAIV::buddy() function (or better the reimplementation in QSFPM). Simply replace the implementation by

    QModelIndex buddy(const QModelIndex &index) const override
     {
        return index;
    }
    

    and the editing works as expected.



  • @Christian-Ehrlicher
    Yay, it works! You are a hero :)

    https://doc.qt.io/qt-5/qabstractitemmodel.html#buddy was the missing link:

    Returns a model index for the buddy of the item represented by index. When the user wants to edit an item, the view will call this function to check whether another item in the model should be edited instead. Then, the view will construct a delegate using the model index returned by the buddy item.

    The default implementation of this function has each item as its own buddy.

    The relevance, of course, is "When the user wants to edit an item", but I hadn't noticed that among all the possible functions needed to override.



  • For the sake of completeness, I present as the solution a complete, standalone, working code for this situation. For simplicity, I have removed any use of a QStyledItemDelegate for QTreeView::setItemDelegate() as this is not required.

    It shows the minimal needed to allow for a QAbstractProxyModel, derived from QIdentityProxyModel, where you want to combine data from an underlying genuine source model with additional data held in, or external to, the proxy model layer.

    I am marking this as the solution. Thanks to @Christian-Ehrlicher for all his hard work/solutions!

    #include <QApplication>
    #include <QDebug>
    #include <QIdentityProxyModel>
    #include <QStandardItemModel>
    #include <QTreeView>
    
    class MyProxyModel : public QIdentityProxyModel
    {
        int proxyData[5] = { 5, 6, 7, 8, 9 };
    
        virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_ASSERT(!parent.isValid());
            return 5;
        }
    
        virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override
        {
            Q_ASSERT(!parent.isValid());
            return 2;
        }
    
        virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override
        {
            if (role == Qt::DisplayRole)
            {
                if (orientation == Qt::Horizontal)
                    return QString("Column #%1 (%2)").arg(section).arg(section == 0 ? "source model" : "proxy model");
            }
            return QIdentityProxyModel::headerData(section, orientation, role);
        }
    
        virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
        {
            if (!index.isValid())
                return QVariant();
            if (index.column() == 1)
            {
                if (role == Qt::DisplayRole || role == Qt::EditRole)
                    return proxyData[index.row()];
                return QVariant();
            }
            return QIdentityProxyModel::data(index, role);
        }
    
        virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override
        {
            if (index.column() == 1)
            {
                if (role == Qt::DisplayRole || role == Qt::EditRole)
                {
                    proxyData[index.row()] = value.toInt();
                    return true;
                }
                return false;
            }
            return QIdentityProxyModel::setData(index, value, role);
        }
    
        virtual Qt::ItemFlags flags(const QModelIndex &index) const override
        {
            if (!index.isValid())
                return Qt::NoItemFlags;
            if (index.column() == 1)
                return (Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled);
            return QIdentityProxyModel::flags(index);
        }
    
        virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
        {
            if (!sourceIndex.isValid())
                return QModelIndex();
            Q_ASSERT(sourceIndex.column() == 0);
            return createIndex(sourceIndex.row(), 0);
        }
    
        virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
        {
            if (!proxyIndex.isValid())
                return QModelIndex();
            if (proxyIndex.column() == 1)
                return QModelIndex();
            Q_ASSERT(proxyIndex.column() == 0);
            return sourceModel()->index(proxyIndex.row(), 0);
        }
    
        virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
        {
            Q_ASSERT(!parent.isValid());
            QModelIndex proxyIndex = createIndex(row, column);
            if (proxyIndex.column() == 1)
                return proxyIndex;
            return mapFromSource(mapToSource(proxyIndex));
        }
    
        virtual QModelIndex sibling(int row, int column, const QModelIndex &idx) const override
        {
            if (!idx.isValid())
                return QModelIndex();
            Q_ASSERT(!idx.parent().isValid());
            if (column == 1)
                return index(row, column);
            return mapFromSource(sourceModel()->sibling(row, column, mapToSource(idx)));
        }
    
        virtual QModelIndex buddy(const QModelIndex &index) const override
        {
            return index;
        }
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QStandardItemModel *model;
        MyProxyModel *proxy;
        QTreeView *tv;
    
        model = new QStandardItemModel(5, 1);
        for (int row = 0; row < model->rowCount(); row++)
            model->setData(model->index(row, 0), row);
    
        proxy = new MyProxyModel;
        proxy->setSourceModel(model);
    
        tv = new QTreeView;
        tv->setModel(proxy);
        tv->show();
    
        return a.exec();
    }
    

Log in to reply