QAbstractTableModel and dataChanged
-
Hi all,
I've maybe a really stupid question / error here, which drives me crazy.
I've read the documentation about read only models and there is nothing really concret about how to update the data. It's said, that on changes I've to emit dataChanged, which in the end doesn't update my view.
Here the model cpp code:
int SqlTableModel::rowCount( const QModelIndex& ) const { return _data.count(); } int SqlTableModel::columnCount( const QModelIndex& ) const { if( _data.count() > 0 ) return _data.first()._data.count(); else return 0; } QVariant SqlTableModel::headerData( int section, Qt::Orientation orientation, int role ) const { if( role != Qt::DisplayRole || orientation != Qt::Horizontal ) return QVariant(); return _header.at( section ); } QVariant SqlTableModel::data( const QModelIndex& index, int role ) const { if( !index.isValid() ) return QVariant(); if( role != Qt::DisplayRole ) return QVariant(); if( index.row() < _data.count() ) { if( index.column() < _data[index.row()]._data.count() ) return _data[index.row()]._data[index.column()]; else return QVariant(); } else return QVariant(); } void SqlTableModel::setQuery( QString queryStr ) { _queryStr = queryStr; SQL::execQuery( queryStr ); QSqlQuery query = SQL::getLastQuery(); int row = 0; QSqlRecord rec = query.record(); int colCount = rec.count(); _data.clear(); while( query.next() ) { SqlDataStruct stuc; for( int column = 0; column < colCount; column++ ) { stuc._data.insert( column, query.value( column ).toString() ); } _data.insert( row, stuc ); row++; } endInsertRows(); QModelIndex topLeft = index( 0, 0 ); QModelIndex bottomRight = index( row-1, colCount-1 ); emit dataChanged( topLeft, bottomRight ); }
Here the usage:
_compModel = new SqlTableModel; _compView = new QTableView; _compView->setModel( _compModel ); _compModel->setQuery( queryStr );
So, what is needed to update the view correctly after "setQuery"? Do I have to use "begin/end/InsertRows" thingy?
Best regards
-
Hi all,
I've maybe a really stupid question / error here, which drives me crazy.
I've read the documentation about read only models and there is nothing really concret about how to update the data. It's said, that on changes I've to emit dataChanged, which in the end doesn't update my view.
Here the model cpp code:
int SqlTableModel::rowCount( const QModelIndex& ) const { return _data.count(); } int SqlTableModel::columnCount( const QModelIndex& ) const { if( _data.count() > 0 ) return _data.first()._data.count(); else return 0; } QVariant SqlTableModel::headerData( int section, Qt::Orientation orientation, int role ) const { if( role != Qt::DisplayRole || orientation != Qt::Horizontal ) return QVariant(); return _header.at( section ); } QVariant SqlTableModel::data( const QModelIndex& index, int role ) const { if( !index.isValid() ) return QVariant(); if( role != Qt::DisplayRole ) return QVariant(); if( index.row() < _data.count() ) { if( index.column() < _data[index.row()]._data.count() ) return _data[index.row()]._data[index.column()]; else return QVariant(); } else return QVariant(); } void SqlTableModel::setQuery( QString queryStr ) { _queryStr = queryStr; SQL::execQuery( queryStr ); QSqlQuery query = SQL::getLastQuery(); int row = 0; QSqlRecord rec = query.record(); int colCount = rec.count(); _data.clear(); while( query.next() ) { SqlDataStruct stuc; for( int column = 0; column < colCount; column++ ) { stuc._data.insert( column, query.value( column ).toString() ); } _data.insert( row, stuc ); row++; } endInsertRows(); QModelIndex topLeft = index( 0, 0 ); QModelIndex bottomRight = index( row-1, colCount-1 ); emit dataChanged( topLeft, bottomRight ); }
Here the usage:
_compModel = new SqlTableModel; _compView = new QTableView; _compView->setModel( _compModel ); _compModel->setQuery( queryStr );
So, what is needed to update the view correctly after "setQuery"? Do I have to use "begin/end/InsertRows" thingy?
Best regards
@Lachrymology
Before you go any further: from your use ofSQL
&Sql
(you don't tell us what they are derived from) all over the place, if this is to serve a SQL database why are you attempting to useQAbstractTableModel
etc. when all the stuff is already available to you from theQSql...
classes?If you were using, say,
QSqlTableModel
yourSqlTableModel::setQuery()
wouldn't look anything like whatever you are doing here. In fact, none of it would, it's written for you! -
Hi all,
I've maybe a really stupid question / error here, which drives me crazy.
I've read the documentation about read only models and there is nothing really concret about how to update the data. It's said, that on changes I've to emit dataChanged, which in the end doesn't update my view.
Here the model cpp code:
int SqlTableModel::rowCount( const QModelIndex& ) const { return _data.count(); } int SqlTableModel::columnCount( const QModelIndex& ) const { if( _data.count() > 0 ) return _data.first()._data.count(); else return 0; } QVariant SqlTableModel::headerData( int section, Qt::Orientation orientation, int role ) const { if( role != Qt::DisplayRole || orientation != Qt::Horizontal ) return QVariant(); return _header.at( section ); } QVariant SqlTableModel::data( const QModelIndex& index, int role ) const { if( !index.isValid() ) return QVariant(); if( role != Qt::DisplayRole ) return QVariant(); if( index.row() < _data.count() ) { if( index.column() < _data[index.row()]._data.count() ) return _data[index.row()]._data[index.column()]; else return QVariant(); } else return QVariant(); } void SqlTableModel::setQuery( QString queryStr ) { _queryStr = queryStr; SQL::execQuery( queryStr ); QSqlQuery query = SQL::getLastQuery(); int row = 0; QSqlRecord rec = query.record(); int colCount = rec.count(); _data.clear(); while( query.next() ) { SqlDataStruct stuc; for( int column = 0; column < colCount; column++ ) { stuc._data.insert( column, query.value( column ).toString() ); } _data.insert( row, stuc ); row++; } endInsertRows(); QModelIndex topLeft = index( 0, 0 ); QModelIndex bottomRight = index( row-1, colCount-1 ); emit dataChanged( topLeft, bottomRight ); }
Here the usage:
_compModel = new SqlTableModel; _compView = new QTableView; _compView->setModel( _compModel ); _compModel->setQuery( queryStr );
So, what is needed to update the view correctly after "setQuery"? Do I have to use "begin/end/InsertRows" thingy?
Best regards
@Lachrymology said in QAbstractTableModel and dataChanged:
It's said, that on changes I've to emit dataChanged, which in the end doesn't update my view.
because you are not changing existing data but inserting new data. you should call
beginInsertRows
/endInsertRows
andbeginInsertColumns
/endInsertColumns
.
In any case follow @JonB 's advice and useQSqlQueryModel
orQSqlTableModel
-
@Lachrymology
Before you go any further: from your use ofSQL
&Sql
(you don't tell us what they are derived from) all over the place, if this is to serve a SQL database why are you attempting to useQAbstractTableModel
etc. when all the stuff is already available to you from theQSql...
classes?If you were using, say,
QSqlTableModel
yourSqlTableModel::setQuery()
wouldn't look anything like whatever you are doing here. In fact, none of it would, it's written for you!@JonB
I'm aware of QSqlTableModel, but I was upset about the setTable function, therefore I tried to wrote my own model. Furthermore later on I discovered the setQuery method .. mhh .. maybe I derive later on from this model. Nevertheless I needed to understand what's happening and why.@VRonin
Okay, that was what I was looking for. So I need to use the begin / insert / end system. Thanks, I will try it. -
@JonB
I'm aware of QSqlTableModel, but I was upset about the setTable function, therefore I tried to wrote my own model. Furthermore later on I discovered the setQuery method .. mhh .. maybe I derive later on from this model. Nevertheless I needed to understand what's happening and why.@VRonin
Okay, that was what I was looking for. So I need to use the begin / insert / end system. Thanks, I will try it.@Lachrymology
I will just say that there is a lot more code going on inQSqlQuery...
,QSqlTableModel
et al code that what you begin to show in yourQAbstractTableModel
attempt. Do whatever you like to learn, but consider using the supplied classes before too long....! -
@JonB
Jep, subclassing QSqlTableModel is easy and it works like expected. I've tried it now.Nevertheless - I don't get it .. the docs are saying the following:
"When subclassing QAbstractItemModel, at the very least you must implement index(), parent(), rowCount(), columnCount(), and data(). These functions are used in all read-only models, and form the basis of editable models."
Currently I've a read-only model. As shown above I've implemented these function, except index and parent.
But I'm not sure about:
"Models that provide interfaces to resizable data structures can provide implementations of insertRows(), removeRows(), insertColumns(),and removeColumns()."The "CAN" confuses me - do I have to or is it optional?
Furthermore I'm now working with: beginInsertRows/endInsertRows and beginInsertColumns/endInsertColumns
void SqlTableModel::setHeader( QStringList header ) { _header = header; beginInsertColumns( QModelIndex(), 0, _header.count() ); int c = 0; for( QString headStr : _header ) { qDebug() << "Inserted Column: " << insertColumn( c, QModelIndex() ); c++; } endInsertColumns(); } void SqlTableModel::setQuery( QString queryStr ) { _queryStr = queryStr; SQL::execQuery( queryStr ); QSqlQuery query = SQL::getLastQuery(); int row = 0; QSqlRecord rec = query.record(); int colCount = rec.count(); _data.clear(); beginInsertRows( QModelIndex(), 0, query.size() ); while( query.next() ) { SqlDataStruct stuc; for( int column = 0; column < colCount; column++ ) { stuc._data.insert( column, query.value( column ).toString() ); } _data.insert( row, stuc ); qDebug() << "Inserted Row: " << insertRow( row ); row++; } endInsertRows(); QModelIndex topLeft = index( 0, 0 ); QModelIndex bottomRight = index( row-1, colCount-1 ); qDebug() << topLeft.isValid(); qDebug() << bottomRight.isValid(); emit dataChanged( topLeft, bottomRight ); }
insertColumn and insertRow are both always false.
So, what I'm missing now?
layoutChanged, modelReset? -
@Lachrymology said in QAbstractTableModel and dataChanged:
So, what I'm missing now?
Why do you reimplement setQuery() at all? There is no need for it.
-
Uff ... I think it would be easier to have the SQL thingy removed first.
So okay, let us assume SQL stands here for Super Quarreler Laughter, which is a complex data structure needing a TableModel to be presented. Deal?
-
@JonB
Jep, subclassing QSqlTableModel is easy and it works like expected. I've tried it now.Nevertheless - I don't get it .. the docs are saying the following:
"When subclassing QAbstractItemModel, at the very least you must implement index(), parent(), rowCount(), columnCount(), and data(). These functions are used in all read-only models, and form the basis of editable models."
Currently I've a read-only model. As shown above I've implemented these function, except index and parent.
But I'm not sure about:
"Models that provide interfaces to resizable data structures can provide implementations of insertRows(), removeRows(), insertColumns(),and removeColumns()."The "CAN" confuses me - do I have to or is it optional?
Furthermore I'm now working with: beginInsertRows/endInsertRows and beginInsertColumns/endInsertColumns
void SqlTableModel::setHeader( QStringList header ) { _header = header; beginInsertColumns( QModelIndex(), 0, _header.count() ); int c = 0; for( QString headStr : _header ) { qDebug() << "Inserted Column: " << insertColumn( c, QModelIndex() ); c++; } endInsertColumns(); } void SqlTableModel::setQuery( QString queryStr ) { _queryStr = queryStr; SQL::execQuery( queryStr ); QSqlQuery query = SQL::getLastQuery(); int row = 0; QSqlRecord rec = query.record(); int colCount = rec.count(); _data.clear(); beginInsertRows( QModelIndex(), 0, query.size() ); while( query.next() ) { SqlDataStruct stuc; for( int column = 0; column < colCount; column++ ) { stuc._data.insert( column, query.value( column ).toString() ); } _data.insert( row, stuc ); qDebug() << "Inserted Row: " << insertRow( row ); row++; } endInsertRows(); QModelIndex topLeft = index( 0, 0 ); QModelIndex bottomRight = index( row-1, colCount-1 ); qDebug() << topLeft.isValid(); qDebug() << bottomRight.isValid(); emit dataChanged( topLeft, bottomRight ); }
insertColumn and insertRow are both always false.
So, what I'm missing now?
layoutChanged, modelReset?@Lachrymology said in QAbstractTableModel and dataChanged:
But I'm not sure about:
"Models that provide interfaces to resizable data structures can provide implementations of insertRows(), removeRows(), insertColumns(),and removeColumns()."
The "CAN" confuses me - do I have to or is it optional?Yes, you have to. Read e.g. https://doc.qt.io/qt-5/qabstractitemmodel.html#insertRows
Note: The base class implementation of this function does nothing and returns false.
On models that support this, inserts count rows into the model
The base does nothing but return
false
, so no insertion/deletion of rows or columns. If you have a mechanism for inserting rows into your data --- which you do --- then to make that available you must implement this, and the othervirtual
s you support.Also it concludes
If you implement your own model, you can reimplement this function if you want to support insertions. Alternatively, you can provide your own API for altering the data. In either case, you will need to call beginInsertRows() and endInsertRows() to notify other components that the model has changed.
-
@JonB
I'm confused.I have a data set/list.
Via the methods "rowCount" and "columnCount" the model provides how many rows/columns are expected.
Via "headerData" the model provides what the names of the head section are.
Via the method "data" the model is iterating over the rows and columns.
Within this it accesses the data set/list and provides it for each tupel.So far what my knowledge is, about why these are the major methods for read-only models.
If I'm using, like in the beginning, only these methods, nothing will be displayed/updated/inserted.
Ok the base of insertRow(s) returns false. In many examples this is mainly used to store external data within the internal data set/list without doing anything else than calling begin/endInsertRows. Same with insertColumn(s)
This is what I'm doing within "setQuery" and "setHeader".
If I'm looking at this example:
https://doc.qt.io/archives/4.6/itemviews-addressbook-tablemodel-cpp.htmlI don't see, what I'm missing to have my model working with the QTableView.
So again - why do I need exactly to implement such methods?
-
Found it on my own:
Within setQuery I missed calling "begin/endResetModel".
void SqlTableModel::setQuery( QString queryStr ) { _queryStr = queryStr; SQL::execQuery( queryStr ); QSqlQuery query = SQL::getLastQuery(); int row = 0; QSqlRecord rec = query.record(); int colCount = rec.count(); beginResetModel(); _data.clear(); beginInsertRows( QModelIndex(), 0, query.size() ); while( query.next() ) { SqlDataStruct stuc; for( int column = 0; column < colCount; column++ ) { stuc._data.insert( column, query.value( column ).toString() ); } _data.insert( row, stuc ); qDebug() << "Inserted Row: " << insertRow( row ); row++; } endInsertRows(); endResetModel(); QModelIndex topLeft = index( 0, 0 ); QModelIndex bottomRight = index( row-1, colCount-1 ); qDebug() << topLeft.isValid(); qDebug() << bottomRight.isValid(); emit dataChanged( topLeft, bottomRight ); }
-
I still don't understand why you implement your own setQuery() function...
/edit: and beginInsertRows()/endInsertRows() is not needed (and may kill something internally) since you already call being/endResetModel(). Cascading those calls is not supported (and not needed at all)
-
I still don't understand why you implement your own setQuery() function...
/edit: and beginInsertRows()/endInsertRows() is not needed (and may kill something internally) since you already call being/endResetModel(). Cascading those calls is not supported (and not needed at all)
@Christian-Ehrlicher
Thanks for the hint. -
Despite the fact that this topic is rather old I wanted to clear things up for myself and maybe others as well. This is the first post that solved my problem.
The main point is that "read-only" models can still be updated by your code but they are "read-only" for the user. So there is no user interaction/manipulation of the data because the corresponding methods are not implemented in the model.
When you now update your internal data e.g. a list or a table or basically any crazy internal representation of your data, you have to notify the model that you did that which in turn notifies all interested views to update them as well.
And here is the problem in the documentation Qt 6 version of model/view concepts. It mentions "read-only" models but does not talk ab out the "beginXYZ" methods. When it does talk about those it is always in the context of insert/removeRow/Column which again is not what you need and want for "read-only" models.
What you need is the beginReset and endReset methods called from where you update your internal data. Or you have to somehow fiddle with the beginInsert.../endInsert... methods if you really want to.
So in short: When implementing models that are read-only from the user's perspective you still need to notify your model when you manipulate your internal data.
For @Lachrymology and me the following would have worked. Note that there is no need for any signals to be emitted and there is no nested rows/reset method. It simply tells the model that the data is going to be reset, then resets the data and in the end it lets the model know that the reset has finished. All signals are emitted internally and my view is updated like charme : - )
void MyReadOnlyModel::setMyInternalData(QList<int> data) { beginResetModel(); m_Data = data; endResetModel(); }
-
@Lachrymology
Before you go any further: from your use ofSQL
&Sql
(you don't tell us what they are derived from) all over the place, if this is to serve a SQL database why are you attempting to useQAbstractTableModel
etc. when all the stuff is already available to you from theQSql...
classes?If you were using, say,
QSqlTableModel
yourSqlTableModel::setQuery()
wouldn't look anything like whatever you are doing here. In fact, none of it would, it's written for you!@JonB I've a tableview implemented with
QSqlQueryModel
, I have to override thesetData()
method, but I cannot find a way to refresh the view exceptsetQuery()
method. I just want to refresh a row not the whole view. The reason why I useQSqlQueryModel
is I have to fetch data from two tables. -
@JonB I've a tableview implemented with
QSqlQueryModel
, I have to override thesetData()
method, but I cannot find a way to refresh the view exceptsetQuery()
method. I just want to refresh a row not the whole view. The reason why I useQSqlQueryModel
is I have to fetch data from two tables.@ArthurPYQT
Does this have anything to do with the question you are asking in https://forum.qt.io/topic/126735/insertrow ? Don't know why you posting here and addressing me.You cannot just fetch one row from the database into the model and then expect the model to still show all rows. I don't know what this has to do with
setData()
.