Results 1 to 11 of 11

Thread: Once more : QAbstractItemModel woes

  1. #1
    Join Date
    Aug 2006
    Posts
    163
    Thanks
    12
    Thanked 5 Times in 4 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Once more : QAbstractItemModel woes

    I am trying to implement a QAbstractItemModel/QTreeView MVC in a program. I have my data in the form of QLists - a list of albums, and every album contains a list of tracks.

    Everything is working, except my data() implementation never returns the right data for the children (tracks). Instead, it always returns the data for the albums, with the result that when the albums expand they list the albums yet again. There is no infinite recursion - i.e. the rowCount() implementation works. I also checked with debug prints to the console that the index() and parent() methods work as expected.

    Here is the code for the model implementation:

    Qt Code:
    1. /***************************************************************************
    2.  * Copyright (C) 2007 by Lawrence Lee *
    3.  * valheru@facticius.net *
    4.  * *
    5.  * This program is free software; you can redistribute it and/or modify *
    6.  * it under the terms of the GNU General Public License as published by *
    7.  * the Free Software Foundation; either version 2 of the License, or *
    8.  * (at your option) any later version. *
    9.  * *
    10.  * This program is distributed in the hope that it will be useful, *
    11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
    12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
    13.  * GNU General Public License for more details. *
    14.  * *
    15.  * You should have received a copy of the GNU General Public License *
    16.  * along with this program; if not, write to the *
    17.  * Free Software Foundation, Inc., *
    18.  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
    19.  ***************************************************************************/
    20. #include <KDE/KDebug>
    21. #include "kzenalbumviewmodel.h"
    22. #include "kzenalbum.h"
    23. #include "kzentrack.h"
    24.  
    25. KZenAlbumViewModel::KZenAlbumViewModel( QObject *parent, const QList<KZenAlbum*> &albums )
    26. : QAbstractItemModel( parent ), albumItems( albums )
    27. {
    28. rootItem << "Album" << "Artist" << "Genre" << "Nr. of Tracks";
    29. }
    30.  
    31. KZenAlbumViewModel::~KZenAlbumViewModel()
    32. {
    33. qDeleteAll( albumItems );
    34. }
    35.  
    36. int KZenAlbumViewModel::columnCount( const QModelIndex& /*parent*/ ) const
    37. {
    38. return 4;
    39. }
    40.  
    41. QVariant KZenAlbumViewModel::data( const QModelIndex &index, int role ) const
    42. {
    43. kDebug() << index;
    44.  
    45. if( !index.isValid() )
    46. return QVariant();
    47.  
    48. if( role != Qt::DisplayRole )
    49. return QVariant();
    50.  
    51. QObject *item = static_cast<QObject*>( index.internalPointer() );
    52.  
    53. if( item ){
    54. KZenAlbum *album = qobject_cast<KZenAlbum*>( item );
    55.  
    56. if( album ){ //Album
    57.  
    58. switch( index.column() ){
    59. case 0:
    60. return album->name();
    61. case 1:
    62. return album->artist();
    63. case 2:
    64. return album->genre();
    65. case 3:
    66. return album->numTracks();
    67. default:
    68. return QVariant();
    69. }
    70. }else{ //Track
    71. KZenTrack *track = qobject_cast<KZenTrack*>( item );
    72.  
    73. if( track ){
    74. switch( index.column() ){
    75. case 0:
    76. return track->title();
    77. case 1:
    78. return track->artist();
    79. case 2:
    80. return track->genre();
    81. case 3:
    82. return track->tracknumber();
    83. default:
    84. return QVariant();
    85. }
    86. }else{
    87. return QVariant();
    88. }
    89. }
    90. }
    91.  
    92. return QVariant();
    93. }
    94.  
    95. Qt::ItemFlags KZenAlbumViewModel::flags( const QModelIndex &index ) const
    96. {
    97. if( !index.isValid() )
    98. return 0;
    99.  
    100. return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
    101. }
    102.  
    103. QVariant KZenAlbumViewModel::headerData( int section, Qt::Orientation orientation, int role ) const
    104. {
    105. if (orientation == Qt::Horizontal && role == Qt::DisplayRole){
    106. return rootItem.value( section );
    107. }
    108.  
    109. return QVariant();
    110. }
    111.  
    112. QModelIndex KZenAlbumViewModel::index( int row, int column, const QModelIndex &parent ) const
    113. {
    114. if( !hasIndex( row, column, parent ) )
    115. return QModelIndex();
    116.  
    117. if( !parent.isValid() ){ // This is an album, and therefor a top-level item
    118. KZenAlbum *album = const_cast<KZenAlbum*>( albumItems.at( row ) );
    119.  
    120. if( album )
    121. return createIndex( row, column, album );
    122. else
    123. return QModelIndex();
    124.  
    125. }else{ //This is a track, and has an album as a parent
    126. QObject *item = static_cast<QObject*>( parent.internalPointer() );
    127. KZenAlbum *album = qobject_cast<KZenAlbum*>( item );
    128. KZenTrack *track = album->albumTracks().value( row );
    129. return createIndex( row, column, track );
    130. }
    131.  
    132. return QModelIndex();
    133. }
    134.  
    135. QModelIndex KZenAlbumViewModel::parent( const QModelIndex &index ) const
    136. {
    137. if( !index.isValid() )
    138. return QModelIndex();
    139.  
    140. QObject *item = static_cast<QObject*>( index.internalPointer() );
    141.  
    142. if( item ){
    143. KZenAlbum *album = qobject_cast<KZenAlbum*>( item );
    144.  
    145. if( album ){
    146. return QModelIndex();
    147. }else{
    148. KZenTrack *track = qobject_cast<KZenTrack*>( item );
    149.  
    150. if( track ){
    151. KZenAlbum *album = track->parent();
    152. return createIndex( albumItems.indexOf( album ), 0, album );
    153. }else{
    154. return QModelIndex();
    155. }
    156. }
    157. }
    158.  
    159. return QModelIndex();
    160. }
    161.  
    162. int KZenAlbumViewModel::rowCount( const QModelIndex &parent ) const
    163. {
    164. if( parent.column() > 0 )
    165. //Out of model bounds
    166. return 0;
    167.  
    168. if( !parent.isValid() )
    169. //We are in the root of the model
    170. return albumItems.size();
    171.  
    172. //Check to see if this is a track
    173. QObject *item = static_cast<QObject*>( parent.internalPointer() );
    174.  
    175. if( item ){
    176. KZenAlbum *album = qobject_cast<KZenAlbum*>( item );
    177.  
    178. if( album ){
    179. return album->numTracks();
    180. }else{
    181. return 0;
    182. }
    183. }
    184.  
    185. return 0;
    186.  
    187. }
    188.  
    189. #include "kzenalbumviewmodel.moc"
    To copy to clipboard, switch view to plain text mode 

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Once more : QAbstractItemModel woes

    Why are your albums and tracks QObjects?

  3. #3
    Join Date
    Aug 2006
    Posts
    163
    Thanks
    12
    Thanked 5 Times in 4 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Once more : QAbstractItemModel woes

    Why does that matter?

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Once more : QAbstractItemModel woes

    It's my curiosity. I doubt it has meanings other than the performance hit. But it's also another layer you add to the complexity increasing total "proness" to errors. Simplifing your code would make it more robust and easier to read, effectively making it easier to spot the real problem. Currently all the casts you have make it quite difficult to follow, especially not knowing how your items look like.

    BTW. Have you tried ModelTest from labs.trolltech.com?

  5. #5
    Join Date
    Aug 2006
    Posts
    163
    Thanks
    12
    Thanked 5 Times in 4 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Once more : QAbstractItemModel woes

    Sorry, I had tried ModelTest. I've solved the problem - the cause was that I was tracking the tracks parent internally in the Track class, and was calling the QObjects parent() method to get the tracks parent - which was returning NULL. Once I got rid of that it all worked. If you look at the code you will see that I was returning a new QModelIndex if the parent() call failed - which is why the recursion was happening.

    The albums and tracks need to be QObjects because the user can edit information in the program about the files on their MP3 player, and the objects need to keep track of any editing going on in the views. I thought at the beginning that it would be easiest to do this using signals, although I now think it may be better to use the QModelIndex of the edited item to achieve this. Although I am unsure how complex this would get if I enabled sorting in the views...

  6. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Once more : QAbstractItemModel woes

    Quote Originally Posted by Valheru View Post
    The albums and tracks need to be QObjects because the user can edit information in the program about the files on their MP3 player, and the objects need to keep track of any editing going on in the views.
    This is what the model-view architecture is for, not QObjects. If you want the view to be coherent with the rest of the application, you should only interface the data through the model.

    Read this: http://blog.wysota.eu.org/index.php/...ta-redundancy/

    Although I am unsure how complex this would get if I enabled sorting in the views...
    As complex as this:
    Qt Code:
    1. proxy->setSourceModel(model);
    2. view->setModel(proxy);
    To copy to clipboard, switch view to plain text mode 

  7. #7
    Join Date
    Aug 2006
    Posts
    163
    Thanks
    12
    Thanked 5 Times in 4 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Once more : QAbstractItemModel woes

    Thanks. By coincidence I'd just read up about QSortFilterProxyModel last night.

    One question I do still have though - in my code I am passing the data structures between the functions like so :

    Qt Code:
    1. KZenAlbumModel( QList<KZenAlbum*> &albums, QObject *parent = 0 ) : QAbstractItemModel( parent ), m_albums( albums )
    To copy to clipboard, switch view to plain text mode 

    My question is, is the album data being duplicated, or is a reference count just being implemented in albums? m_data is declared as :

    Qt Code:
    1. QList<KZenAlbum*> &m_albums;
    To copy to clipboard, switch view to plain text mode 

  8. #8
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Once more : QAbstractItemModel woes

    QObject's can't be copied. If you copy the list of pointers, no data gets copied. If you modify one of the lists the objects kept within it are copied. Because the list contains pointers, only those will be copied and not the objects they point at.

  9. #9
    Join Date
    Aug 2006
    Posts
    163
    Thanks
    12
    Thanked 5 Times in 4 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Once more : QAbstractItemModel woes

    Ok, I am rewriting the code now so that the albums and tracks are not QObject and I am running into problems with my model again.

    Since they are not QObject's any more, it is difficult to figure out how to write that parent() function of the model. index.internalPointer() returns a void pointer, so there is no way to figure out if you are dealing with an album or a track.
    Calling index.parent() sends my program into an infinite loop upon the creation of the model, I don't know if this is a bug in Qt or not. According to the documentation, calling index.parent() should return an invalid QModelIndex in the case of an album, but all it does is sends my program into a loop

    I am stumped, I admit. How can I figure this out? It was partly this problem in the first place that caused me to make the albums and tracks QObject, because then I could use qobject_cast to see what I was dealing with. Can I solve this cleanly, or would I have to use some workaround like providing my classes with names internally and using calls to the name() functions of the class to see what it is?

    Any help would be greatly appreciated.

  10. #10
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Once more : QAbstractItemModel woes

    Quote Originally Posted by Valheru View Post
    Since they are not QObject's any more, it is difficult to figure out how to write that parent() function of the model.
    Have a look at the "simple tree model" example that comes with Qt.

    index.internalPointer() returns a void pointer, so there is no way to figure out if you are dealing with an album or a track.
    Sure there is. static_cast() to a common base class and use dynamic_cast() or some dedicated method in the base class to decide the type of the object and then cast to the proper pointer type.

  11. #11
    Join Date
    Aug 2006
    Posts
    163
    Thanks
    12
    Thanked 5 Times in 4 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11

    Default Re: Once more : QAbstractItemModel woes

    Sorry, that is what I meant - that is of course more or less I was doing by having declared the albums and tracks as QObjects.

    And I must have read the Simple Tree Model at least a dozen times Unfortunately, it is of no help when you are doing something like this, and I suspect it is for a large part responsible for that practice that you belate on your blog - the same practice that I am trying to avoid in this program, namely that of restructuring your data into *TreeItems.

    I got it working by using polymorphism and first static_casting to the base class and then using dynamic_cast to figure out what I was dealing with. I suspect that this is the only way to return a correct value for the parent() function if your root and child objects are of differing types.

Similar Threads

  1. QAbstractItemModel newbie question
    By okellogg in forum Qt Programming
    Replies: 14
    Last Post: 18th February 2008, 12:30
  2. Checkboxes in QAbstractITemModel
    By Valheru in forum Qt Programming
    Replies: 5
    Last Post: 28th November 2007, 20:23
  3. Question on QAbstractItemModel vs. QTableView
    By lni in forum Qt Programming
    Replies: 1
    Last Post: 26th April 2007, 07:29
  4. Displaying QAbstractItemModel in a Table
    By bmesing in forum Qt Programming
    Replies: 1
    Last Post: 28th March 2007, 11:11
  5. Subclassing QAbstractItemModel
    By r366y in forum Qt Programming
    Replies: 2
    Last Post: 2nd May 2006, 08:46

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.