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

QTreeview Custom model and QSortFilterProxyModel



  • I have a custom Hierarchical model Implemented which is basically displayed in a QtreeView. My requirement is to filter data on multiple columns. The code of the model is as follows:
    model:

    @namespace {
    const int ColumnCount = 7;
    enum Column {Application=0, StartTime, AccountedTime,Activity,Purpose,OffLine,MachineName};
    }

    QVariant CSapActvtyTreeModel::data(const QModelIndex &index, int role) const
    {
    qDebug()<<"CSapActvtyTreeModel::data ... start"<<endl;
    if (!m_pObjRootItem || !index.isValid() || index.column() < 0 ||
    index.column() >= ColumnCount) {
    return QVariant();
    }

    //This is the data display role
    if (COnlineSapActvtyItem *item = itemForIndex(index)) {
        if (role == Qt::DisplayRole) {
            switch (index.column()) {
            //Application, StartTime, AccountedTime,Activity,Purpose
            case Application: return item->GetTaskName();
            case StartTime: return item->GetStartTimeStr();
            case AccountedTime: return item->GetAccountedTimeStr();
            case Activity: return item->GetActivityType();
            case Purpose: return item->GetActivityPurpose();
            case OffLine: return (item->IsOnline() ? "1" : "0");
            case MachineName: return item->GetMachineName();
            default: return QVariant();
            }
        }
    }
    return QVariant();
    

    }

    int CSapActvtyTreeModel::rowCount(const QModelIndex &parent) const
    {
    qDebug() << "CSapActvtyTreeModel::rowCount" << endl;
    if (parent.isValid() && parent.column() != 0)
    return 0;
    COnlineSapActvtyItem *parentItem = itemForIndex(parent);
    int irowCount = (parentItem ? parentItem->childCount() : 0);
    qDebug()<<"Returning row count: "<<irowCount <<endl;
    return irowCount;
    }

    int CSapActvtyTreeModel::columnCount(const QModelIndex &parent) const
    {
    qDebug() << "CSapActvtyTreeModel::columnCount" << endl;
    int iColCount = (parent.isValid() && parent.column() != 0 ? 0 : ColumnCount);
    qDebug()<<"Returning Col count: "<<iColCount <<endl;
    return iColCount;
    }

    QModelIndex CSapActvtyTreeModel::index(int row, int column,
    const QModelIndex &parent) const
    {
    qDebug() << "CSapActvtyTreeModel::index" << endl;
    if (!m_pObjRootItem || row < 0 || column < 0 || column >= ColumnCount
    || (parent.isValid() && parent.column() != 0))
    return QModelIndex();
    COnlineSapActvtyItem *parentItem = itemForIndex(parent);
    if(parentItem) {
    if (COnlineSapActvtyItem *item = parentItem->childAt(row))
    return createIndex(row, column, item);
    }
    return QModelIndex();
    }

    COnlineSapActvtyItem* CSapActvtyTreeModel::itemForIndex(const QModelIndex &index) const
    {
    qDebug() << "CSapActvtyTreeModel::itemForIndex" << endl;
    if (index.isValid()) {
    if (COnlineSapActvtyItem item = static_cast<COnlineSapActvtyItem>(
    index.internalPointer())) {
    return item;
    }
    }
    return m_pObjRootItem;
    }

    QStringList CSapActvtyTreeModel::pathForIndex(const QModelIndex &index) const
    {
    qDebug() << "CSapActvtyTreeModel::pathForIndex" << endl;
    QStringList path;
    QModelIndex thisIndex = index;
    while (thisIndex.isValid()) {
    path.prepend(data(thisIndex).toString());
    thisIndex = thisIndex.parent();
    }
    return path;
    }

    QModelIndex CSapActvtyTreeModel::indexForPath(const QStringList &path) const
    {
    qDebug() << "CSapActvtyTreeModel::indexForPath" << endl;
    return indexForPath(QModelIndex(), path);
    }

    QModelIndex CSapActvtyTreeModel::indexForPath(const QModelIndex &parent,
    const QStringList &path) const
    {
    qDebug() << "CSapActvtyTreeModel::indexForPath2" << endl;
    if (path.isEmpty())
    return QModelIndex();
    for (int row = 0; row < rowCount(parent); ++row) {
    QModelIndex thisIndex = index(row, 0, parent);
    if (data(thisIndex).toString() == path.at(0)) {
    if (path.count() == 1)
    return thisIndex;
    thisIndex = indexForPath(thisIndex, path.mid(1));
    if (thisIndex.isValid())
    return thisIndex;
    }
    }
    return QModelIndex();
    }@

    These are the major overloaded functions implemented for my model. When I use this model, the treeView displays data correct. But When I changed the treeview to use a custom proxy model which just overrides filterAcceptRows the tree view does not display anything. Also, I added a debug trace in the filterAcceptRows. The filterAcceptRows is not getting called.

    The way I set up the treeView and models is:

    @m_pFilterProxyModel = new CSapFilterProxyModel(); //Derived from QSortFilterProxyModel
    treeView->setModel(m_pFilterProxyModel);

    m_pMainTreeModel = new CSapActvtyTreeModel(); //My Main model

    PopulateMainModel();
    m_pFilterProxyModel->setSourceModel(m_pMainTreeModel);
    @

    Sample Xml using which I build the tree
    @
    <TA NM="Qt Creator" OL="1" MN="PRADY-LP">

            <TA NM="SapienceTray" OL="1" MN="PRADY-LP">
    
                <TM ST="2013-03-29T04:47:02" TT="2100"/>
    
                <AC AT="Development" AP="Sapience Multi Client"/>
    
            </TA>
    

    </TA>
    <TA NM="Ms Visual Studio" OL="1" MN="PRADY-LP">

        <TA NM="Test" OL="1" MN="PRADY-LP">
    
            <TM ST="2013-03-29T02:47:02" TT="3000"/>
    
            <AC AT="Development" AP="Sapience Multi Client"/>
    
        </TA>
    
        <TA NM="Number Crunching" OL="1" MN="PRADY-LP">
    
            <TM ST="2013-03-29T01:47:02" TT="2000"/>
    
            <AC AT="Development" AP="Sapience Multi Client"/>
    
        </TA>
    

    </TA>
    @

    This is how the proxymodel is implemented.

    FilterAcceptsRow is not getting called. What could be the problem???



  • Are you positive that your overloaded filterAcceptsRow is really an overload? I mean: does the signature match the one from the base class exactly? Look for a missing const, for instance...



  • Andre, yes my function overload is correct.
    My trimmed down derivation of QSortFilterProxyModel is as follows:
    ////.h
    @
    #include <QDate>
    #include <QSortFilterProxyModel>

    class CSapFilterProxyModel : public QSortFilterProxyModel {
    Q_OBJECT

    public:
    CSapFilterProxyModel(QObject* parent = 0);
    virtual ~CSapFilterProxyModel();

    //All the filtering criteria go in here...
    

    protected:
    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;

    private:
    //Data members and helper functions here
    };
    @

    and the .cpp file is :
    @
    bool CSapFilterProxyModel::filterAcceptsRow(int sourceRow,
    const QModelIndex &sourceParent) const {
    qDebug() << "filterAcceptsRow called" << endl;
    bool bIsAccepted = true;
    bIsAccepted = bIsAccepted && MatchMe(1,sourceRow,sourceParent);
    return bIsAccepted;

    }

    bool CSapFilterProxyModel::MatchMe(int iCol,int sourceRow,
    const QModelIndex &sourceParent) const {
    //return true or false based on matching criteria
    }
    @

    Also the way I load the XML into my data structure (for reference just in case),
    where pObjTask contains the root item pointer.
    @
    void CSapActvtyTreeModel::readTasks(QXmlStreamReader *reader, COnlineSapActvtyItem *pObjTask)
    {
    qDebug() << "CSapActvtyTreeModel::readTasks" << endl;
    while ((reader && !reader->atEnd())) {
    reader->readNext();
    if (reader->isStartElement()) {
    if (reader->name() == TaskTag) { //TA
    QString strName = reader->attributes()
    .value(NameAttribute).toString();

                bool bIsOnLine = reader->attributes()
                        .value(OnLineAttribute)
                        == "1";
    
                QString strMachineName = reader->attributes()
                        .value(MachinNameAttribute).toString();
                pObjTask = new COnlineSapActvtyItem(NULL,pObjTask);
                pObjTask->SetTaskName(strName);
                pObjTask->SetOnlineFlag(bIsOnLine);
                pObjTask->SetMachineName(strMachineName);
    
            } else if(reader->name() == TimeTag) { //TM
                Q_ASSERT(pObjTask);
    
                QDateTime objStartTime = QDateTime::fromString(
                            reader->attributes().value(StartTimeAttribute)
                            .toString(), Qt::ISODate);
                int iTotalTime =
                        reader->attributes().value(TotalTimeAttribute)
                        .toString().toInt();
                pObjTask->SetStartTime(objStartTime);
                pObjTask->SetAccountedTime(iTotalTime);
            }
            else if (reader->name() == ActvtyTag) { //AC
                Q_ASSERT(pObjTask);
                QString strActvtyType =
                        reader->attributes().value(ActvtyTypeAttribute)
                        .toString();
                QString strActvtyPurpose =
                        reader->attributes().value(ActvtyPurposeAttribute)
                        .toString();
                pObjTask->SetActvtyType(strActvtyType);
                pObjTask->SetActvtyPurpose(strActvtyPurpose);
            }
        }
        else if (reader->isEndElement()) {
            if (reader->name() == TaskTag) { //TA end
                Q_ASSERT(pObjTask);
                pObjTask = pObjTask->parent();
                Q_ASSERT(pObjTask);
            }
        }
    }
    

    }
    @

    Please let me know. Thanks



  • Any one has any ideas on the above?



  • I have been able to solve this using the QDomModel example from Qt examples which works very well with QSortFilterProxyModel.


Log in to reply