QTableView does not update
-
I have a desktop gui app (Qt 5.5.1, Qt Creator 3.6, Ubuntu 14.04 LTS) with a QTableView that displays two columns of data that are entered by the user.
Sorry for the length of this, I have tried to post all relevant code. I am sure it is something stupid, if anyone has the patience to read through this, please indicate my error.The data are managed by a class
class ExclModel : public QAbstractTableModel { Q_OBJECT public: explicit ExclModel(QObject *prnt=0); virtual ~ExclModel(){exclVec.clear();} void addItem(double st, double nd); exclItem *getItem(const QModelIndex &index); const exclItem *getConstItem(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE; int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE { cerr << "rowCount "<< exclVec.size() << endl; return exclVec.size(); } int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE {return 2;} QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); bool insertRows(int row, int count, const QModelIndex & parent = QModelIndex()); protected: std::vector<exclItem> exclVec; }; Note the data are contained in std::vector<exclItem> exclVec; The exclItem code is
struct exclItem
{exclItem():strtT(0.0),endT(0.0){} exclItem(double s, double e):strtT(s),endT(e){} exclItem(const exclItem &pi) { strtT=pi.strtT;endT=pi.endT; } bool setData(int column, const QVariant &value); static int size(){return 2;} double strtT, endT;
};
In my gui (created with Qt Creator gui editor), I have a QTableView that gets the exclModel via
ui->exclTable->setModel(exclMdl);
To add rows to the table (which has only two columns...), I have two spin boxes add an 'Add' button. The button event code is:
void ExerGPSMainWin::on_addExclBtn_clicked(bool checked)
{
double strt=ui->strtSpin->value();
double nd=ui->endSpin->value();
double lstT=nlm.getTime()[nlm.getTime().size()-1];
if ((nd>strt) && nd<lstT)
{
exclMdl->addItem(strt,nd);
}
}There is a range checking step, after which the addItem method is called. Here is the addItem code:
void
ExclModel::addItem(double st, double nd)
{
int rw=exclVec.size();
QModelIndex ndx = QAbstractTableModel::createIndex(rw,0);
cerr << "addItem" << ", exclVec.size "<< exclVec.size() << endl;
if (insertRows(rw,1,ndx))
{
setData(QAbstractTableModel::index(exclVec.size()-1,0),QVariant(st));
setData(QAbstractTableModel::index(exclVec.size()-1,1),QVariant(nd));
}
}And the code for insertRows:
bool
ExclModel::insertRows(int row, int count, const QModelIndex & parent)
{
bool bRet=false;
if (parent.isValid())
{
// we always insert at the end...
cerr << "insertRows" << ", exclVec.size "<< exclVec.size() << endl;
beginInsertRows(parent,row,row+count-1);
int ii;
for (ii=0;ii<count;ii++)
{
exclItem itm;itm.strtT=0.0;itm.endT=0.0;
exclVec.push_back(itm);
}
cerr << "end insertRows" << ", exclVec.size "<< exclVec.size() << endl;
endInsertRows();
bRet=true;
}
return bRet;
}You can see lots of cerr output statements. I can see that the data are being inserted, data in the model are being changed appropriately, but the table in the gui does not show the new rows. All calls to the rowCount() function show the correct number of rows in the table, but in my data() and flags() methods are never called!
QVariant
ExclModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
{
cerr << "Data index is invalid = " << index.row() << ", " << index.column() << endl;
return QVariant();
}if (index.row() >= exclVec.size()) return QVariant(); cerr << "Data role = " << role << endl; if (role == Qt::DisplayRole || role == Qt::EditRole) { cerr << "Data, row " << index.row() << ", col "<< index.column() << ", exclVec.size "<< exclVec.size() << endl; switch (index.column()) { case 0: return QVariant(getConstItem(index)->strtT); // this->paramList.at(index.row()).pName; break; case 1: return QVariant(getConstItem(index)->endT); // this->paramList.at(index.row()).pValue; break; } } else return QVariant();
}
Qt::ItemFlags
ExclModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
Qt::ItemFlags flg=QAbstractTableModel::flags(index);
cerr << "flags row "<<index.row() << ", col "<<index.column()<< ", flg " << (Int)flg << endl;
cerr << " returning "<< (Qt::ItemIsEditable | Qt::ItemIsEnabled | QAbstractTableModel::flags(index)) << endl;
return (Qt::ItemIsEditable | Qt::ItemIsEnabled | QAbstractTableModel::flags(index));
}Here is odd behavior: if I add data to the model before setting the model to the table, that row of data is shown, and calls are made to both the data() step and flags(), but never beyond row 0 after further data are added by the user. User can edit the added data, but attempts to add new never display. So, again, if anyone has gotten this far and can see an error, please let me know!
-
hi,
do you call
QAbstractItemModel::dataChanged() anywhere in setData / model ? -
Yes, my setData method looks like:
bool ExclModel::setData(const QModelIndex & index, const QVariant & value, int role) { bool bRet=false; cerr << "setData" << ", exclVec.size "<< exclVec.size() << ", role " << role << endl; if (index.isValid() && role==Qt::EditRole) { if (index.row()>exclVec.size()-1) { insertRows(index.row(),index.row()-exclVec.size(),index); } switch (index.column()) { case 0: cerr << "setData" << ", setting strtT " << getItem(index)->strtT << " to " << value.toDouble() << endl; break; case 1: cerr << "setData" << ", setting endT " << getItem(index)->endT << " to " << value.toDouble() << endl; break; } bRet=getItem(index)->setData(index.column(),value); if (bRet) { cerr << "setData emit dataChanged on "<< index.row() << ", " << index.column()<<endl; emit dataChanged(index,index); } } return bRet; }
Using the cerr outputs, I know that the data do succesfully get changed. But, again, no new data from the user appear on table. The only data that display are data that I put into the model before I link it to the table:
exclMdl->addItem(0,5); exclMdl->addItem(20,220); ui->exclTable->setModel(exclMdl);
-
BINGO!!!!
I added layoutAboutToBeChanged and layoutChanged signals to my insertRows method:bool ExclModel::insertRows(int row, int count, const QModelIndex & parent) { bool bRet=false; if (parent.isValid()) { // we always insert at the end... cerr << "insertRows" << ", exclVec.size "<< exclVec.size() << endl; emit layoutAboutToBeChanged(); beginInsertRows(parent,row,row+count-1); int ii; for (ii=0;ii<count;ii++) { exclItem itm;itm.strtT=0.0;itm.endT=0.0; exclVec.push_back(itm); } cerr << "end insertRows" << ", exclVec.size "<< exclVec.size() << endl; endInsertRows(); emit layoutChanged(); bRet=true; } return bRet; } AFAICT, the documentation does not mention this. I *think* it says you need those signals if you are doing sorting, but under the the insertRows method it says the following:
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.
For clarity and naive dumb-asses like me, it should explicitly state the need for layout... signals.