PDA

View Full Version : QSqlTableModel::SetData on different column



silmaril
30th January 2014, 20:43
I have a QSqlTableModel that works well and all the slots are firing, but I need to update a different column when an check box is clicked.

The code I have right now is below and I just can't get the model to update a different column in the index



#include <QtSql>
#include <QTextCharFormat>
#include <QtDebug>
#include <QCheckBox>

#include "includes/selectionsetmodel.h"


selectionsetSqlModel::selectionsetSqlModel(QObject *parent) : QSqlTableModel(parent)
{
}

//! [0]
QVariant selectionsetSqlModel::data(const QModelIndex &index, int role) const
{
QVariant value = QSqlQueryModel::data(index, role);
int col = index.column();

switch(role){
case Qt::DisplayRole:
if (col == 6)
{
if (value.toInt()<1024)
{
return QString(QString::number(value.toInt()) + "B");
}
else
{
return QString(QString::number(value.toInt()/1024) + "KB");
}
}
else
{
return QString(value.toString());
}
case Qt::FontRole:

// QFont boldFont;
// boldFont.setBold(true);
// return boldFont;

case Qt::BackgroundRole:

case Qt::TextAlignmentRole:
if (col == 5 or col == 6)
{
return Qt::AlignRight + Qt::AlignVCenter;
}
if (col == 2)
{
return Qt::AlignVCenter;
}
case Qt::CheckStateRole:
int selected = index.model()->data(index.model()->index(index.row(), 1), Qt::DisplayRole).toInt();
if (col == 2 )
{
if(selected==1)
{
return Qt::Checked;
}
else
{
return Qt::Unchecked;
}
}
}
return QVariant();
}

Qt::ItemFlags selectionsetSqlModel::flags ( const QModelIndex & index ) const
{
if (index.column() == 2)
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable ;
else
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

bool selectionsetSqlModel::setData(const QModelIndex& index,const QVariant& value,int role)
{

bool v;
if(role == Qt::CheckStateRole && index.column() == 2)
{
bool selected = index.model()->data(index.model()->index(index.row(), 1), Qt::DisplayRole).toBool();
v = selected ? false : true;
}
//QModelIndex idx = QModelIndex(index.row(), 1, index);
//bool r = this->setData(idx,v,role);
QModelIndex top = createIndex(index.row(), 0);
QModelIndex bottom = createIndex(index.row(), 3);

emit dataChanged(top, bottom); // emit layoutChanged() if headers changed
//qDebug() << "Now Checkable Column: " << r << " = " << v;
return true;
}

ChrisW67
31st January 2014, 00:15
Nothing in your code tries to set the value of any column. As it stands I cannot see how you could update anything at all in the table as you never call the base implementation.

I think you want column 1 of the model to hold the state of the checkbox displayed against column 2.

You want something like this (untested code):


QVariant selectionsetSqlModel::data(const QModelIndex &index, int role) const
{
...
case Qt::CheckStateRole:
if (index.column() == 2) {
const QModelIndex sibling = index.sibling(index.row(), 1);
return sibling.data(Qt::EditRole);
}
...
}

bool selectionsetSqlModel::setData(const QModelIndex& index,const QVariant& value,int role)
{
if (role == Qt::CheckStateRole && index.column() == 2) {
const QModelIndex sibling = index.sibling(index.row(), 1);
bool ret = setData(sibling, value, Qt::EditRole);
if (ret) emit dataChanged(index, index);
return ret;
}
else
return QSqlTableModel::setData(index, value, role);
}

silmaril
31st January 2014, 00:24
Thank you for the reply.

I tried the code but it still does not update the view:



bool selectionsetSqlModel::setData(const QModelIndex& index,const QVariant& value,int role)
{

{
if (role == Qt::CheckStateRole && index.column() == 2) {
bool selected = index.model()->data(index.model()->index(index.row(), 1), Qt::DisplayRole).toBool();
QVariant v = selected ? false : true;
qDebug() << v;
const QModelIndex sibling = index.sibling(index.row(), 1);

QModelIndex top = createIndex(index.row(), 0);
QModelIndex bottom = createIndex(index.row(), 3);

emit dataChanged(top, bottom); // emit layoutChanged() if headers changed
return setData(sibling, v, Qt::EditRole);
}
else
return QSqlTableModel::setData(index, value, role);
}

silmaril
31st January 2014, 04:55
Ok, looking at your code I've tried multiple options but it seems to has the same outcome.

I first tried the code you provided as is and the following code:


bool selectionsetSqlModel::setData(const QModelIndex& index,const QVariant& value,int role)
{

{
if (role == Qt::CheckStateRole && index.column() == 2) {
const QModelIndex sibling = index.sibling(index.row(), 1);
bool ret = setData(sibling, value, Qt::EditRole);
if (ret) emit dataChanged(index, index);
qDebug() << "Success: "<<ret << " = " << value;
return ret;
}
else
return QSqlTableModel::setData(index, value, role);
}


When the checkbox is checked, the value it tries to set the value to is:
QVariant(int, 0) "Success: false = QVariant(int, 0) "

When the checkbox is unchecked, the value it tried to set the value to is
QVariant(int, 2) "Success: false = QVariant(int, 2)"

In both instances the setData returns a "false".

If I change the code to the following:


bool selectionsetSqlModel::setData(const QModelIndex& index,const QVariant& value,int role)
{

{
if (role == Qt::CheckStateRole && index.column() == 2) {
//bool selected = index.model()->data(index.model()->index(index.row(), 1), Qt::DisplayRole).toBool();
const QModelIndex sibling = index.sibling(index.row(), 1);
QVariant v;
v = value.toInt() == Qt::Checked ? true : false;
bool ret = setData(sibling, v, Qt::EditRole);
if (ret) emit dataChanged(index, index);
qDebug() << "Success: "<<ret << " = " << value;
return ret;
}
else
return QSqlTableModel::setData(index, value, role);
}


The set values changes to bool with true and false, but it still fails:
Success: false = QVariant(bool, false)
Success: false = QVariant(bool, true)

Any suggestions around why setData refuses to set the SQL record?

ChrisW67
31st January 2014, 07:05
What data type is the column underlying column 1?

A complete example:


#include <QtGui>
#include <QtSql>
#include <QDebug>

class MyModel: public QSqlTableModel {
Q_OBJECT
public:
explicit MyModel(QObject *p = 0): QSqlTableModel(p) {
}

Qt::ItemFlags flags ( const QModelIndex & index ) const {
if (index.column() == 2)
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable ;
else
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

QVariant data(const QModelIndex &index, int role) const {
if (role == Qt::CheckStateRole && index.column() == 2) {
const QModelIndex sibling = index.sibling(index.row(), 1);
return sibling.data(Qt::EditRole);
}
else
return QSqlTableModel::data(index, role);
}

bool setData(const QModelIndex& index,const QVariant& value,int role) {
if (role == Qt::CheckStateRole && index.column() == 2) {
const QModelIndex sibling = index.sibling(index.row(), 1);
bool ret = setData(sibling, value, Qt::EditRole);
if (ret) emit dataChanged(index, index);
qDebug() << "Success:"<< ret << "Value:" << value;
return ret;
}
else
return QSqlTableModel::setData(index, value, role);
}
};

int main(int argc, char *argv[]) {
QApplication app(argc, argv);

QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
db.open();

QSqlQuery query;
query.exec("create table test ( col0 int, col1 int, col2 text )");
query.exec("insert into test values(101, 2, 'Row 0')");
query.exec("insert into test values(102, 2, 'Row 1')");
query.exec("insert into test values(103, 0, 'Row 2')");
query.exec("insert into test values(104, 0, 'Row 3')");
query.exec("insert into test values(105, 2, 'Row 4')");

MyModel model;
model.setTable("test");
model.select();

QTableView view;
view.setModel(&model);
view.show();
return app.exec();
}
#include "main.moc"

I am choosing to store that actual Qt::CheckState value, but you could convert to/from Y/N character or 0/1 integer etc.

silmaril
31st January 2014, 16:31
Thanks again for the reply...

I took your code and it seems to be doing the same as my code. It renders the checkbox, but it does not allow you to change the checkbox:



#include <QtGui>
#include <QtSql>
#include <QDebug>
#include <QTableView>
#include <QApplication>


class MyModel: public QSqlTableModel {
Q_OBJECT
public:
explicit MyModel(QObject *p = 0): QSqlTableModel(p) {
}

Qt::ItemFlags flags ( const QModelIndex & index ) const {
if (index.column() == 2)
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable ;
else
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

QVariant data(const QModelIndex &index, int role) const {
if (role == Qt::CheckStateRole && index.column() == 2) {
const QModelIndex sibling = index.sibling(index.row(), 1);
return sibling.data(Qt::EditRole);
}
else
return QSqlTableModel::data(index, role);
}

bool setData(const QModelIndex& index,const QVariant& value,int role) {
if (role == Qt::CheckStateRole && index.column() == 2) {
const QModelIndex sibling = index.sibling(index.row(), 1);
bool ret = setData(sibling, value, Qt::EditRole);
if (ret) emit dataChanged(index, index);
qDebug() << "Success:"<< ret << "Value:" << value;
return ret;
}
else
return QSqlTableModel::setData(index, value, role);
}
};

int main(int argc, char *argv[]) {
QApplication app(argc, argv);

QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(":memory:");
db.open();

QSqlQuery query;
query.exec("create table test ( col0 int, col1 int, col2 text )");
query.exec("insert into test values(101, 2, 'Row 0')");
query.exec("insert into test values(102, 2, 'Row 1')");
query.exec("insert into test values(103, 0, 'Row 2')");
query.exec("insert into test values(104, 0, 'Row 3')");
query.exec("insert into test values(105, 2, 'Row 4')");

MyModel model;
model.setTable("test");
model.select();

QTableView view;
view.setModel(&model);
view.show();
return app.exec();
}
#include "main.moc"




Debugging output is:


Success: false Value: QVariant(int, 2)
Success: false Value: QVariant(int, 2)
Success: false Value: QVariant(int, 2)
Success: false Value: QVariant(int, 2)
Success: false Value: QVariant(int, 0)
Success: false Value: QVariant(int, 0)
Success: false Value: QVariant(int, 0)
Success: false Value: QVariant(int, 0)
Success: false Value: QVariant(int, 0)
Success: false Value: QVariant(int, 0)
Success: false Value: QVariant(int, 0)

silmaril
31st January 2014, 21:19
Seems after many hours one will eventually figure stuff out. It seems the "Qt::DisplayRole" slot was causing me the problems.

I changed:



case Qt::DisplayRole:
if (col == 6)
{
if (value.toInt()<1024)
{
return QString(QString::number(value.toInt()) + "B");
}
else
{
return QString(QString::number(value.toInt()/1024) + "KB");
}
}
return value;


to



case Qt::DisplayRole:
if (col == 6)
{
if (value.toInt()<1024)
{
return QString(QString::number(value.toInt()) + "B");
}
else
{
return QString(QString::number(value.toInt()/1024) + "KB");
}
}
return QSqlTableModel::data(index, Qt::DisplayRole).toString();

ChrisW67
31st January 2014, 22:24
I took your code and it seems to be doing the same as my code. It renders the checkbox, but it does not allow you to change the checkbox:
My code was built and tested as working on Qt 4.8.5. I just built it with 5.2 to get the same result as you: Qt5 interprets the flags slightly differently and will not let the code update a column that is not editable. This version of flags() works with both Qt versions:


Qt::ItemFlags flags ( const QModelIndex & index ) const {
Qt::ItemFlags f = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
if (index.column() == 2)
f |= Qt::ItemIsUserCheckable;
return f;
}



Seems after many hours one will eventually figure stuff out. It seems the "Qt::DisplayRole" slot was causing me the problems.
AFAICT this has nothing to do with the column containing the checkbox state, the one displaying the checkbox, or the topic of the thread.