[SOLVED] QTableView how to limit shown rows



  • Hi,
    my Problem is I have a Table with about 40k+ rows and when I call
    @table->resizeColumnsToContents()@
    the window freezes fro 3 minutes.

    How can I make that the View only shows ~1000 rows and resizes them?
    And how can I load the next 1000 rows when I reached the end of the table?

    MyModel
    @class MyModel : public QAbstractTableModel
    {
    Q_OBJECT
    public:
    MyModel(QObject *parent = 0);
    int rowCount(const QModelIndex &parent = QModelIndex()) const ;
    int columnCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const;
    Qt::ItemFlags flags(const QModelIndex &index) const;
    void SetDataStorage(QStringList data);
    void SetDataStorage(QStringList data, int row);
    void AppendString(QString string, int row);
    QStringList GetList(int row);

    private:
    int rows;
    QMap<int,QStringList> *dataStorage;
    mutable QFile file;@

    the implementatoin
    @
    MyModel::MyModel(QObject *parent)
    :QAbstractTableModel(parent)
    {
    this->rows = 0;
    dataStorage = new QMap<int, QStringList>;
    }

    int MyModel::rowCount(const QModelIndex & /parent/) const
    {
    return this->rows;
    }

    int MyModel::columnCount(const QModelIndex & /parent/) const
    {
    return 15;
    }

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

    if (role == Qt::DisplayRole)
    {
        QStringList tmp = this->dataStorage->value(row);
        if(col < tmp.size())
            return tmp.at(col);
    }
    return QVariant();
    

    }

    QVariant MyModel::headerData(int section, Qt::Orientation orientation, int role) const
    {
    if (role == Qt::DisplayRole)
    {
    if (orientation == Qt::Horizontal) {
    switch (section)
    {
    case 0: return QString("Date");
    case 1: return QString("Time");
    case 2: return QString("source");
    case 3: return QString("define");
    case 4: return QString("data0");
    case 5: return QString("data1");
    case 6: return QString("count");
    case 7: return QString("description");
    case 8: return QString("event/\nerror");
    case 9: return QString("logging\nmethod");
    case 10: return QString("debug\nlevel");
    case 11: return QString("event\ngroups");
    case 12: return QString("internal/\nexternal");
    case 13: return QString("internal description");
    case 14: return QString("additional information");
    }
    }
    }
    return QVariant();
    }

    Qt::ItemFlags MyModel::flags(const QModelIndex & /index/) const
    {
    return Qt::ItemIsSelectable | Qt::ItemIsEnabled ;
    }

    void MyModel::SetDataStorage(QStringList data)
    {
    this->dataStorage->insert(rows,data);
    rows++;
    }

    void MyModel::SetDataStorage(QStringList data, int row)
    {
    this->dataStorage->remove(row);
    this->dataStorage->insert(row, data);
    }

    QStringList MyModel::GetList(int row)
    {
    return dataStorage->value(row);
    }

    void MyModel::AppendString(QString string, int row)
    {
    QStringList tmp = dataStorage->value(row);
    tmp.append(string);
    dataStorage->insert(row, tmp);
    }
    @

    I'm only using the normal QTableView. Didn't made my own.

    Thanks :)



  • Hi,

    I notice model::data() makes a QStringList copy each time it's called. If you get a (const) reference out of your dataStroage QMap, that would help a lot.

    Apart from that, you might double check that the QTableView really calls data() for every cell. Of course it really only needs the values for the cells which are visible, and I would hope that it only does that - but I don't know - you check with a debugger. Naturally that's where I would expect you to improve performance :- make your own QTableView if needs be.



  • ok I put a qDebug into my data() function and it goes through all of the 40k rows and 15 columns.
    When I resize a column it goes again through all rows and columns.
    Is there a way to resize only the rows that the user can see (about 30 rows)
    or make the view only show 1k rows and resize them?
    Or do you know any good Tutorials that show how to create my own View, didn't found any good on the web.

    and thanks for the reply



  • It shouldn't be too hard to make a sub-class of QTableView. There's a handy isRowHidden function you can use to limit which rows to call the model data function. You just need to figure out where this happens in the current QTableView...

    The Qt Documentation seems to be quite helpful ;-) But I've never done it myself



  • just had the same idea. hide the rows that are not in the range and show more when I scroll down or up.
    But does QTableView resize only the shown rows?
    When it would resize the hidden rows also, that wouldn't help me :(.



  • You can try to insert a proxy model between your view and your model. With the proxy model you can filter the model in order to get the 50-100 rows that have to be shown by the view.



  • bq. insert a proxy model
    Hmmm - or, just have the model check the view to see if the asked for values are visible or not. It doesn't sound very nice to me...

    bq. QTableView resize only the shown rows?
    I'm not sure if you or I have got the wrong end of the stick here. Anyway, my point is that the QTableView class itself knows which rows/columns are visible (depending on how you resize the width/height of the rows/columns and the overall widget size) and thus only asks the model for the data it needs.

    Actually I'm a little suprised (but not very) that QTableModel itself doesn't do this already. And a little nervous for you that the QTableModel is quite complicated.

    So, probably Tom's idea is the most pragmatic (even if it's not "right").



  • well I have to build a new filter for my table anyway. I will try it with the Proxy Model



  • I think the view can be configure to resize itself automatically.

    See :
    "QHeaderView::setResizeMode":http://qt-project.org/doc/qt-5.0/qtwidgets/qheaderview-compat.html#setResizeMode
    "QTableView::horizontalHeader":http://qt-project.org/doc/qt-5.0/qtwidgets/qtableview.html#horizontalHeader

    By doing so, you do not have to resize the view programmatically.



  • ok i solved it now with a proxy model
    Thanks for the help



  • Tecnova,

    Do you have a generic code of your solution that can be shared with me? I'm interested to do the same.

    I'm using QTreeWidget to display a list of items that is queried from a server. I intend to limit it to display a maximum of 50 items. If returned list of items exceed 50, idea is to present a little navigator at the bottom of that window to allow user to click next 50-100, etc.

    Seems very same to what you implement and was wondering whether you could share your solution or direct me to an example that you refer to in your implementation?

    Thanks in advance.

    Regards,

    TTK



  • Sry I dont have acces to the source code anymore.
    But I can tell you what I did.

    First of all I stoped using the QT Widget and made my own View, like you read above. Than I used the Proxy Model for filtering. The Advantage of the Proxy Model is that it is between your View(the actual data that is shown on you application) and the whole database. In the proxy model you can define what data of the database will be shown and which not.

    In my task I did create my own datastructure(database) from files I did read in. So I did know the row number. In my Filter I simply did a range of 500 rows. Than I did overwride the keyboard buttons up and down to load the next/previous 500 rows, simply by setting the minrow and maxrow +/- 500 in my Filter. I accessed the Filter by saving his reference(pointer) when i created him. and than i simly did View.refresh() or something like that to reset the view with the new filter.

    I know Views can be a bit complicated at the beginning, but they will really help you when you have to do complex things with your data.

    An other oppertunity what you can do is hide every other row.
    @void hideshow(int direction)
    {
    //not shure with that static you can also put them in a class
    static int minrow = 0;
    static int maxrow = 50;

    if(maxrow == widget.getMaxRow() || minrow == 0) // do nothing when //maxrow or minrow gets over the given data range
    return;

    for(int i = minrow; i < maxrow; i++;)
    widget.hideRow(i);

    switch(direction)
     case 1: //down
               minrow += 50;
               maxrow += 50;
               for(int j = minrow; j < maxrow; j++;)
                   widget.showRow(j);
               break; 
     case 2: //up
               minrow -= 50;
               maxrow -= 50;
               for(int j = minrow; j < maxrow; j++;)
                   widget.showRow(j);
               break;
    

    return;
    }@

    when you want to use this code you have to hide every top level row when initialising your widget except the top 50 one. You can also override some keyboard buttons and redefine they´re meaning.

    Thats all what I can help you so far. For the views you should better look up other forums or the Qt tutorials.
    I hope it helps you :/



  • Thanks for the quick response! I'll take your advice on reading up the proxy model on filtering the data. I think the address book example in QT is quite relevant. Going through that now.

    Thanks!

    Regards,

    TTK


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.