PDA

View Full Version : Custom Model Class



mattjgalloway
3rd June 2007, 09:55
Hi,

I'm trying to figure out how best to make my own model class but I'm going round in circles figuring out what to inherit from and what to reimplement.

Currently I have it inheriting from QStandardItemModel like so:


class PlayersModel : public QStandardItemModel
{
Q_OBJECT
public:
PlayersModel(QObject* parent = 0);
};

And then the constructor is set up like so:


PlayersModel::PlayersModel(QObject* parent) : QStandardItemModel(0, 2, parent)
{
this->setHeaderData(0, Qt::Horizontal, QObject::tr("Name"));
this->setHeaderData(1, Qt::Horizontal, QObject::tr("Email"));
}

But then I am aware that the user of PlayersModel could use the add column functions which I don't want them to be able to do. I simple want to define a list of name and email address (and maybe other things later).

Can anyone point me in the right direction of a tutorial (or just simply help by giving me a bit of code to help) which I can use to learn this? I've tried the model view tutorial in the Qt docs but it doesn't really explain what I need.

I would also like to be able to perform drag and drop between table view classes using the same model, which I have no idea how to implement at the moment but am keeping that in mind for now incase it has a bearing on how to implement my model class.

Thanks so much guys!

marcel
3rd June 2007, 10:42
Here' what you need to make your own model:


Subclassing
Note: Some general guidelines for subclassing models are available in the Model Subclassing Reference (http://www.qtcentre.org/forum/model-view-model-subclassing.html).
When subclassing QAbstractItemModel, at the very least you must implement index (http://www.qtcentre.org/forum/qabstractitemmodel.html#index)(), parent (http://www.qtcentre.org/forum/qabstractitemmodel.html#parent)(), rowCount (http://www.qtcentre.org/forum/qabstractitemmodel.html#rowCount)(), columnCount (http://www.qtcentre.org/forum/qabstractitemmodel.html#columnCount)(), and data (http://www.qtcentre.org/forum/qabstractitemmodel.html#data)(). These functions are used in all read-only models, and form the basis of editable models.
You can also reimplement hasChildren (http://www.qtcentre.org/forum/qabstractitemmodel.html#hasChildren)() to provide special behavior for models where the implementation of rowCount (http://www.qtcentre.org/forum/qabstractitemmodel.html#rowCount)() is expensive. This makes it possible for models to restrict the amount of data requested by views, and can be used as a way to implement lazy population of model data.
To enable editing in your model, you must also implement setData (http://www.qtcentre.org/forum/qabstractitemmodel.html#setData)(), and reimplement flags (http://www.qtcentre.org/forum/qabstractitemmodel.html#flags)() to ensure that ItemIsEditable is returned. You can also reimplement headerData (http://www.qtcentre.org/forum/qabstractitemmodel.html#headerData)() and setHeaderData (http://www.qtcentre.org/forum/qabstractitemmodel.html#setHeaderData)() to control the way the headers for your model are presented.
Note that the dataChanged (http://www.qtcentre.org/forum/qabstractitemmodel.html#dataChanged)() and headerDataChanged (http://www.qtcentre.org/forum/qabstractitemmodel.html#headerDataChanged)() signals must be emitted explicitly when reimplementing the setData (http://www.qtcentre.org/forum/qabstractitemmodel.html#setData)() and setHeaderData (http://www.qtcentre.org/forum/qabstractitemmodel.html#setHeaderData)() functions, respectively.
Custom models need to create model indexes for other components to use. To do this, call createIndex (http://www.qtcentre.org/forum/qabstractitemmodel.html#createIndex)() with suitable row and column numbers for the item, and supply a unique identifier for the item, either as a pointer or as an integer value. Custom models typically use these unique identifiers in other reimplemented functions to retrieve item data and access information about the item's parents and children. See the Simple Tree Model (http://www.qtcentre.org/forum/itemviews-simpletreemodel.html) example for more information about unique identifiers.
It is not necessary to support every role defined in Qt::ItemDataRole (http://www.qtcentre.org/forum/qt.html#ItemDataRole-enum). Depending on the type of data contained within a model, it may only be useful to implement the data (http://www.qtcentre.org/forum/qabstractitemmodel.html#data)() function to return valid information for some of the more common roles. Most models provide at least a textual representation of item data for the Qt::DisplayRole (http://www.qtcentre.org/forum/qt.html#ItemDataRole-enum), and well-behaved models should also provide valid information for the Qt::ToolTipRole (http://www.qtcentre.org/forum/qt.html#ItemDataRole-enum) and Qt::WhatsThisRole (http://www.qtcentre.org/forum/qt.html#ItemDataRole-enum). Supporting these roles enables models to be used with standard Qt views. However, for some models that handle highly-specialized data, it may be appropriate to provide data only for user-defined roles.
Models that provide interfaces to resizable data structures can provide implementations of insertRows (http://www.qtcentre.org/forum/qabstractitemmodel.html#insertRows)(), removeRows (http://www.qtcentre.org/forum/qabstractitemmodel.html#removeRows)(), insertColumns (http://www.qtcentre.org/forum/qabstractitemmodel.html#insertColumns)(), and removeColumns (http://www.qtcentre.org/forum/qabstractitemmodel.html#removeColumns)(). When implementing these functions, it is important to notify any connected views about changes to the model's dimensions both before and after they occur:
·An insertRows (http://www.qtcentre.org/forum/qabstractitemmodel.html#insertRows)() implementation must call beginInsertRows (http://www.qtcentre.org/forum/qabstractitemmodel.html#beginInsertRows)() before inserting new rows into the data structure, and it must call endInsertRows (http://www.qtcentre.org/forum/qabstractitemmodel.html#endInsertRows)() immediately afterwards.
·An insertColumns (http://www.qtcentre.org/forum/qabstractitemmodel.html#insertColumns)() implementation must call beginInsertColumns (http://www.qtcentre.org/forum/qabstractitemmodel.html#beginInsertColumns)() before inserting new columns into the data structure, and it must call endInsertColumns (http://www.qtcentre.org/forum/qabstractitemmodel.html#endInsertColumns)() immediately afterwards.
·A removeRows (http://www.qtcentre.org/forum/qabstractitemmodel.html#removeRows)() implementation must call beginRemoveRows (http://www.qtcentre.org/forum/qabstractitemmodel.html#beginRemoveRows)() before the rows are removed from the data structure, and it must call endRemoveRows (http://www.qtcentre.org/forum/qabstractitemmodel.html#endRemoveRows)() immediately afterwards.
·A removeColumns (http://www.qtcentre.org/forum/qabstractitemmodel.html#removeColumns)() implementation must call beginRemoveColumns (http://www.qtcentre.org/forum/qabstractitemmodel.html#beginRemoveColumns)() before the columns are removed from the data structure, and it must call endRemoveColumns (http://www.qtcentre.org/forum/qabstractitemmodel.html#endRemoveColumns)() immediately afterwards.
The private signals that these functions emit give attached components the chance to take action before any data becomes unavailable. The encapsulation of the insert and remove operations with these begin and end functions also enables the model to manage persistent model indexes (http://www.qtcentre.org/forum/qpersistentmodelindex.html) correctly. If you want selections to be handled properly, you must ensure that you call these functions.



As for examples, have you checked the Interview example? Or the Item views example( they are all in the demos ).

Regards

mattjgalloway
3rd June 2007, 11:52
Hey, many thanks for the help there - I shall look into that.

I'm just going round in circles at the moment because I want to be able to add and remove to this model using a button underneath a qtableview which is displaying it. But I can't work out how on earth to do this properly. Do I have slots in the model for this? Or in the tableview? Because the model needs to know where to insert / delete the item... which needs access to the view displaying it...

ARGH!

fullmetalcoder
3rd June 2007, 14:10
I'm just going round in circles at the moment because I want to be able to add and remove to this model using a button underneath a qtableview which is displaying it. But I can't work out how on earth to do this properly. Do I have slots in the model for this? Or in the tableview? Because the model needs to know where to insert / delete the item... which needs access to the view displaying it...

Could you make yourself a little clearer? If you want to modify the content of your model you MUST specify where you want to add (or remove) rows (or columns). I've not used QStandardItemModel myself because I thought having full control over node types would better fit my needs. It does not mean however that you can't use this class, although it is rumored to be buggy...

What is particularly puzzling in what you are saying is that you seem to be looking for a way to make the model magically guess what should be added/removed just by pressing a button... :rolleyes: Would be a neat feature but I'm afraid, even Trolltech is unable to craft such a thing...

mattjgalloway
3rd June 2007, 15:29
Hehe, I know that it won't be able to magically know what wants deleting, but what I am trying to do is have a QTableView with two buttons beneath it, Add and Remove. These should add rows to my derived model class. So what slot should these buttons be connected to? Slots of the model, or of the table view? And I assume I have to implement the slot myself? I can't seem to find any slots in QTableView which automatically do this anyway.

I'm getting there with implementing the model, I'm now deriving from QAbstractItemModel where each item is a "Player" which contains a QString for name and a QString for email, etc.

wysota
4th June 2007, 06:25
So what slot should these buttons be connected to? Slots of the model, or of the table view?
Custom slot of the widget which holds the model or the view variable.


And I assume I have to implement the slot myself?
You assume correct. Any by this assumptions you should have already answered your previous question - if we're talking about a custom slot, it has to be in a class you're just creating...


I can't seem to find any slots in QTableView which automatically do this anyway.
The button doesn't emit any parameters when clicked, so how would you want the view that only displays the model to know what you want to add to the model?

mattjgalloway
4th June 2007, 10:10
Well exactly... if the button doesn't emit any parameters, that's why I was confused as to how to implement the adding.

So... I think I've worked it out... in that I have to add a slot into the mainWindow class (which holds the model, view and button) and have a slot there for addPlayer, removePlayer which looks at the current index of the view and then adds to the model at the correct place. I'm guessing that looks reasonable?

wysota
4th June 2007, 12:28
Yes, it's fine.

mattjgalloway
4th June 2007, 12:58
Argh I'm now coming across another problem...

My QTableView which I am using to view the model can't see to edit the model! I've implemented a flags() function which is like so:


if (!index.isValid())
return Qt::ItemIsEnabled;

return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;


So that should return that it's editable, selectable, etc. But when I try to click on a cell, nothing happens, it doesn't turn blue or allow me to edit, nothing!

Any ideas?

wysota
4th June 2007, 14:15
Did you implement the model to return some data when asked for EditRole? Did you set the edit triggers for the view that fit your needs?

mattjgalloway
4th June 2007, 16:40
I think I figured out why it wasn't working - can't remember what it was now, but it seems to be fine.

Now I have yet another problem... I have switched to using a QTreeView because that seems actually to be more what I want. I don't actually have a tree structure, but the view looks better than a QTableView for my functionality.

But my custom model I have created doesn't work properly. Basically whenever I add an item, it gets put into the list, but also as a child of itself, and thus a child of that, and so on and so on. Adding another new entry makes that a child of the first, and etc, etc. It's really odd.

I'm fairly sure this is something to do with the way my index() and parent() functions are defined. Currently they are thus:



QModelIndex PlayersModel::index(int row, int column, const QModelIndex &parent) const
{
return createIndex(row, column);
}

QModelIndex PlayersModel::parent(const QModelIndex &index) const
{
return QModelIndex();
}


Any ideas what I am doing wrong?

fullmetalcoder
4th June 2007, 17:13
The index() function shouldn't return a "blank" index like this... Index created by it are used by the view to iterate over your data and display it properly. The great big thing here is to clearly define how you associate model indexes with your underlying data. QModelIndex provides a way of associating either a void* or an integer which can be retrieved later on (mainly in functions like
data(), flags(), setData(), ... but not only). What's wrong with your code is that you discard the parent passed to createIndex()...

If this parent is not valid it means that the (row, col) are relative to the root of the tree. Otherwise they are children of the parent index passed. and QTreeView keeps adding children. To fix this just use this code (I'mm assuming that you really don't use any tree structure here...)


QModelIndex PlayersModel::index(int row, int column, const QModelIndex& parent)
{
if ( parent.isValid() )
return QModelIdex();
return createIndex(row, column);
}

mattjgalloway
4th June 2007, 17:30
Thanks! I had literally just figured it out as well, but went with:



if(parent == QModelIndex())
return createIndex(row, column);
else
return QModelIndex();


Is your code more valid? It looks like it'll do pretty much the same thing. I'm just trying to see if the code I came up with has any obvious design flaw you see, as I'm trying to learn good programming technique at the same time as coding.

Many thanks!