[Solved] Questions about QAbstractItemModel and Q(List/Tree)View



  • Hello Qt-ers! :D

    I am trying to implement something using the model/view architecture of Qt but I am stuck.

    My initial problem was the following:
    I have an application that keeps data in a tree-like structure. In this application I had multiple QListWidgets and QTreeWidgets, referring to the same logical data. Each time I was adding one new node in the tree I had to create a new Q(List/TreeW)idgetItem for each Q(List/Tree)Widget. Each time I was unchecking one item from one of the Q(List/Tree)Widgets I had to go and uncheck it also from all the rest. Because of this I decided to use the Model/View architecture of Qt and have a single ItemModel but multiple Item views refering to it. I guess this is the right way to go. Right?

    So, I did create a Custom QAbstractItemModel and I replaced the Q(List/Tree)Widgets with Q(List/Tree)Views.
    The Custom QAbstractItemModel is based on the http://doc.qt.nokia.com/latest/itemviews-simpletreemodel.html with the addition to edit the data.

    After that several problems and questions have arisen:

    • The QTreeView looks file, but the QListView doesn't because in the QListView I only want to list the children of the high level items of the CustomModel (by default the high level items appear, and not their children). How do I define which items of an QAbstractItemModel should be listed an a QListView?
    • Inside my custom QAbstractItemModel I save a bool for each item indicating if it is checked or not. How do I control the checked state of the items in my case? In the past I was using the setChecked() function of Q(List/Tree)WidgetItem.
    • I have an extra QListView in which I should only list the items that their name partialy matches a string I type in a QLineEdit. Any suggestions how this should be done?

    Thanks for your time guys.

    Cheers



  • [quote author="Bekos" date="1313677637"]
    After that several problems and questions have arisen:

    • The QTreeView looks file, but the QListView doesn't because in the QListView I only want to list the children of the high level items of the CustomModel (by default the high level items appear, and not their children). How do I define which items of an QAbstractItemModel should be listed an a QListView?
      [/quote]

    QAbstractItemView::setRootIndex

    [quote]

    • Inside my custom QAbstractItemModel I save a bool for each item indicating if it is checked or not. How do I control the checked state of the items in my case? In the past I was using the setChecked() function of Q(List/Tree)WidgetItem.
      [/quote]

    Check status works out-of-the-box. See the Qt::ItemIsUserCheckable item flag (QAbstractItemModel::flags), and the Qt::CheckStateRole role for QAbstractItemModel::data / setData.

    [quote]

    • I have an extra QListView in which I should only list the items that their name partialy matches a string I type in a QLineEdit. Any suggestions how this should be done?
      [/quote]

    A proxy model between the "true" model and the data (f.i. a QSortFilterProxyModel).



  • For filtering your datas, using QSortFilterProxyModel as peppe said, you can either :



  • Thank you very much for your responses guys!

    @peppe: The QAbstractItemView::setRootIndex does not do exactly what I want. In my case I want the children of all the high level items of the model. Not the children of only one of the high level items. I guess this might be possible using a proxy model. Right?

    Indeed the check status works out of the box. I just didn't know what exactly the 'role' parameter of the data/setData functions was doing. After giving me the hints I was able to understand it and now the check boxes work fine!

    Indeed the proxy model can help me to do what I want to achieve. I will override the function filterAcceptsRow() as octal suggested and I will do my checks there (filterRegExp() can also work but I might need to add more features later on that wont work with it). If I find any difficulties I will right back here.

    Thank you very much for your time guys!

    Cheers



  • I am back again! :D

    Looks like overriding filterAcceptRow won't work. This is because if I don't accept a parent node, it will never accept its children. What I want is to 'flatten' a tree structure to a list view and only keep the leaf nodes. For example I have this Model:
    @
    -A
    -A1
    -A2
    -B
    -B1
    -B2
    @
    And I want to list the leaf items in the QListView:
    @
    -A1
    -A2
    -B1
    -B2
    @
    Any ideas? Should I reimplement QAbstractProxyModel::mapToSource() and QAbstractProxyModel::mapFromSource() functions? Is there any other easier way to do this?

    Thanks again!

    Cheers



  • I don't think there's anything in Qt for it. You must write a proxy model that "flattens" your tree in a list (and maybe implements filtering, if you like it that way). It shouldn't be complicated to do -- if you already made a tree model, you're ready for it! :)



  • I recently posted a proxy model class here on the wiki that can filter trees. It does not flatten the list, but instead keeps parent nodes that have children that match (and keep child nodes where the parent matches). Perhaps that would be of use?

    Note that it is meant for use on relatively small, simple tree models. Using it on a QFileSystemModel would result in less than satisfactory results, I bet.



  • I am back!

    Thank you all for helping me guys! I managed to do what I wanted by overriding QAbstractProxyModel::mapToSource() and QAbstractProxyModel::mapFromSource() methods. It wasn't that hard after all! I just needed to understand good how the whole thing works! I did a simple implementation and it works fine! Hopefully I won't encounter any other difficulties now! Thanks again!

    Cheers



  • I had a similar issue. I solved it by using KDescendantsProxyModel from KDE. Other than commenting out two lines related to kdebug() and including kbihash_p.h, it worked great. :-)

    source: http://websvn.kde.org/trunk/KDE/kdepimlibs/akonadi/?pathrev=1217600
    api-ref: http://api.kde.org/4.6-api/kdepimlibs-apidocs/akonadi/html/classKDescendantsProxyModel.html



  • Hey Bekos

    I have same thing to do for my work

    @
    -A
    -A1
    -A2
    -B
    -B1
    -B2
    @

    And I want to list the leaf items in the QListView:
    @
    -A1
    -A2
    -B1
    -B2
    @

    could you please help me out how could you solve this problem or share any peace of code for it

    Thanks



  • I would adapt the KDescendantsProxyModel class referenced above for that. You can add a new role NodeLevelRole, and have the data() method return an integer representing the original level. So, in your case, node A and B would return 0 for this role, and A1, A2, B1 and B2 would return 1.

    The next step is then to create a simple proxy model that filters based on the NodeLevelRole.



  • Thanks for reply Andre but i couldn't use these classes for some reasons i have my own implementation of Proxy Model class i have even re implement the filterAcceptsRow function right now if i click on A from above mention then all child of A will be shown in QList View and i have added search box in list view there i can type A1 so all A1 will be visible in list view and rest will be not but i need to show all child at one point of click say ALL so seeking for the work around for it

    For your reference my current implementation

    @
    bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const
    {
    QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
    QString checkStr = sourceModel()->data(index, Qt::DisplayRole).toString();
    return ShowThis(index);
    }

    bool MySortFilterProxyModel::ShowThis(const QModelIndex index) const
    {
    bool retVal = false;
    //Gives you the info for number of childs with a parent
    if ( sourceModel()->rowCount(index) > 0 )
    {
    for( int nChild = 0; nChild < sourceModel()->rowCount(index); nChild++)
    {
    QModelIndex childIndex = sourceModel()->index(nChild,0,index);
    if ( ! childIndex.isValid() )
    break;
    retVal = ShowThis(childIndex);
    if (retVal)
    {
    break;
    }
    }
    }
    else
    {
    QModelIndex useIndex = sourceModel()->index(index.row(), 0, index.parent());
    QString type = sourceModel()->data(useIndex, Qt::DisplayRole).toString();
    //qDebug()<<"type is "<<type;
    if ( ! type.contains(filterRegExp()))
    {
    retVal = false;
    }
    else
    {
    retVal = true;
    }
    }
    return retVal;
    }
    @

    Edit: Please use @ tags around code sections; Andre



  • Could you please re-phrase your question, this time using punctuation and separate sentences? I have a very hard time reading your question. Note that I have added some @ tags around your code to make that readable. Please do that yourself next time.



  • Sorry for that i have first time post here so don't know this, i will take care of this from next time

    I will not use these KDescendantsProxyModel class for some reasons,

    I have my own implementation of Proxy Model class, & re-implement already filterAcceptsRow
    function for filter specific to parent in QListView.

    I have QAbstractItemModel implementation which share in two view [ QTreeView & QListView]

    Scenario - 1
    Click on QTreeView any of parent item i emit the signal and set root index of list view with that clicked index so all child with respect to that parent will show in list view.

    There is search box [QLineEdit] in which i type any char so according to this list view hide and show the items.

    ......This works fine for me

    Now i have combo box in list view which contains list of parent items of tree view with check box
    and ALL option as well , so when click on particular parent item and check it true in that combo box from list view it will fetch that child items from list view as i mention above set root index to list view and show them in list.

    But when i click on ALL i need to get all child's to list in list view but i can set root index to list view only one , ........ so how could it possible ?

    Also if i seek tree view root and find index of it and set it to list view then it will list the all parent items instead of it i will try to list all child's of that all parent items to be list.

    For that search box implementation please look that code
    @
    bool MySortFilterProxyModel::filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const
    {
    QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
    QString checkStr = sourceModel()->data(index, Qt::DisplayRole).toString();
    return ShowThis(index);
    }

    bool MySortFilterProxyModel::ShowThis(const QModelIndex index) const
    {
    bool retVal = false;
    //Gives you the info for number of childs with a parent
    if ( sourceModel()->rowCount(index) > 0 )
    {
    for( int nChild = 0; nChild < sourceModel()->rowCount(index); nChild++)
    {
    QModelIndex childIndex = sourceModel()->index(nChild,0,index);
    if ( ! childIndex.isValid() )
    break;
    retVal = ShowThis(childIndex);
    if (retVal)
    {
    break;
    }
    }
    }
    else
    {
    QModelIndex useIndex = sourceModel()->index(index.row(), 0, index.parent());
    QString type = sourceModel()->data(useIndex, Qt::DisplayRole).toString();
    //qDebug()<<"type is "<<type;
    if ( ! type.contains(filterRegExp()))
    {
    retVal = false;
    }
    else
    {
    retVal = true;
    }
    }
    return retVal;
    }
    @

    also i have tried with passing something like this proxy model

    @
    void ModelBrowser::mbUpdateListViewForFilterChanged( QString filterString )
    {
    if(filterString == "ALL")
    {
    QRegExp rx("[A-Za-z_]+([A-Za-z_0-9]*)");
    proxy->setFilterRegExp(rx);
    }
    else
    {
    QRegExp::PatternSyntax syntax = QRegExp::RegExp;

    Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
    QString strPattern = "^" + filterString;
    QRegExp regExp(strPattern, caseSensitivity);

    proxy->setFilterRegExp(regExp);
    }
    }
    @

    but still it didn't work .....

    Thank
    Prashant



  • Hello pmoglaikar,

    Unfortunately I won't be able to help much. I deleted the code of my iplementation a week after writing it. And since then, I never touched Qt again. :( As a result I remember almost nothing. What I remember is that in order to flatten my tree to a view (i.e. list only the child nodes ignoring all parents) I had to use QAbstractProxyModel::mapToSource() and QAbstractProxyModel::mapFromSource(). What you actually do in these methods is to "translate" a QModelIndex to another QModelIndex. A1 and B1 in the Tree have both Row id 0. But in the list they have row id 0 and 2 respectively. Override functions above to do this "translation".
    Keep in mind that there is a high chance I am talking nosense. I don't remember much. I haven't touched Qt for a while now.

    Cheers,
    George

    [quote author="pmoglaikar" date="1335428929"]Hey Bekos

    I have same thing to do for my work

    @
    -A
    -A1
    -A2
    -B
    -B1
    -B2
    @

    And I want to list the leaf items in the QListView:
    @
    -A1
    -A2
    -B1
    -B2
    @

    could you please help me out how could you solve this problem or share any peace of code for it

    Thanks [/quote]



  • Hi Andre,

    Any help now i am still struggling to get it done ?

    Thanks
    Prashant



  • Hi Andre,
    Any help now i am still struggling to get it done ?
    Thanks
    Prashant
    [quote author="Andre" date="1335508912"]Could you please re-phrase your question, this time using punctuation and separate sentences? I have a very hard time reading your question. Note that I have added some @ tags around your code to make that readable. Please do that yourself next time.[/quote]


Log in to reply
 

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