[SOLVED] Use of QIdentityProxyModel and QTableView (Qt5.2)



  • Hi all,

    I'd like to use QIdentityProxyModel to add a special line in my tableview in order to avoid adding a button for inserting new data (i use the proxy for not adding fake data in my database for graphic purpose...).
    I have as a sourcemodel, a QSqlTableModel.
    With the following code, i have a line yes, but the text is blank. It seems that the data() function is not called for index.row=0! (that is where i want the special line). The data corresponding to the sourcemodel is well displayed.
    Would you have an explanation what is wrong in my code ? Many thanks!

    @class ProxyModelForTablePers : public QIdentityProxyModel {
    public :
    ProxyModelForTablePers(QObject* parent = 0);
    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
    virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const;
    virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
    virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
    };

    ProxyModelForTablePers::ProxyModelForTablePers (QObject* parent)
    : QIdentityProxyModel (parent) {
    }

    int ProxyModelForTablePers::rowCount(const QModelIndex &parent) const {

    return (this->sourceModel()->rowCount()+1);//for adding a fake line
    

    }

    QVariant ProxyModelForTablePers::data(const QModelIndex &proxyIndex, int role) const {

    if (!proxyIndex.isValid())
        return QVariant ();
    
    if (proxyIndex.row() == 0) {
        return QVariant ("NEW");
    } else
        return QIdentityProxyModel::data(proxyIndex, role);
    

    }

    bool ProxyModelForTablePers::setData(const QModelIndex &index, const QVariant &value, int role) {

    if (index.row() == 0) {
        return true;//not yet imlemented will do the insert
    }
    else
        return QIdentityProxyModel::setData (index, value, role);
    

    }

    Qt::ItemFlags ProxyModelForTablePers::flags(const QModelIndex &index) const {

    Qt::ItemFlags flags = QIdentityProxyModel::flags(index);
    flags |= Qt::ItemIsEditable;
    return flags;
    

    }

    QModelIndex ProxyModelForTablePers::index(int row, int column, const QModelIndex &parent) const {

    if (row == 0) {
        return QModelIndex(parent);
    } else
        return this->sourceModel()->index(row-1, column, parent);
    

    }

    and the code called for the QTableview :

    _SourceModel = new QSqlTableModel (this);
    FillSourceModelViewPers();
    
    ProxyModelForTablePers * lProxy = new ProxyModelForTablePers (this);
    lProxy->setSourceModel(_SourceModel);
    
    this->setModel(lProxy); ("this" is a subclass of QTableView)
    

    @



  • Anyone has an experience of this proxy ? Thanks.

    [quote author="bizut" date="1390287425"]Hi all,

    I'd like to use QIdentityProxyModel to add a special line in my tableview in order to avoid adding a button for inserting new data (i use the proxy for not adding fake data in my database for graphic purpose...).
    I have as a sourcemodel, a QSqlTableModel.
    With the following code, i have a line yes, but the text is blank. It seems that the data() function is not called for index.row=0! (that is where i want the special line). The data corresponding to the sourcemodel is well displayed.
    Would you have an explanation what is wrong in my code ? Many thanks!

    @class ProxyModelForTablePers : public QIdentityProxyModel {
    public :
    ProxyModelForTablePers(QObject* parent = 0);
    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
    virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const;
    virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
    virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
    };

    ProxyModelForTablePers::ProxyModelForTablePers (QObject* parent)
    : QIdentityProxyModel (parent) {
    }

    int ProxyModelForTablePers::rowCount(const QModelIndex &parent) const {

    return (this->sourceModel()->rowCount()+1);//for adding a fake line
    

    }

    QVariant ProxyModelForTablePers::data(const QModelIndex &proxyIndex, int role) const {

    if (!proxyIndex.isValid())
        return QVariant ();
    
    if (proxyIndex.row() == 0) {
        return QVariant ("NEW");
    } else
        return QIdentityProxyModel::data(proxyIndex, role);
    

    }

    bool ProxyModelForTablePers::setData(const QModelIndex &index, const QVariant &value, int role) {

    if (index.row() == 0) {
        return true;//not yet imlemented will do the insert
    }
    else
        return QIdentityProxyModel::setData (index, value, role);
    

    }

    Qt::ItemFlags ProxyModelForTablePers::flags(const QModelIndex &index) const {

    Qt::ItemFlags flags = QIdentityProxyModel::flags(index);
    flags |= Qt::ItemIsEditable;
    return flags;
    

    }

    QModelIndex ProxyModelForTablePers::index(int row, int column, const QModelIndex &parent) const {

    if (row == 0) {
        return QModelIndex(parent);
    } else
        return this->sourceModel()->index(row-1, column, parent);
    

    }

    and the code called for the QTableview :

    _SourceModel = new QSqlTableModel (this);
    FillSourceModelViewPers();
    
    ProxyModelForTablePers * lProxy = new ProxyModelForTablePers (this);
    lProxy->setSourceModel(_SourceModel);
    
    this->setModel(lProxy); ("this" is a subclass of QTableView)
    

    @
    [/quote]

    [quote author="bizut" date="1390287425"]Hi all,

    I'd like to use QIdentityProxyModel to add a special line in my tableview in order to avoid adding a button for inserting new data (i use the proxy for not adding fake data in my database for graphic purpose...).
    I have as a sourcemodel, a QSqlTableModel.
    With the following code, i have a line yes, but the text is blank. It seems that the data() function is not called for index.row=0! (that is where i want the special line). The data corresponding to the sourcemodel is well displayed.
    Would you have an explanation what is wrong in my code ? Many thanks!

    @class ProxyModelForTablePers : public QIdentityProxyModel {
    public :
    ProxyModelForTablePers(QObject* parent = 0);
    virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
    virtual QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const;
    virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    virtual Qt::ItemFlags flags(const QModelIndex &index) const;
    virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
    };

    ProxyModelForTablePers::ProxyModelForTablePers (QObject* parent)
    : QIdentityProxyModel (parent) {
    }

    int ProxyModelForTablePers::rowCount(const QModelIndex &parent) const {

    return (this->sourceModel()->rowCount()+1);//for adding a fake line
    

    }

    QVariant ProxyModelForTablePers::data(const QModelIndex &proxyIndex, int role) const {

    if (!proxyIndex.isValid())
        return QVariant ();
    
    if (proxyIndex.row() == 0) {
        return QVariant ("NEW");
    } else
        return QIdentityProxyModel::data(proxyIndex, role);
    

    }

    bool ProxyModelForTablePers::setData(const QModelIndex &index, const QVariant &value, int role) {

    if (index.row() == 0) {
        return true;//not yet imlemented will do the insert
    }
    else
        return QIdentityProxyModel::setData (index, value, role);
    

    }

    Qt::ItemFlags ProxyModelForTablePers::flags(const QModelIndex &index) const {

    Qt::ItemFlags flags = QIdentityProxyModel::flags(index);
    flags |= Qt::ItemIsEditable;
    return flags;
    

    }

    QModelIndex ProxyModelForTablePers::index(int row, int column, const QModelIndex &parent) const {

    if (row == 0) {
        return QModelIndex(parent);
    } else
        return this->sourceModel()->index(row-1, column, parent);
    

    }

    and the code called for the QTableview :

    _SourceModel = new QSqlTableModel (this);
    FillSourceModelViewPers();
    
    ProxyModelForTablePers * lProxy = new ProxyModelForTablePers (this);
    lProxy->setSourceModel(_SourceModel);
    
    this->setModel(lProxy); ("this" is a subclass of QTableView)
    

    @
    [/quote]


  • Lifetime Qt Champion

    Hi,

    IIRC, the QIdentityProxyModel is not suited to do that. It can be used to modify the data but not to modify the model structure. You should rather start with a QSortFilterProxyModel

    Hope it helps



  • Hi,
    Thanks for your answer. It doesn't work neither with QSortFilterProxyModel.
    I'm wondering if my index method reimplementaion is correct.
    I can see that data form the source model but not my fake line...


  • Lifetime Qt Champion

    IIRC you would have to also modify mapToSource



  • Hi,

    Ok Thank you. Below is the code.
    It works. I mean i have the "new line" displayed as the first line and the sql data displayed the other lines.
    But i still have 2 problems :

    1. The first is that when i move the mouse over the first line, the program crashes.
    2. Instead of having the text "NEW" in the cell where it should be displayed, i have a small rectangular and the text "N...".

    Would you have some suggestions to make it work ? Thank you.
    By the way, would you have some working example ? It would be great.
    Many thanks!

    @
    QModelIndex ProxyModelForTablePers::index(int row, int column, const QModelIndex &parent) const {

    if (row == 0) {
        return this->createIndex(row, column);
    } else
        return this->sourceModel()->index(row-1, column, parent);
    

    }

    QModelIndex ProxyModelForTablePers::mapFromSource(const QModelIndex & source) const
    {
    if (!sourceModel())
    return QModelIndex();

    if (!source.parent().isValid())
        return QModelIndex();
    
    return index(source.row()+1, source.column());
    

    }

    QModelIndex ProxyModelForTablePers::mapToSource(const QModelIndex & proxy) const
    {

    if (!sourceModel())
        return QModelIndex();
    
    if (proxy.row()==0)
        return QModelIndex();
    
    return this->sourceModel()->index(proxy.row()-1, proxy.column());
    

    }
    @


  • Lifetime Qt Champion

    Did you implement the data function ? If so, how does it look like ?



  • Hi,

    Below is the my code :

    @
    ProxyModelForTablePers::ProxyModelForTablePers (QObject* parent)
    : QSortFilterProxyModel (parent) {

    }

    int ProxyModelForTablePers::rowCount(const QModelIndex &parent) const {
    return (this->sourceModel()->rowCount()+1);
    }

    QVariant ProxyModelForTablePers::data(const QModelIndex &proxyIndex, int role) const {

    if (!proxyIndex.isValid())
        return QVariant ();
    
    if (proxyIndex.row() == 0) {
    
        if (proxyIndex.column()==1)
            return QVariant ("new");
        else
            return QVariant("test");
    } else
        return QSortFilterProxyModel::data(proxyIndex, role);
        //return this->sourceModel()->data(this->mapToSource(proxyIndex), role);
    

    }

    bool ProxyModelForTablePers::setData(const QModelIndex &index, const QVariant &value, int role) {

    if (index.row() == 0) {
        return false;
    }
    else
        return this->sourceModel()->setData (index, value, role);
    

    }

    Qt::ItemFlags ProxyModelForTablePers::flags(const QModelIndex &index) const {

    Qt::ItemFlags flags = QSortFilterProxyModel::flags(index);
    flags |= Qt::ItemIsEditable;
    return flags;
    

    }

    QModelIndex ProxyModelForTablePers::index(int row, int column, const QModelIndex &parent) const {

    if (row == 0) {
        return this->createIndex(row, column);
    } else
        return this->sourceModel()->index(row-1, column, parent);
    

    }

    QModelIndex ProxyModelForTablePers::mapFromSource(const QModelIndex & source) const
    {
    if (!sourceModel())
    return QModelIndex();

    if (!source.parent().isValid())
        return QModelIndex();
    
    return index(source.row()+1, source.column());
    

    }

    QModelIndex ProxyModelForTablePers::mapToSource(const QModelIndex & proxy) const
    {

    if (!sourceModel())
        return QModelIndex();
    
    if (proxy.row()==0)
        return QModelIndex();
    
    return this->sourceModel()->index(proxy.row()-1, proxy.column());
    

    }

    TableViewPers::TableViewPers (QWidget *parent)
    : QTableView (parent)
    {

    this->setEditTriggers(QAbstractItemView::AllEditTriggers);
    this->setSortingEnabled(true);
    this->setSelectionBehavior(QAbstractItemView::SelectRows);
    this->setSelectionMode(QAbstractItemView::ContiguousSelection);
    
    _SourceModel = new QSqlTableModel (this, DataBaseManager::getManager()->getDataBase());
    

    FillSourceModelViewPers();

    ProxyModelForTablePers * lProxy = new ProxyModelForTablePers (this);
    lProxy->setSourceModel(_SourceModel);
    
    this->setModel(lProxy);
    
    this->setColumnHidden(0, true);
    this->resizeColumnsToContents();
    

    }

    @


  • Lifetime Qt Champion

    You don't check what role you are given so basically you are giving "test" and "new" for every role whether it asks for DisplayRole or DecorationRole etc...



  • Hi, thanks for your help !
    You are perfectly right! It is well displayed now !

    But i still have the crash problem when the mouse is over the first line. ..Like a onmouseOver event ? Have you got any clue about it ?

    @
    QVariant ProxyModelForTablePers::data(const QModelIndex &proxyIndex, int role) const {

    qDebug()<<proxyIndex.row()<<" "<<proxyIndex.column()<<" "<<rowCount();
    
    if (role != Qt::DisplayRole)
            return QVariant();
    
    if (!proxyIndex.isValid())
        return QVariant ();
    
    if (proxyIndex.row() == 0) {
                if (proxyIndex.column()==1)
                    return QVariant ("new");
                else
                    return QVariant("test");
    
    } else
        //return QSortFilterProxyModel::data(proxyIndex, role);
        return this->sourceModel()->data(this->mapToSource(proxyIndex), role);
    

    }
    @


  • Lifetime Qt Champion

    You should run your program using the debugger, from your proxy code here the only thing that could happen is that you don't have a source model which shouldn't happen since you are setting it in the constructor of TableViewPers



  • Hi,

    I don't understand. Maybe that is a Qt bug ?
    In debug mode, this are the last calls before crash :
    QSortFilterProxyModel::parent
    QTableView::visualRect
    QAbstractItenView::viewportEvent

    I removed the wrong line from mapFromSource
    if (!source.parent().isValid())
    return QModelIndex();

    But i still have the problem. Any idea ? Thanks


  • Lifetime Qt Champion

    First thing to do is correct the data function to return proper values (or default) for each role



  • Hi,

    It was simply to implement the parent method...
    Thanks


  • Lifetime Qt Champion

    Of course ! I forgot about that one… Sorry


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.