PDA

View Full Version : How to create dependency between 2 columns using combobox delegates in MVC



uzairsaeed702
2nd March 2015, 10:52
Hello,

I have a 5 column Model inherited by "QAbstractTableModel" because of QTableView as View. The last two columns contains combo boxes which are populated through their delegates inherited by "QStyledItemDelegate". Everything is working fine independently.

Next Task:
Column 4 contains some mode which is related to column 5 in terms of respective functions,(refer the below figure) e.g. if I select some item in the (col-4) combo box, it will change/create the col-5 combo box according to the provided functions from Model. Which means every time when I receive the signal of datachange in column 4, it has to call the createWidget function from the delegate class in order to create a new combo box and its items. I tried some ways to call createWidget manually when I have received the datachange but as the Model is running with specific index.col I can not change the data of column 5 while changing settings in column 4.

10977

above figure shows the example what i want to do , Like if I select the Decrement mode it will fetch the data(done) and create a new combo box or update the data of the combo box in column 5. I have tried many logic and algo to change things from setEditorData & setModelData though the only problem is that index is at column 4 so couldn't even do the qobject_cast.

Please share your experience in this regard or maybe I am doing something wrong.

Santosh Reddy
3rd March 2015, 07:30
How did you put combo boxes in table view, did you use setIndexWidget()?

uzairsaeed702
3rd March 2015, 08:09
Via combo box delegate class
inherited by "QStyledItemDelegate"

Santosh Reddy
3rd March 2015, 08:37
So how is that you are doing? Painting the combo box from delegate and when clicked creating an editor for the item using delegate?

uzairsaeed702
3rd March 2015, 08:50
No, I am creating combo box in QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & option , const QModelIndex &index ) const method while i can check the "QModelIndex &index" in prder to check the specific column. Everything is working fine but only problem is that i have to make some logic that when i change something in column 4 then respectively columns 5 combo box has to be changed also.

Santosh Reddy
3rd March 2015, 08:56
When the column 4 QComboBox is edited and data is submitted to the model, the model internally (in setData()) has to adjust the data for the column 5 and emit dataChanged() signal for the column 5 model index.

uzairsaeed702
3rd March 2015, 09:18
Well i am setting the model via setData() by void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const function but when i send the data to the model it was currently having the pointer of column 4 not column 5. QcomboBox is working

e.g. see below Model


bool TableModel::setData(const QModelIndex & index, const QVariant &value, int role)
{
int row = index.row();
int col = index.column();
QMap<QString, QVariant> qMapPair = p_tagSimItems->getItemsMap().at(row);

if(index.isValid() && role == Qt::EditRole)
{
switch(col) {
case 0 :
{
qMapPair.insert("a",value.toString());
p_tagSimItems->setItemMap(row,qMapPair);
emit (dataChanged(index , index));
return true;
}
break;
case 1 :
{
qMapPair.insert("b",p_tagSimItems->isValidEnteredType(value.toString()));
p_tagSimItems->setItemMap(row,qMapPair);
emit (dataChanged(index , index));
return true;
}
break;
case 2 :
{
qMapPair.insert("c",value.toString().toUShort(0,10));
p_tagSimItems->setItemMap(row,qMapPair);
emit (dataChanged(index , index));
return true;
}
break;

case 3 :
{ // the problem is here when model receive the signal from delegate in column 4 then i have to change the column 5 also manually which seems not possible because model is on column 4 .
if (value.toString().toUShort(0,10) < 256)
{
qMapPair.insert("d",value.toString().toUShort(0,10));
}else
{
qMapPair.insert("d",0);
}
p_tagSimItems->setItemMap(row,qMapPair);
emit (dataChanged(index , index));
return true;
}
break;
case 4 :
{
if (value.toString().toUShort(0,10) < 256)
{
qMapPair.insert("e",value.toString().toUShort(0,10));
}else
{
qMapPair.insert("e",0);
}
p_tagSimItems->setItemMap(row,qMapPair);
emit (dataChanged(index , index));
return true;
}
break;
default :
break;
}
}
return false;
}


After this, model call the
void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const function in order to set the Index of the combo box.

Santosh Reddy
3rd March 2015, 10:57
// the problem is here when model receive the signal from delegate in column 4 then i have to change the column 5 also manually which seems not possible because model is on column 4 .
When inside setData() for column 4, you could call setData for column 5, you just need to create a new index (by calling QAbstractItemModel::createIndex) for column 5 and then pass it to the setData();

uzairsaeed702
3rd March 2015, 12:20
When inside setData() for column 4, you could call setData for column 5, you just need to create a new index (by calling QAbstractItemModel::createIndex) for column 5 and then pass it to the setData();

Can you give me a example ?

Santosh Reddy
3rd March 2015, 13:08
Here you go, run this code.

I used delegate (because I used standard model) but you could do the similar in the your custom model.



#include <QtWidgets>

class Delegate : public QStyledItemDelegate
{
public:
explicit Delegate(QObject * parent = 0) : QStyledItemDelegate(parent) {}

QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QComboBox * widget = new QComboBox(parent);

widget->addItems(index.data(Qt::UserRole+1).toStringList() );

return widget;
}

void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
{
if(index.column() == 0)
{
QComboBox * widget = dynamic_cast<QComboBox *>(editor);

if(widget)
{
QString country = widget->currentText();

QVariant russia = QStringList() << "Moscow" << "Saint Petersburg" << "Novosibirsk" << "Yekaterinburg";
QVariant canada = QStringList() << "Toronto" << "Parliament Hill" << "Quebec" << "Montreal";
QVariant china = QStringList() << "Shanghai" << "Beijing" << "Chongqing" << "Hong Kong";
QVariant states = QStringList() << "New York City" << "Los Angeles" << "Chicago" << "Houston";

QModelIndex nextColumnIndex = model->index(index.row(), 1);

if(country == "Russia")
{
model->setData(nextColumnIndex, russia, Qt::UserRole+1);
}
else if(country == "Canada")
{
model->setData(nextColumnIndex, canada, Qt::UserRole+1);
}
else if(country == "China")
{
model->setData(nextColumnIndex, china, Qt::UserRole+1);
}
else if(country == "United States")
{
model->setData(nextColumnIndex, states, Qt::UserRole+1);
}

model->setData(index, country);
model->setData(nextColumnIndex, "Select City Again");
}
widget->addItems(index.data(Qt::UserRole+1).toStringList() );
}
else
{
QStyledItemDelegate::setModelData(editor, model, index);
}
}
};

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

QTableView table;
QStandardItemModel model(4, 2);

for (int row = 0; row < 4; row++)
{
QStandardItem *item = new QStandardItem(QString("Select Country").arg(row).arg(0));
item->setData(QStringList() << "Russia" << "Canada" << "China" << "United States",Qt::UserRole+1);
model.setItem(row, 0, item);

item = new QStandardItem(QString("Select City").arg(row).arg(1));
item->setData(QStringList());
model.setItem(row, 1, item);
}

table.setModel(&model);
table.setItemDelegate(new Delegate(&model));
table.show();

return app.exec();
}

uzairsaeed702
4th March 2015, 12:31
Here you go, run this code.

I used delegate (because I used standard model) but you could do the similar in the your custom model.



QModelIndex nextColumnIndex = model->index(index.row(), 1);



Santosh Reddy thanks alot for the code but in custom model its difficult to examine the problem .Your hint for AbstractItemModel::createIndex worked like charm !!! . I have just inserted two line of code in model and few lines of code in my delegate class and work done.



QModelIndex changeRequest = createIndex(row,4);
setData(changeRequest ,Qt::EditRole);


Above was my modification ,

Thanks again Santosh :)

Problem Solved (Y)