PDA

View Full Version : How to display periodically updated data in QTableView



nickla
8th March 2011, 11:36
I am developing an application that updates the data in QTableView with apache server once per second. The server renders the data as XML table. Number of columns is constant, but the number of rows changes each time. The data in the rows may also vary.

To convert the XML into the data, I created a class TxTableData, which is used in TxTableModel (child of QAbstractTableModel). Also TxTableModel uses QTimer to update data from the server.

The problem is that if the number of lines decreases - QTableview did not react to it. When the number of rows increases - it's all right.



void TxTableModel::refreshData()
{
TxRequest request;
request.setObject("order");
request.setMethod("getlist");
request.addParam("begin_time", 60*60*4);
request.addParam("end_time", 60*4);

http.queryAsync(request);
}

void TxTableModel::parseXml(const QByteArray &xml)
{
QXmlInputSource inputSource;
QXmlSimpleReader reader;
TxSaxTableHandler handler(&m_Data, false);

inputSource.setData(xml);
reader.setContentHandler(&handler);
reader.setErrorHandler(&handler);

beginResetModel();
reader.parse(inputSource);
endResetModel();
}

void TxTableModel::httpDone(bool error)
{
if (error) {
qDebug() << http.errorString();
} else {
parseXml(http.readAll());
}
}

void TxTableModel::timerDone()
{
refreshData();
}


How to update the data in QTableview if the number of rows can change?

wysota
8th March 2011, 12:14
What does refreshData() do?

MarekR22
8th March 2011, 13:58
Read more carefully documentation of QAbstractTableModel and QAbstractItemModel!
see QAbstractItemModel::beginInsertRows, QAbstractItemModel::beginRemoveColumns and so on :)

nickla
8th March 2011, 15:56
What does refreshData() do?

This method makes async request to apache using QHttp.




// QHttp http;

TxTableModel::TxTableModel(QObject *parent) :
QAbstractTableModel(parent)
{
timer = new QTimer(this);

connect(&http, SIGNAL(done(bool)), this, SLOT(httpDone(bool)));
connect(timer, SIGNAL(timeout()), this, SLOT(timerDone()));

timer->start(1000);
}




Read more carefully documentation of QAbstractTableModel and QAbstractItemModel!
see QAbstractItemModel::beginInsertRows, QAbstractItemModel::beginRemoveColumns and so on


I get every time a new XML. New and deleted rows can be both at the beginning and at the end or the middle of the document. If I want to use QAbstractItemModel::beginInsertColumns and QAbstractItemModel::beginRemoveRows, should I remove all old rows and then insert all new rows?

Also, I think I can compare the rows and add or remove the necessary ones without clearing the entire table.

Is there another way to insert and remove these lines?

I ask this because I used a similar approach (remove all old rows, add all new rows) in working with wxWidgets. It was very slow, so I decided to switch to Qt.

MarekR22
9th March 2011, 09:43
If you are lazy and dont care about perfomance you can use QAbstractItemModel::beginResetModel and QAbstractItemModel::endResetModel.
If you are just changing some values then emit signal: QAbstractItemModel::dataChanged.

nickla
9th March 2011, 10:38
I am lazy, because it cost 10 ms for 100 rows to delete and insert. I found the answer in a brainstorming session last night.

My mistake was simple. I have not cleaned the data before I save new.

This is my code for others:



void TxTableModel::parseXml(const QByteArray &xml)
{
QXmlInputSource inputSource;
QXmlSimpleReader reader;
TxSaxTableHandler handler(&m_Data, false);

inputSource.setData(xml);
reader.setContentHandler(&handler);
reader.setErrorHandler(&handler);

QModelIndexList selectedList = parent->selectionModel()->selectedRows();

beginResetModel();
m_Data.clearData();
reader.parse(inputSource);
endResetModel();

for( int i=0; i<selectedList.count(); i++) {
parent->selectRow(selectedList.at(i).row());
}
}

wysota
9th March 2011, 11:33
Just be aware that if you reset the model, you'll lose the selection and positioning in all the views attached to the model.

nickla
9th March 2011, 12:09
I can already see it. Is there any way to restore the scroll?

wysota
9th March 2011, 12:17
Don't reset the model :)

nickla
9th March 2011, 16:59
I cant. Line change so often that it is much easier to reset the entire model than to merge

wysota
9th March 2011, 17:52
I cant. Line change so often that it is much easier to reset the entire model than to merge

Then you won't retain scrolling and selection. Simple as that. It's either easy or effective, the choice is yours.

ferrabras
15th March 2011, 20:47
I had a similar problem and even tried to retain unique data from each selected itens before modelReset and then, after modelReset I tried to search for the data in then new model and finaly select the respective position at the view. Did not work or I was doing something really wrong. I finally surrendered to the model merging process. It is working like a charm but model merging took me a while to figure out!

nickla
15th March 2011, 21:33
This is my NonMerge method:



void TxTableModel::parseXml(const QByteArray &xml)
{
QXmlInputSource inputSource;
QXmlSimpleReader reader;
TxSaxTableHandler handler(&m_Data, false);

inputSource.setData(xml);
reader.setContentHandler(&handler);
reader.setErrorHandler(&handler);

QModelIndexList selectedList = parent->selectionModel()->selectedRows();
QModelIndex scrollTopIndex = parent->indexAt(QPoint(1, 1));
QModelIndex scrollBottomIndex = parent->indexAt(QPoint(1, parent->height() - 1));

beginResetModel();
m_Data.clearData();
reader.parse(inputSource);
endResetModel();

if (scrollBottomIndex.row() == -1) {
parent->scrollToBottom();
} else {
parent->scrollTo(scrollTopIndex);
}

if (selectedList.count() > 0 && (selectedList.at(0).row() >= scrollTopIndex.row()) && (scrollBottomIndex.row() == -1 || scrollBottomIndex.row() > selectedList.at(0).row() )) {
parent->selectRow(selectedList.at(0).row());
}
}


But I really hope that you would not use this. It makes me a lot of pain to support (edit cell is something terrible) and I`m working on merge too.