PDA

View Full Version : QTtableView how to display aggregate in the bottom



cpuinthebrain
9th May 2017, 21:15
Hi,

I want to make a sales statement and display data of many sales in QTableView.
The problem is that I can't find a way to make in the bottom aggregate.I want to display the sum of some of the columns.
What is the right approach to do this.Mono has this feature and I expect Qt as much better framework to have it as well.

d_stranz
10th May 2017, 17:11
Probably the most straightforward way is to make a proxy model for your sales data model. You will need to reimplement the rowCount() and data() methods.

If there are N lines of sales information in your source model, then your proxy model will return N+1 for rowCount(). For the data method, if the row number in the QModelIndex is one of your sales data lines, then you just return the result from the source model. If the row number is line N, then you return the appropriate sum for the column specified in the QModelIndex column.

You can either compute your sums on-the-fly (in the data() method) or you can connect to the source model's signals that indicate that the model data has changed, and compute and store the sums in your proxy. It probably won't make any difference how you do it, because each time the source model changes, you will recompute and redisplay it all anyway.

cpuinthebrain
11th May 2017, 18:10
Thank you very much.

Despite of your kind answer I find it very , very inconvenient.This is some super common feature when make financial statements.
There is another BIG problem, the SUM row should be displayed always on the bottom of the QTableView INDEPENDENT of the SCROLL!
Where we displayed large data, nobody wants to scroll 10 meters in order to see the sum.

d_stranz
11th May 2017, 20:35
Sorry that you find the answer inconvenient. Maybe in the future you can ask questions on a different forum where all of the answers are convenient for your needs.

It is probably also very inconvenient, but you could create a second QTableView to hold the sums. Put the two table views in a vertical layout and fix the size of the bottom (sum) table to be one row high. Turn off its column headers. You will need to add a proxy for the sum table that returns a row count of 1 and returns the sum of the rows in the other table in the data() method. If the top table has a vertical scroll bar, this could cause the bottom table to be misaligned with the top. In that case you can set the vertical scroll bar policy for the bottom table to be always on or to match with visibility of the top scroll bar.

Sorry that Qt doesn't have every feature implemented for your convenience. Sometimes you actually have to write your own widgets if what you want isn't available in the standard distribution.

cpuinthebrain
13th May 2017, 20:35
Thank you for your answer.
This approach sound good and reasonable.
I understand your sarcasm , I like QT and I use LGPL version, but I can't imagine to pay for the framework and such a feature to miss.... Qt is a mature framework with huge amount of followers and participants , I don't acuse anybody, I'm just wandering how such a feature can miss.

d_stranz
14th May 2017, 00:27
I'm just wandering how such a feature can miss.

Because Qt is a general-purpose GUI framework, it cannot do everything for everyone. While such a feature might be important for financial reporting, most uses for table views are not reports, they are displays of tables of information. The Qt table views are not spreadsheets; they have no ability to calculate sums, do not have a macro language, or other features of reports or spreadsheets.

What makes Qt such a popular framework is not only what it offers built-in, it is the fact that if what is built-in is not enough for your needs, you can add to it by deriving new classes from Qt classes, modify it by changing the Qt source code, or combine different parts of it to make more complex GUIs.

If you like what you see in Mono, then maybe that would be a better platform, with the help of Qyoto (https://techbase.kde.org/Languages/Qyoto) or QtSharp (https://gitlab.com/ddobrev/QtSharp).

kef
20th July 2017, 16:07
Probably the most straightforward way is to make a proxy model for your sales data model. You will need to reimplement the rowCount() and data() methods.

Thanks for this idea, I was able to use my proxy to re-format the data as I needed.

I also need to display a "Total:" additional custom row at the bottom of the table and I ran into some issues:



int DecoratorProxyModel::rowCount(const QModelIndex &parent) const
{
return sourceModel()->rowCount(parent) + 1;
// return QSortFilterProxyModel::rowCount(parent) + 1;
}


Both of these implementations correctly supply the view with the row count info. However, even if the view correctly draws the table cells, it doesn't populate it with the data, nor it allows me to interact with this new row (election etc.). Digging deeper, I found that the proxy's data() method isn't ever called by the view for this additional row:



QVariant DecoratorProxyModel::data(const QModelIndex &index, int role) const
{
qint32 row = index.row();
qint32 col = index.column();

qDebug() << "row: " << row << "col:" << col;

if (role != Qt::DisplayRole) {
if (row < QSortFilterProxyModel::rowCount(QModelIndex())) {
return QSortFilterProxyModel::data(index, role);
}
return QVariant();
}

// if (row == QSortFilterProxyModel::rowCount(QModelIndex())) {
return QVariant(QString("Total:"));
// }
// return QSortFilterProxyModel::data(index, role);
}


This implementation expectedly replaces all of the output to QString("Total:") except for the additional custom row:

12521

What could I be missing?

d_stranz
20th July 2017, 17:56
What could I be missing?

I am guessing that your proxy model is relying on the source model for the row count - does your override of rowCount() ever get called?

Have you also overridden the QSortFilterProxyModel::filterAcceptsRow() protected method?

kef
21st July 2017, 13:27
...does your override of rowCount() ever get called?

Yes, it gets called properly. I modified my override to confirm:



int DecoratorProxyModel::rowCount(const QModelIndex &parent) const
{
qDebug() << "proxy rowCount() call, sourceModel():" << sourceModel()->rowCount(parent) << "base class rowCount():" << QSortFilterProxyModel::rowCount(parent);
qDebug() << "modified rowCount() response:" << sourceModel()->rowCount(parent) + 1;
return sourceModel()->rowCount(parent) + 1;
// return QSortFilterProxyModel::rowCount(parent) + 1;
}


Both implementations, using the source model rowCount() and using the base class rowCount() report the same number of rows.

Debug output:


> Source model initModel(), rows: 7
proxy rowCount() call, sourceModel()->rowC: 7 base class rowCount(): 7
modified rowCount() response: 8
proxy rowCount() call, sourceModel(): 7 base class rowCount(): 7
modified rowCount() response: 8
proxy rowCount() call, sourceModel(): 7 base class rowCount(): 7
modified rowCount() response: 8
proxy rowCount() call, sourceModel(): 7 base class rowCount(): 7
modified rowCount() response: 8
proxy rowCount() call, sourceModel(): 7 base class rowCount(): 7
modified rowCount() response: 8
proxy rowCount() call, sourceModel(): 7 base class rowCount(): 7
modified rowCount() response: 8
proxy rowCount() call, sourceModel(): 7 base class rowCount(): 7
modified rowCount() response: 8



Have you also overridden the QSortFilterProxyModel::filterAcceptsRow() protected method?

No, I didn't touch the QSortFilterProxyModel::filterAcceptsRow() method. In fact, I've read Qt docs and found out that it is better to use QIdentityProxyModel if I just want to modify the output. So, I tried subclassing identity proxy overriding only rowCount() and data() methods with the same code I did for sortfilter proxy, but it didn't help, the view doesn't call the data() method for that additional virtual rowCount()+1 row.

I also tried fiddling with the QSortFilterProxyModel::flags(), but seems like it doesn't get called for the virtual row either.


Qt::ItemFlags DecoratorProxyModel::flags(const QModelIndex &index) const
{
qint32 row = index.row();
qint32 col = index.column();
qDebug() << "flags():" << row << col;
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}


And I thought I understood how Qt's Model View framework works :-)