How to sort original data structure of QAbstractTableModel?
-
Hello!
I'm using QAbstractTableModel for representing data in table. Also, I'm using QSortFilterProxyModel as a wrapper for sorting. Hence, I implementlessThan
method for comparison.
Unfortunately, this sorts only the view, but I also need to sort the data structre my table takes data from. Is there a way to do it? -
@CuriousPan Qt doesn't do that but if you want to implement that feature then override the model's QAbstractItemModel::sort method and implement the sorting there.
-
@eyllanesc, well, I implemented
sort
method in QAbstractTableModel, but it's not even called. -
@CuriousPan How are they calling him or what action should he be called upon? You should no longer use QSortFilterProxyModel.
-
@CuriousPan said in How to sort original data structure of QAbstractTableModel?:
well, I implemented sort method in QAbstractTableModel, but it's not even called.
You have to call it if you want it to sort.
Also, I'm using QSortFilterProxyModel as a wrapper for sorting.
[...]
Unfortunately, this sorts only the view,No "view" involved here.
QSortFilterProxyModel
is a proxy for your model, it's a model itself. You have to access via the proxy model if you want to see things sorted, it does not sort the underlying model. -
@JonB, now I'm calling
sort
method of source model inlessThan
method. The datastructre is getting sorted, as well as the view, but view is sorted incorrectly. Isn't it strange?
Here's the view:
Printout of the situation inside the datastructure:
-
@CuriousPan said in How to sort original data structure of QAbstractTableModel?:
now I'm calling
sort
method of source model inlessThan
method.Pardon? That is not going to be a good thing to do....
but view is sorted incorrectly.
Now I'm not sure whether your view is connected to your
QSortFilterProxyModel
or your underlying (sorted) model.You had better show code --- preferably minimal --- for whatever you are doing which illustrates problem.
P.S.
If you are really calling asort()
inside alessThan()
comparison, and that changes the order of the data, your results e.g. in a view are liable to be rubbish. You must not do that! AnylessThan()
method will beconst
, and you should regard it as forbidden to change any state inside it. -
Okay, here's the code:
//m_model is a ForecastingProxyModel class inheriting from QAbstractTableModel ForecastingProxyModel *proxyModel = new ForecastingProxyModel(this); proxyModel->setSourceModel(m_model); ui->forecastingTableView->setModel(proxyModel); ui->forecastingTableView->setSortingEnabled(true);
Here's the code of the
lessThan
of the QSortFilterProxyModel:bool ForecastingProxyModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const { const QVariant &leftData = sourceModel()->data(sourceLeft); const QVariant &rightData = sourceModel()->data(sourceRight); //sourceModel()->sort(sourceLeft.column(), sortOrder()); you said it's a bad idea to call it hear. if (leftData.userType() == QMetaType::QDate) { return leftData.toDate() < rightData.toDate(); } else if (leftData.userType() == QMetaType::QString) { return leftData.toString() < rightData.toString(); } else if (leftData.userType() == QMetaType::Double) { return leftData.toDouble() < rightData.toDouble(); } else if (leftData.userType() == QMetaType::Bool) { return leftData.toBool() < rightData.toBool(); } return QSortFilterProxyModel::lessThan(sourceLeft, sourceRight); }
Here's a
sort
method of the source model:void ForecastingModel::sort(int column, Qt::SortOrder order) { switch(column) { case Name: if (order == Qt::AscendingOrder) { std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(), [](const Forecast &a, const Forecast &b) { return a.name < b.name; }); } else { std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(), [](const Forecast &a, const Forecast &b) { return a.name > b.name; }); } break; case Price: if (order == Qt::AscendingOrder) { std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(), [](const Forecast &a, const Forecast &b) { return a.price < b.price; }); } else { std::sort(m_logicController->forecasts().begin(), m_logicController->forecasts().end(), [](const Forecast &a, const Forecast &b) { return a.price > b.price; }); } break; } }
Is it enough or something more is required?
-
@CuriousPan
Well, you certainly do not want to have calledsourceModel()->sort()
insideForecastingProxyModel::lessThan()
, so that's an improvement!You don't call (or show you have called) your
ForecastingModel::sort()
, so I think that is irrelevant.Then your view is attached to your
QSortFilterProxyModel
. I would start by debugging/printing from withinlessThan()
to see which column is in theQModelIndex
es to compare so you know what it is sorting by, and maybe check the values. I don't think your "Printout of the situation inside the datastructure:" is relevant because you are accessing via theQSortFilterProxyModel
, so the ordering comes from there.While I think of it: I don't think your
lessThan()
handles anyQVariant
types not already catered for in https://doc.qt.io/qt-5/qsortfilterproxymodel.html#lessThan (except maybeBool
, but that probably degenerates correctly anyway, or isn't relevant to your data), so don't think yourlessThan()
does anything which is actually needed? -
@JonB,
After suggested debugging I found out following things:lessThan
method considers Date as String, which I find quite suprising. I can fix it by checking the data behind QModelIndex using not QMetaType, but rather column number.lessThan
doesn't recognize bool type too.lessThan
considers columns correctly.
While I think of it: I don't think your lessThan() handles any QVariant types not already catered for in https://doc.qt.io/qt-5/qsortfilterproxymodel.html#lessThan (except maybe Bool, but that probably degenerates correctly anyway, or isn't relevant to your data), so don't think your lessThan() does anything which is actually needed?
What do you exactly mean by this?
You don't call (or show you have called) your ForecastingModel::sort(), so I think that is irrelevant.
Where am I supposed to call it? Isn't it a slot wihich is called autimatically?
-
@CuriousPan said in How to sort original data structure of QAbstractTableModel?:
bool ForecastingProxyModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const
{
const QVariant &leftData = sourceModel()->data(sourceLeft);
const QVariant &rightData = sourceModel()->data(sourceRight);
//sourceModel()->sort(sourceLeft.column(), sortOrder()); you said it's a bad idea to call it hear.if (leftData.userType() == QMetaType::QDate) { return leftData.toDate() < rightData.toDate(); } else if (leftData.userType() == QMetaType::QString) { return leftData.toString() < rightData.toString(); } else if (leftData.userType() == QMetaType::Double) { return leftData.toDouble() < rightData.toDouble(); } else if (leftData.userType() == QMetaType::Bool) { return leftData.toBool() < rightData.toBool(); } return QSortFilterProxyModel::lessThan(sourceLeft, sourceRight);
This is not needed. QSortFilterProxyModel does exact the same (and honors more meta types than your implementation)
-
@Christian-Ehrlicher, do you suggest totally removing the method?
-
@CuriousPan
If you followed my link it lists:This function is used as the < operator when sorting, and handles the following QVariant types:
So you see how it says which
QVariant
types it handles and that includes the ones you handle. It also includesQMetaType::QDate
so I don't know why your "lessThan
method considers Date as String, which I find quite suprising". I don't believe you need your own override.EDIT I can see @Christian-Ehrlicher agrees.
Put
qDebug()
statements inside yoursort()
to see whether it's called. Your view is attached to theQSortFilterProxyModel
rather than the underlying model. I'm not sure, but find out which models' sorting are getting used/called. I don't thinkQSortFilterProxyModel::sort()
will callsourceModel->sort()
. I think it's confusing to have sorting on your source model and also to be usingQSortFilterProxyModel
against it.Where am I supposed to call it?
I would normally expect you to sort a (source) model immediately after you populate it if you want it sorted. So wherever that is in your code.
-
After removing
lessThan
method I have following behavior:QSortFilterProxyModel::sort()
is getting called, but not the source one, hence I have to callsource::sort()
inside it. It sorts source data, but view isn't changing.
-
@CuriousPan
I have said before: I do not know why you are using aQSortFilterProxyModel
and also doingsourceModel()->sort()
. If you want to sort the underlying model I don't know why you want the proxy too. But there you are.Meanwhile, since again you do not show your code for "hence I have to call source::sort() inside it", we do not know what you are doing. Are you/do you need to take
QSortFilterProxyModel::sortColumn()
&QSortFilterProxyModel::sortOrder()
into account?It sorts source data, but view isn't changing.
I have said before: the view is bound to the
QSortFilterProxyModel
, not the source model directly, so I don't know that any change you might or might not make in the source model's rows' order would be reflected in either theQSortFilterProxyModel
or any view bound to it.I would get it working with either the
QSortFilterProxyModel
and no source modelsort()
or source modelsort()
and noQSortFilterProxyModel
--- and possibly compare the two --- before I tried to understand what was happening with both in play.That's all I know to say.
-
After searching the internet I guess I found a solution. After sorting the data it's important to emit
dataChanged
.
Thanks everyone who tried to help me. -
@CuriousPan
That is a good point, which I should have thought about! :)I still don't know why you want both to sort the model and to have a sort proxy on top of it. You sure you want the
QSortFilterProxyModel
at all, given your desire to sort the underlying model? Best of luck. -
@JonB said in How to sort original data structure of QAbstractTableModel?:
I still don't know why you want both to sort the model and to have a sort proxy on top of it.
Because he does not understand what a QSortFilterProxyModel is doing...
-
@Christian-Ehrlicher, yeah, you are right. I was given the example with
QSortFilterProxyModel
so I thought it's mandatory to use it. I've removed it. -
Sorting the base model is normally much more expansive than using a QSortFilterProxyModel so it should be avoided.