PDA

View Full Version : QSortFilterProxyModel trouble - corrupting (dublicating) data in table



araglin
21st December 2008, 00:33
Here is another difficult (for me) thing with using QSortFilterProxyModel.
I have custom Delegate, custom Model, standard QTableView, standard QSortFilterProxyModel.

My custom Delegate provides a widget (based on QDialog) for editing whole row of data at once.
Widget appears on display, user edits LineEdits, then all data is updating. Works well.
But it works only if you don't use sorting. After sorting, rows are lined up in some order, but if we edit a row now, there are some bugs - edited row is dublicated - one with old data and another one with new. I think that's because of incorrect applying of QSortFilterProxyModel. Would you please look at my code to help me find out what's wrong.

main window:


Eve::Eve(QWidget* parent): QMainWindow(parent) {
setupUi(this); readSettings();

eveModel = new EveModel(this);

proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(eveModel);
proxyModel->setDynamicSortFilter(true);

tableView->setModel(proxyModel);

header = tableView->horizontalHeader();
header->setModel(eveModel);

eveDelegate = new EveDelegate(this, eveModel, proxyModel);
tableView->setItemDelegate(eveDelegate);
}

void Eve::insertRow() {
eveModel->insertRows(eveModel->rowCount(), 1);
}

void Eve::removeRow() {
eveModel->removeRows(tableView->selectionModel()->currentIndex().row(), 1);
}



Main methods of Delegate:



QWidget *EveDelegate::createEditor ( QWidget *parent,
const QStyleOptionViewItem& option,
const QModelIndex& /* index */ ) const
{
uEditor* editor = new uEditor ( parent ); // my class based on QDialog
return editor;
}

void EveDelegate::setEditorData ( QWidget *e,
const QModelIndex &index ) const
{
// ugly but works
QModelIndex index_sibling0 = index.sibling ( index.row(), 0 ); // Variable
QVariant variable = index_sibling0.model()->data ( index_sibling0, Qt::DisplayRole );
QModelIndex index_sibling1 = index.sibling ( index.row(), 1 ); // Value
QVariant value = index_sibling1.model()->data ( index_sibling1, Qt::DisplayRole );
QModelIndex index_sibling4 = index.sibling ( index.row(), 4 ); // placeToSave
QString placeToSave = index_sibling4.model()->data ( index_sibling4, Qt::DisplayRole ).toString();
uEditor *editor = static_cast<uEditor*> ( e );
editor->valueEdit->setPlainText ( value.toString() );
editor->variableEdit->setText ( variable.toString() );
if( "global" == placeToSave )
editor->checkBox->setChecked(true);
else
editor->checkBox->setChecked(false);
}

void EveDelegate::setModelData (
QWidget *e,
QAbstractItemModel *m,
const QModelIndex& index ) const
{
uEditor *editor = static_cast<uEditor*> ( e );

// getting values from Widget
QString value = editor->valueEdit->toPlainText();
QString variable = editor->variableEdit->text();
QString placeToSave;
if ( editor->checkBox->isChecked() )
placeToSave = "global";
else
placeToSave = "local";
eveModel->setData ( index, variable, value, placeToSave, value );
}



and Model's :



bool EveModel::setData ( const QModelIndex& index,
const QString variable,
const QString value,
const QString placeToSave,
const QString initValue,
int role )
{
if ( index.isValid() )
{
if ( ( *m_records ) [index.row() +1][6].isEmpty() )
( *m_records ) [index.row() +1][6] = ( *m_records ) [index.row() +1][1];
( *m_records ) [index.row() +1][0] = variable;
( *m_records ) [index.row() +1][1] = value;
( *m_records ) [index.row() +1][4] = placeToSave;
emit dataChanged ( index.sibling ( index.row()+1, 0 ), index.sibling ( index.row()+1, 6 ) );
reset();
return true;
}
return false;
}

araglin
21st December 2008, 09:41
Also, when I'm calling removeRows() method the message appears in console:
"QSortFilterProxyModel: inconsistent changes reported by source model".
But the method seems to be working.

Here are 2 variants of removeRows() method:


void Eve::removeRow() {
eveModel->removeRows(tableView->selectionModel()->currentIndex().row(), 1);
}



void Eve::removeRow() {
QModelIndex index = proxyModel->mapToSource(tableView->selectionModel()->currentIndex());
eveModel->removeRows(index.row(), 1);
}

wysota
21st December 2008, 10:15
Could we see implementation of the model?

araglin
21st December 2008, 12:23
Of course!

evemodel.cpp:


#include "evemodel.h"
#include <QProcess>
#include <QSize>

EveModel::EveModel(QObject* parent /* = 0 */ )
: QAbstractTableModel(parent) {
m_headers = new QStringList();
*m_headers << tr("Variable") << tr("Value") << tr("Origin") << tr("Description");
m_records = new QList<QStringList>();
m_delvars = new QList<QStringList>();
initListFromMem(*m_records);
}

int EveModel::rowCount(const QModelIndex& parent /*=QModelIndex() */) const {
if( parent.isValid() || ( m_records->count() == 0 ) )
return 0;
else
return m_records->count() - 1;
}

int EveModel::columnCount(const QModelIndex& parent /*=QModelIndex()*/ ) const {
Q_UNUSED(parent);
if ( m_records->count() )
return 7;
else
return 0;
}

QVariant EveModel::data(const QModelIndex& index, int role /* =Qt::DisplayRole */ ) const {
if ( !index.isValid() || !m_records->count() )
return QVariant();
QStringList record = m_records->at(index.row()+1);
if ( role == Qt::DisplayRole || role == Qt::EditRole )
return record.at(index.column());
if ( role == Qt::ToolTipRole ) {
QString tip, key, value;
tip = "<table>";
//int maxLines = record.count();
for (int i = 0; i < 4/*maxLines*/; i++) {
key = headerData(
i, Qt::Horizontal, Qt::DisplayRole).toString();
value = record.at(i);
if (!value.isEmpty())
tip += QString("<tr><td><b>%1</b>: %2</td></tr>").arg(key, value);
}
tip += "</table>";
return tip;
}
return QVariant();
}

Qt::ItemFlags EveModel::flags(const QModelIndex &index) const {
if ( !index.isValid() || !m_records->count() )
return 0;
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}

bool EveModel::setData ( const QModelIndex& index, const QVariant& value, int role /*=Qt::EditRole */ )
{
if ( index.isValid() && ( role == Qt::EditRole ) )
{
if ( ( *m_records ) [index.row() +1][6].isEmpty() )
( *m_records ) [index.row() +1][6] = ( *m_records ) [index.row() +1][index.column() ];
( *m_records ) [index.row() +1][index.column() ] = value.toString();
emit dataChanged ( index, index );
return true;
}
return false;
}

bool EveModel::setData ( const QModelIndex& index,
const QString variable,
const QString value,
const QString placeToSave,
const QString initValue,
int role )
{
if ( index.isValid() )
{
if ( ( *m_records ) [index.row() +1][6].isEmpty() )
( *m_records ) [index.row() +1][6] = ( *m_records ) [index.row() +1][1];
( *m_records ) [index.row() +1][0] = variable;
( *m_records ) [index.row() +1][1] = value;
( *m_records ) [index.row() +1][4] = placeToSave;
emit dataChanged ( index.sibling ( index.row()+1, 0 ), index.sibling ( index.row()+1, 6 ) );
reset();
return true;
}
return false;
}

QVariant EveModel::headerData(int section, Qt::Orientation orientation, int role /* =Qt::DisplayRole */ ) const {

if (orientation == Qt::Horizontal) {
if (role == Qt::DisplayRole)
return m_headers->count() > section ? m_headers->at(section) : QAbstractTableModel::headerData(section, orientation, role);
if (role == Qt::SizeHintRole) {
return QSize(1, 30);
}
}
else if (orientation == Qt::Vertical) {
if (role == Qt::DisplayRole)
return QVariant();
}
return QAbstractTableModel::headerData(section, orientation, role);
}



EveModel::~EveModel() {
m_headers->clear();
delete m_headers;
m_delvars->clear();
delete m_delvars;
m_records->clear();
delete m_records;
}

void EveModel::initListFromMem(QList<QStringList>& outputList) {
outputList.clear();
QStringList sysvars = QProcess::systemEnvironment();
QRegExp rx("([a-zA-Z0-9_]+)=(.+)");
foreach( QString str, sysvars)
{
rx.indexIn(str);
QStringList list = rx.capturedTexts();
if (!list.isEmpty())
{
list.removeFirst();
if ( !list.at(0).isEmpty() ) {
list << tr("System");
setDescription(list);
list << tr("local");
list << tr("");
list << tr("");
outputList.append(list);
}
}
}
}

void EveModel::setDescription(QStringList& list) {
if ("HOME" == list.at(0)) {
//this->homePath = list.at(1);
list << tr("This is the path to the home directory");
}
else if ("LOGNAME" == list.at(0)) {
//if( "root" == list.at(1) )
// this->isRoot = true;
list << tr("This is your logname");
}
else if ("SHELL" == list.at(0)) {
//if( "/bin/bash" == list.at(1) )
// this->isBash = true;
list << tr("Your shell");
}
else
list << tr("");
}

bool EveModel::insertRows(int row, int count, const QModelIndex& parent /* =QModelIndex()*/){
Q_UNUSED(parent);
QStringList emptyRecord;
emptyRecord << tr("NEW") << tr("VALUE") << tr("System") << tr("") << tr("") << tr("") << tr("");
beginInsertRows(QModelIndex(), row, row);
m_records->append(emptyRecord);
endInsertRows();
return true;
}

bool EveModel::removeRows( int row, int count, const QModelIndex& parent) {
//Q_UNUSED(parent);
beginRemoveRows(QModelIndex(), row, row+1);
if( (m_records->at(row+1)).at(3) != "System")
m_delvars->append(m_records->at(row+1));
m_records->removeAt(row+1);
reset();
endRemoveRows();
if ( !parent.isValid() || m_records->count() == 0)
emit disableDel();

return true;
}


I appreciate your help. Thank you!