Program crashes after defining new role for model - c++

I tried to implement a new model that sorts numerically as described here.
It looks like this:
#ifndef NUMERICMODEL_H
#define NUMERICMODEL_H
#include <QStandardItemModel>
class NumericModel : public QStandardItemModel
{
public:
enum Role {
SortRole=Qt::UserRole
};
NumericModel() {}
~NumericModel() {}
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const {
switch ( role ) {
case Qt::DisplayRole:
return index.data().toString();
case SortRole:
return index.data().toUInt();
default:
return index.data().toString();
}
}
};
#endif // NUMERICMODEL_H
I am setting the sort role like this:
QSortFilterProxyModel * proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(&m_movesModel);
proxyModel->setSortRole(NumericModel::SortRole);
qDebug() << __LINE__;
ui->tableView_Moves->setModel(proxyModel);qDebug() << __LINE__;
ui->tableView_Moves->resizeColumnsToContents();qDebug() << __LINE__;
However my program crashes in the last line, when calling ui->tableView_Moves->resizeColumnsToContents().

why you are subleasing QStandardItemModel?
This is not needed since this model handles any kind of roles! Just set them.
Anyway your implementation lead to infinitive recursion!
The answer you provided is incomplete. You have to understand that QStandardItemModel can store any kind of data it can be QString, it can be int or double (and other types see doc). Default compare method for QSortFilterProxyModel handles all of this types so problem is of comparing strings with numbers, so problem is what type exactly QVariant stores. Linked answer tries enforce uniform type used for sorting and this solution is quite poor.
IMO it is safer to subclass QSortFilterProxyModel like that:
bool MyUIntSortFilterProxyModel::lessThan(const QModelIndex & left,
const QModelIndex & right ) const {
int cmpRole = sortRole();
return left.data(cmpRole).toUInt()<right.data(cmpRole).toUInt();
}

Thanks to the other answer I now know that my data function was incomplete. Instead of going through all possible cases manually, I just asked for my specific case and then called the data function of the base class.
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const {
if (role == SortRole) {
return index.data().toUInt();
}
return QStandardItemModel::data(index, role);
}

Related

Qt6 - Draw custom metatype in table/tree from QAbstractTableModel

If I have an overridden QAbstractTableModel that supplies a non-qt type, it's my understanding that supplying overloads for the << and >> operators will allow Qt to natively represent those types.
I have prepared an example with std::u16string in an attempt to create the most minimal test case, but can't seem to render anything.
Here's how I register the type with Qt:
#include <QtCore>
Q_DECLARE_METATYPE(std::u16string);
QDataStream& operator<<(QDataStream& out, const std::u16string& myObj)
{
return out << QString::fromStdU16String(myObj);
}
QDataStream& operator>>(QDataStream& in, std::u16string& myObj)
{
QString tmp;
in >> tmp;
myObj = tmp.toStdU16String();
return in;
}
My trivial main.cpp which connects the type to the appropriate widget:
#include <QItemEditorFactory>
#include <QLineEdit>
int main()
{
// not sure if this is required.
// this blogpost (https://www.qt.io/blog/whats-new-in-qmetatype-qvariant) suggests it's
// needed for name-to-type conversions, but no idea if that is still needed internally.
qRegisterMetaType<std::u16string>();
// tell qt that we would like to visualise std::u16string with the default text editor.
QItemEditorFactory* factory = new QItemEditorFactory;
factory->registerEditor(QMetaType::fromType<std::u16string>().id(), new QStandardItemEditorCreator<QLineEdit>());
QItemEditorFactory::setDefaultFactory(factory);
// kick off ui, etc
return doApp();
}
And my trivial model, which supplies the external type via a variant:
#include <QAbstractTableModel>
class simple_model : public QAbstractTableModel
{
public:
explicit simple_model(QObject* parent = nullptr) : QAbstractTableModel(parent) {}
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
{
return QVariant::fromValue<std::u16string>(u"Hello, World!");
}
};
Now, when I create QTableView like so:
QTableView* tableView = new QTableView;
tableView->setModel(new simple_model);
I would expect every column and row to print "Hello, World!". However, I just get a blank text box instead. Attaching my debugger to my overloaded << and >> operators shows they don't get run at all.
I feel like I'm missing a link here, but I'm not quite sure what. Some ideas about what could possibly be wrong:
Do I need to create a custom delegate and set it for each row and column which I'm returning this value? Ideally, I'd like my types to be interpreted as automatically and naturally as Qt would allow; I feel like it could create a lot of boilerplate code in my (actual, non-trivial) application.
Does QLineEdit not actually invoke any data conversions to display custom data? Perhaps there's a more appropriate text editor I should be using? I was hoping that QLineEdit would automatically convert them because it's the default QString editor; would be nice to have the exact same behaviour.
In my case, it turns out that the << and >> operators weren't needed at all. Instead, providing an appropriate meta type converter allowed for what I wanted.
Sample code for converter:
struct string_converter
{
static QString toQString(const std::u16string& value)
{
return QString::fromStdU16String(value);
}
static std::u16string tou16String(const QString& value)
{
return value.toStdU16String();
}
};
and to register these converters:
QMetaType::registerConverter<std::u16string, QString>(&string_converter::toQString);
QMetaType::registerConverter<QString, std::u16string>(&string_converter::tou16String);
Once these are provided, no other registration code appears to be needed. The simple_model seems to be able to construct a QVariant from the std::u16string and represent it with a QLineEdit without any extra boilerplate code.
However, we'll need to explicitly convert the modified data back to std::u16string in simple_model, because they get tweaked and returned as a QString from QLineEdit. Eg:
QVariant simple_model::setData(const QModelIndex& index, const QVariant& value, int role) override
{
std::u16string newData = value.value<std::u16string>();
// do something with newData
}
While this has unblocked me, I'm not sure this is preferred Qt approach as it seems to be quite different to what the documentation says (as I cited originally in the question). Any extra feedback or tips would be appreciated.

How to access QStandardItemModel and it's data using a QModelIndex?

I have a clicked()-signal which knows a selected index which is of the type QModelIndex.
void onListClicked(const QModelIndex & index) { /* ... */ }
No I want to access the data of the clicked item. I found out I can access the model using model():
void onListClicked(const QModelIndex & index)
{
QStandardItemModel * model {index.model()};
}
But this fails as the model() getter only allows me to return an QAbstractItemModel.
error: invalid conversion from 'const QAbstractItemModel*' to 'QStandardItemModel*' [-fpermissive]
How to access the QStandardItemModel or even better the selected QStandardItem? My unique identifier is stored in QStandardItem::data().
What I need is something like that:
void onListClicked(const QModelIndex & index)
{
QStandardItemModel * model {index.model()};
QStandardItem * item {model->itemFromIndex(index)};
qDebug() << item->data().toString();
}
But that does not work. Why is that so difficult. What do I miss here?
I think you can get the data directly from the model index:
void onListClicked(const QModelIndex & index) {
index.data(Qt::UserRole + 1);
// ...
}
You can use any other role to retrieve different kind of data.
Just cast it:
QStandardItemModel *model { static_cast<QStandardItemModel *>(model()); }
I had the same problem, as I need to retrieve my special model:
auto myModel=const_cast<MySpecialModel*>(dynamic_cast<const MySpecialModel*>(modelIndex.model()));
This was a perfectly working solution for me.
But, there seems to be an important issue doing so:
http://doc.qt.io/qt-5/qmodelindex.html
It says there:
A const pointer to the model is returned because calls to non-const functions of the model might invalidate the model index and possibly crash your application.
Unfortunately, the docs didn't say, why there might be a possible crash and what one shouldn't do.

sourceModel()->createIndex() in QAbstractProxyModel sub-class

I am attempting to create a proxy model that dynamically maps items from source model.
Following the implementation of QIdentityProxyModel with the intention to go from there I have discovered that it is in fact impossible to replicate it by examining the 4 core functions:
mapFromSource()
mapToSource()
index()
parent()
Consider this based on QIdentityProxyModel:
mapFromSource()
QModelIndex ProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
if(sourceIndex.isValid())
return createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer());
else
return QModelIndex();
}
mapToSource()
QModelIndex ProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
if(proxyIndex.isValid())
return sourceModel()->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());
else
return QModelIndex();
}
index()
QModelIndex ProxyModel::index(int row, int column, const QModelIndex &parent) const
{
const QModelIndex sourceParent = mapToSource(parent);
const QModelIndex sourceIndex = sourceModel()->index(row, column, sourceParent);
return mapFromSource(sourceIndex);
}
parent()
QModelIndex ProxyModel::parent(const QModelIndex &index) const
{
const QModelIndex sourceIndex = mapToSource(index);
const QModelIndex sourceParent = sourceIndex.parent();
return mapFromSource(sourceParent);
}
THE ISSUE
Problem lies in mapToSource() line
return sourceModel()->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());
The QAbstractItemModel::createIndex is protected function and cannot be used unless the caller is declared friend. That is obviously not an option without modifying the QAbstractItemModel class directly. The only alternative is to use regular QAbstractItemModel::index but that requires parent in form of QModelIndex as one of the arguments. However doing this:
return sourceModel()->index(proxyIndex.row(), proxyIndex.column(), proxyIndex.parent());
causes an infinite loop due to the fact that parent() function relies on mapToSource() and vice versa now.
The alternative approach shown for example here relies on the stored map of QPersistentModelIndex objects that are queried in the above mentioned functions. This approach while viable has several disadvantages:
It requires a lots of extra memory to store the indexes
All persistent indexes need to be updated by the source model upon every structure change (as opposed to on demand index lookup with dynamic models)
Queries to the map may be slow when iterating over kyes of the map (this can be remedied by making two identical maps in reverse at the expense of yet more memory)
Hence my questions:
Is there another way to handle the hierarchical proxy model dynamically without relying on createIndex() and without running into infinite loop of function calls?
Is it in fact necessary to create and maintain the structure of the proxy model in a storage vis-à-vis the source structure or is there a way to create a dynamic proxy model?
Thanks!
Perhaps this is a late reply, but I've just faced the same problem. I solved it by casting sourceModel() to my model, meanwhile I declared my ProxyModel as a friend.
This is my mapToSource definition:
QModelIndex ProxyModel::mapToSource(const QModelIndex& proxyIndex) const
{
Model* pModel = qobject_cast<Model*>(sourceModel());
if (!pModel || !proxyIndex.isValid()) return QModelIndex();
...
return pModel->createIndex(row, col, proxyIndex.internalPointer());
}
and the friend declaration:
class Model : public QAbstractTableModel
{
Q_OBJECT
friend class ProxyModel;
...
};
I understand your concern about using only QAbstractItemModel interface (believe me, I had the same), but let's face it the ProxyModel can only be used with my Model and nothing else (at least according to my implementation). Therefore I don't see anything bad in "friendship" like this. As for UB there shouldn't be any issues here as well, because qobject_cast would return 0 if other model other than mine was provided.
Building on the previous answers, I would like to add some alternate ways
First, the uglyest hack in the history of time itself is safe-ish on most compile:
class HackyDummy : public QAbstractItemModel
{
friend class ProxyModel;
};
QModelIndex ProxyModel::createSourceIndex(int r, int c, void *i) {
return ((HackyDummy*) sourceModel())->createIndex(r,c,i);
}
Now, of course, this is a terrible, terrible hack. It also wont compile in some compiler as the C++ spec isn't very clear (or at least as been mis-interpreted by some) on accessing parent class private and protected methods from sub-class friends. This lead us to a better plan. But first, a little detail: QIdentityProxyModel does it (almost). QIdentityModel own mapToSource/mapFromSource are public and re-implementable, so is its createIndex. So, it is possible do an internal/private QIdentityProxyModel with a method to create our little source indices from the row, column and internal pointers by accessing the hack contained inside of QIdentityProxyModel itself.
Edit: typo
I just inherited from QIdentityProxyModel and used it's mapToSource method in my mapToSourceMethod.
QModelIndex PropertyModel::mapToSource(const QModelIndex & proxyIndex) const
{
if(hasNoModel())
{
return QModelIndex();
}
QModelIndex remapped = createIndex( /* transform proxyIndex here */ );
return QIdentityProxyModel::mapToSource(remapped);
}
It means you don't end up with a cast that could crash your code should you ever change the source model type and forget to update your cast.

QModelIndex::data not working

I have a QTreeView in my application with a data model. I'm capturing when items are double clicked with the following slot:
void MainWindow::on_treeView_doubleClicked(const QModelIndex &index)
{
if (index.parent().isValid()) {
QSharedPointer<GMResource> resource;
resource = index.data(Qt::UserRole).value<QSharedPointer<GMResource> >();
Workspace::GetSingleton()->OpenResourceEditor(resource);
}
}
I expected the QModelIndex::data() method to (execute and) return the underlying QStandardItem::data() for the item referenced by that index, however its not returning anything. I set a breakpoint in my QStandardItem::data() method, and it's not even being called, so I might have incorrectly assumed what QModelIndex::data() actually returns.
How can I access the item data referenced by the QModelIndex (eg. Access to the original QStandardItem I added to the model).
Here is my data() method for my QStandardItem derived class:
virtual QVariant data( int role) const {
if (role==Qt::UserRole) {
return QVariant(resource);
}
return QStandardItem::data(role);
}
Any help would be much appreciated
I found the solution to the problem.
I replaced this code:
return QVariant(resource);
With this code:
QVariant r;
r.setValue<QSharedPointer<GMResource> >(resource);
return r;
Seems to be working as expected. I guess the data() method was being executed, but the breakpoints weren't being triggered for some reason.
You should add Q_DECLARE_METATYPE(QSharedPointer<GMResource>) to be abe to wrap QSharedPointer<GMResource> type in QVariant

How to make a constant correct read only model/view architecture in Qt?

Suppose I want to implement a model/view architecture using the QTableView and QAbstractTableModel classes. So I subclass the QAbstractTableModel to create class MyModel and implement the QAbstractTableModel interface. Then connect the instance of this model to a QTableView instance using the setModel method.
#include <QtGui/QApplication>
#include <QtGui/QTableView>
#include "mymodel.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QTableView tableView;
MyModel myModel(0);
tableView.setModel( &myModel );
tableView.show();
return a.exec();
}
But how can I make the model read only? I cannot declare
const MyModel myModel(0);
because setModel takes a non constant argument. I reimplemented only constant methods of QAbstractTableModel.
What do you mean by const in this case? What do you want to achieve?
Do you want your underlying data to be immutable - so that edition from QTableView would be impossible? Then just disallow editing the model - e.g. by not implementing setData.
Also note that the standard implementation of
Qt::ItemFlags QAbstractItemModel::flags ( const QModelIndex & index ) const
will not return Qt::ItemIsEditable which is sufficient.
You will have to take care not to modify the model outside of the UI (note that modifying it outside without sending appropriate signals can result in bad things). But as it is your code - this shouldn't be an issue.
You can't make the model constant, as there are things that views need to be able to do that affect the QAbstractItemModel object, such as creating persistent model indexes.
The best way to communicate the fact that your model is read-only to the view is by overriding QAbstractTableModel::flags to always unset ItemIsEditable:
Qt::ItemFlags MyModel::flags ( const QModelIndex & index ) const
{
return QAbstractTableModel::flags(index) & ~Qt::ItemIsEditable;
}
Well on the assumption you mean read only by the end-user, and not read-only by the programmer, this article explains that the model is only editable when you re-implement QAbstractItemModel::flags(), QAbstractItemModel::setData(), and less importantly QAbstractItemModel::setHeaderData().
Furthermore, the functions you need to reimplement for a read only model, are all const:
Qt::ItemFlags TreeModel::flags(QModelIndex const & index) const { }
QModelIndex TreeModel::index(int row, int column, QModelIndex const & parentIndex) const { }
QModelIndex TreeModel::parent(QModelIndex const & childIndex) const { }
QVariant TreeModel::data(QModelIndex const & index, int role) const { }
QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const { }
int TreeModel::rowCount(QModelIndex const & parent) const { }
int TreeModel::columnCount(QModelIndex const & parent) const { }