Qt QAbstractItemModel function data() called with undefined role - qt

I want to create a custom list using QListView and so I had to extend QListView, QItemDelegate and QAbstractListModel and then implement the specific methods, along with QAbstractItemModel::data(const QModelIndex & index, int role = Qt::DisplayRole) const.
It displays correctly on the screen at first glance, but the problem occurs after populating the list model.,The function data(index,role) is called 4-5 times per item model with different roles (some of them undefined roles/out of range/probably random). And it occurs not only after the initialization of the list model! When I hover a list element, the view calls data(index,role) with the correct index and role but right afterwards it is called again with an unexpected role value.
It seems an awkward behavior. I couldn't find the source of the strange calls. I put logs in every method of my custom classes to be sure that I'm not calling by mistake the data(index,role) method with wrong values.
Does anyone have some ideas where to look at or why this strange calls occur?
EDIT
The source of the "strange" calls is in:
QSize CDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
{
return QSize(QItemDelegate::sizeHint(option, index));
}
somehow when calling QItemDelegate::sizeHint() it triggers data() with different role values.
In my case I have defined role values starting from 0(0,1,2,3). According to #Jens those values are reserved. Changing the starting value of my custom roles solved my problem.

Custom roles start at 0x100, 0..3 are roles defined by Qt. See the list of roles in qnamespace.h. 13 for example ist SizeHintRole.
The different role calls stem from the interns of QListView, which is trying to figure out, how the cells should be displayed. Example: If you want to change the font of a row, you simply add the switch for FontRole to your data() implementation and pass back a bold font whenever something is important and should be displayed in bold.

Related

How to set text alignment for all the data in the tableView without using FOR loop?

I have create a tableView from database. Then, how to make set text alignment for all the data in this tableView? I do not want to use for loop to set each item in the tableView since it is time consuming.
The main code is as follows:
QSqlRelationalTableModel *model= new QSqlRelationalTableModel(NULL, db);
model->setTable(dbName);
model->select();
QTableView *tableView = new QTableView(NULL);
tableView->setModel(model);
Some may suggest to make a subclass of QSqlRelationalTableModel, but this also need a for loop to set all the data. Is there any other option? Thank you.
Use QIdentityProxyModel (since 4.8 - however, the idea is the same using a QAbstractProxyModel, the former is simply a convenience class that saves you time by allowing to reimplement only the methods you need). This approach avoids using a for loop since the data is set and returned at the moment it is being requested request on a per-element basis. In other words, instead of iterating over all the data and setting the required value (i.e. an imperative approach), you modify a method in the class that provides data to the view (more of a declarative approach), and the view pulls the data when necessary on its own.
To quote the docs:
http://doc.qt.io/qt-5/qidentityproxymodel.html
The QIdentityProxyModel class proxies its source model unmodified. Because it does no sorting or filtering, this class is most suitable to proxy models which transform the data() of the source model. For example, a proxy model could be created to define the font used, or the background colour, or the tooltip etc. This removes the need to implement all data handling in the same class that creates the structure of the model, and can also be used to create re-usable components.
For our case, a possible implementation follows:
class StylingProxyModel : public QIdentityProxyModel
{
QVariant data(const QModelIndex &index, int role) const
{
if (role != Qt::TextAlignmentRole)
return QIdentityProxyModel::data(index, role);
return Qt::AlignCenter;
}
};
As you can see, the model will pass through itself the part of the data that doesn't match Qt::TextAlignmentRole, and return the desired value otherwise.
Set the QSqlRelationalTableModel as the source of this custom identity model, and set the proxy model to be the source for the view.
The "heavy artillery" option would be to subclass QStyledItemDelegate but I feel it's overkill for the case in question, being better suited for tasks that modify the view / delegate appearance in more profound ways than just setting text alignment.
See Set color to a QTableView row for further reading.

Calling QStandardItem mimeData() method

In my app I have a QTableView and a QTreeView. I need to drag from the table view and drop in the tree view. I had this working, but I reworked the app so that the GUI elements were created in C++ and this this has stopped working.
I can pick up an item from the table and drop it on the tree - all looks fine. However, the table model's mimeData() method is not called, so the dropped data is incomplete.
How do I get the drag drop operation to call the mimeData() method?
The tables' model is based on a QStandardItemModel.
Had a similar issue with my code. Make sure you override the mimeData properly. For example, some examples show the prototype as:
QMimeData *mimeData(const QList<QTreeWidgetItem *> &items) const;
while the correct prototype is:
QMimeData * mimeData(const QList<QTreeWidgetItem *> items) const;
(note the missing reference in front of items). And if you implement it incorrectly, you're not overriding the mimeData() but implementing another mimeData() function.
An easy check to make sure you're doing it right is to declare a function you want to override, but change its return type from QMimeData* to bool, such as:
bool mimeData(const QList<QTreeWidgetItem *> items) const;
If you are overriding a correct function, your code will not compile due to conflicting return type (you're not allowed to override only the return type). If your code compiles fine, you are not overriding anything but instead declaring a new function. Check the function signature.

QAbstractItemView asks model for invalid index

I'm writting a program using Qt5 which works fine on Linux but on Windows I was observing strange behaviour:
When QTreeView::setModel was called, it asked model for index (QAbstractItemModel::index) with some row and col and invalid parent.
It never happend on Linux, view always asked for hasChildren, rowCount etc before calling index.
I've downloaded Qt5's sources to see what's going on and I can see:
// These asserts do basic sanity checking of the model
Q_ASSERT_X(d->model->index(0,0) == d->model->index(0,0),
"QAbstractItemView::setModel",
"A model should return the exact same index "
"(including its internal id/pointer) when asked for it twice in a row.");
Q_ASSERT_X(!d->model->index(0,0).parent().isValid(),
"QAbstractItemView::setModel",
"The parent of a top level index should be invalid");
I cannot find a single word about those sanity checks in documentation of view classes nor model classes.
Where are they defined?
Another interesting thing here is that I could deduce by observation of model/view classes I've written that top index should be invalid but I couldn't find this information directly in docs.
From the documentation of QAbstractItemModel::parent():
If the item has no parent, an invalid QModelIndex is returned.
This means that calling index() with an invalid QModelIndex asks for top level items.
The sanity checks you encountered may have been disabled in Linux (maybe release build?) - but your model's functionality should in no way depend on the order of function calls.
If index() is called with invalid row / column parameters (also if your model is not populated yet), return QModelIndex().

QTreeView & QAbstractItemModel & insertRow

I'm trying to implement QAbstractItemModel for QTreeView. I have problem with inserting rows.
I noticed that if I insert at the beginning of my application all works fine. But If I insert rows later - after some other operations (like selections etc.) new items stay invisible. Moreover QTreeView seems to doesn't work at all! Do I have to emit some signals to notify QTreeView about rows insertion?
This is my insertion method:
bool LayersModel::insertRows(int position, int count, const QModelIndex & parent)
{
LayersModelItem * parentItem = getItem(parent);
if (position > parentItem->childCount())
return false;
beginInsertRows(parent,position,position+count-1);
bool result = true;
for (;count;--count)
result &= parentItem->insertChildren(position, new LayersModelItem());
endInsertRows();
return result;
}
LayersModelItem is class with QList with its children and data.
Full code of my project (KDE libs needed) is here:
https://github.com/coder89/PhotoFramesEditor/tree/v0.0.8
To see the problem select one of blue item on main window and then right-click on it and select "Delete item". (this method is in Canvas::removeItems()) and it is completly commented - I'm desperate and I've tried to find reason of this problem... (in fact it wasn't delete anything - it adds new item).
Thanks for any help & advice!
Just a quick guess, the QT Doc for QAbstractItemModel says...
The model emits signals to indicate
changes. For example, dataChanged() is
emitted whenever items of data made
available by the model are changed.
Changes to the headers supplied by the
model cause headerDataChanged() to be
emitted. If the structure of the
underlying data changes, the model can
emit *layoutChanged() to indicate to
any attached views that they should
redisplay any items shown, taking the
new structure into account*.
So i guess, you need to emit layoutChanged() signal from your model (whenever you change the data in model) in order to update connected views.
Also read the QT docs for model view architecture, how it is implemented in QT
see if that helps, if it doesn't i will try to download your code and debug it and see, what's wrong.
Good Luck

Qt: QAbstractItemModel and 'const'

I'm trying to use a QTreeView for the first time with QAbstractItemModel and instantly have a problem. QAbstractItemModel interface declares methods as const, assuming they will not change data. But I want the result of a SQL query displayed, and returning data for a record with specified index requires the use of QSqlQuery::seek() which is non-const. Are there any 'official' guidelines to using a QAbstractItemModel with data that must be changed in order to get the number of items, data per item etc? Or must I hack C++ with const casts?
You can get away without any const casts by holding a pointer to the QSqlQuery; your pointer won't change, only the value to which you point, hence the operation will still be considered "const".

Resources