PDA

View Full Version : How to create a Combo box in a Tablview Model



uzairsaeed702
8th January 2015, 15:13
Hello,

I am new in QT and trying hard to learn it. I have created a TableView model with 5 columns , i need to create the combo box in 4 and 5 columns with some respective data. I am using the Model View approach which is confusing me . I have read many posts about combobox delegates , can any one reply me which approach is better and easy to insert combobox in a tableview .

This (http://www.qtforum.org/article/14626/how-can-i-display-combobox-in-a-qtableview-qt4.html) is something close but they are using combo delegates which make the things complex.

Please help me out

Thanks

ChrisW67
8th January 2015, 20:23
The way a view's cells are drawn is provided by a default QStyledItemDelegate. If you want to change the way the data appears when it is not being editted, or if you want to provide a combo box as the editor, then you need to provide the view with customised (subclassed) QStyledItemDelegate with the paint() and/or createEditor() functions reimplemented. The mechanism is described in the docs.
http://doc.qt.io/qt-5/qstyleditemdelegate.html#details
http://doc.qt.io/qt-5/qtwidgets-itemviews-stardelegate-example.html

neuronet
8th January 2015, 22:11
And also check out the spinbox delegate example, it should be very similar to combobox:
http://qt-project.org/doc/qt-4.8/itemviews-spinboxdelegate.html

uzairsaeed702
9th January 2015, 14:38
Hey ChrisW67,

Exactly you get my point ,star delegate is a good example but i guess i don't have to go through paint() and CreateEditor() because i just have to put the combobox in my 4th and 5th column. Please have a look at the following picture
10880

I had followed the basic tecnique of TableView by QAbstractTableModel which display all my data perfectly , but with combobox its confusing me . I also thought to use the Combobox delegate but still confused in that.

thanks

ChrisW67
9th January 2015, 19:56
QStyledItemDelegate::createEditor() is what puts a functional editor, in your case a QComboBox, in place of the table cell when you go into edit mode on the cell.
QStyledItemDelegate::paint() is what draws the content of the cell when it is not in edit mode. In your case you might want to fake the appearance of a QComboBox or just accept the default behaviour of displaying simple text. I suggest you leave the non-editing appearance as the default at least until you get to grips with creating the editor widget.

The QTableWidget provides an alternate mechanism to put a persistent editor widget into a cell. You can do that for a relativly small number of cells before performance starts to slow.

uzairsaeed702
12th January 2015, 14:24
Hey ChrisW67,

I have understood your point but the problem is that i have the TableViewModel on "QAbstractTableModel" and how could i configure my TableView model that on column 5 and 6 i need combo box with some data . This "http://qt-project.org/doc/qt-4.8/sql-relationaltablemodel.html" is the perfect example which i want to achieve but it is based on QSqlQuery class which i am not using . I am getting all the data from xml and storing in QMap's later which is to be displayed in these columns .

Do you suggest combo box delegate ? http://programmingexamples.net/wiki/Qt/Delegates/ComboBoxDelegate Or what do your suggest in this situation ?

Thanks

anda_skoa
12th January 2015, 15:06
This is not a model side thing, delegates are on the view side of things.

It works with any model.

Cheers,
_

ChrisW67
12th January 2015, 20:09
The example you link to is exactly the sort of thing you need to be be doing.

uzairsaeed702
13th January 2015, 08:23
This is not a model side thing, delegates are on the view side of things.

It works with any model.

Cheers,
_

I understand that but the problem is if i use the Spinbox delegate example with combo box delegate how i can say that only in 4 and 5 column i need combo box . This http://qt-project.org/doc/qt-4.8/sql-relationaltablemodel.html example is perfect for me but it use internal library of QSqlTableModel which i can not use because i am not using Sql.

Thanks

anda_skoa
13th January 2015, 08:39
I understand that but the problem is if i use the Spinbox delegate example with combo box delegate how i can say that only in 4 and 5 column i need combo box .


In such cases I can highly recommend to use a facility referred to as the API documentation.
A set of documents describing an API, in this case Qt's model/view classes.

When using such documentation in Qt's documentation browser Qt assistant or on the web, the respective program's functionality even allows to search within these documents!

For example a search on "Delegate" in the documentation of QAbstractItemView would find http://doc.qt.io/qt-5/qabstractitemview.html#setItemDelegateForColumn

Repeatedly ignoring any hint on the forum and reiterating a non existant problem will eventually also lead to the solution, but take a lot longer, frustrate everyone involved and not bode well for the future of your programming endeavors.

Cheers,
_

jefftee
13th January 2015, 18:32
I understand that but the problem is if i use the Spinbox delegate example with combo box delegate how i can say that only in 4 and 5 column i need combo box.
I am definitely not an expert, but I have successfully used a ProgressBar delegate as follows. In my MainWindow, I set the item delegate for the view as follows:


ui->tableView->setItemDelegate(new ProgressBarDelegate());

ProgressBarDelegate is a class derived from QStyledItemDelegate and here's the paint method:


void ProgressBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (!index.isValid())
return;

int col = index.column();

if (col == 5)
{
int progress = index.data().toInt();
QStyleOptionProgressBar progressBarOption;
progressBarOption.rect = QRect(option.rect.x(), option.rect.y() + 5 , option.rect.width(), option.rect.height() / 1.5);
if (progress == 100)
{
QApplication::style()->drawItemText(painter, option.rect, Qt::AlignCenter|Qt::AlignVCenter, option.palette, true, QString::number(progress) + "%");
}
else
{
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.progress = progress;
progressBarOption.text = QString::number(progress) + "%";
progressBarOption.textVisible = true;
progressBarOption.textAlignment = Qt::AlignCenter|Qt::AlignVCenter;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
}
}
else
{
QStyledItemDelegate::paint(painter, option, index);
}

return;
}

So you'll see that the delegate is set at the view level and then in the paint method, I only override how the progress bar I want in column 5 is drawn and I call the base class paint method for all other columns.

If I am following your thread correctly, I believe you should be able to take a similar approach.

Good luck.

ChrisW67
13th January 2015, 19:41
If you only want customised behaviour for a couple of columns (or rows) then you only apply a custom delegate to those columns (or rows). This is exactly what your own linked combo box delegate does.

Jthomps approach is also a way to achieve the goal. The paint() and createEditor() functions look at which column they are being asked to act on and modify their behaviour accordingly. This method ties the delegate fairly closely to this particular view, i.e. You cannot reuse this delegate class on another view with 8 columns to get customised behaviour on columns 3 and 6.

uzairsaeed702
14th January 2015, 09:46
The example you link to is exactly the sort of thing you need to be be doing.

Hey ChrisW67,

Yes true but in that example everything is handled by QSqlTableModel class which automatically detects the queries and place the combo box where it is necessary , which is invalid in my case. My model

TableModel *newTable = new TableModel(XmlMap,this)

ui->tableView->setModel(&newTable)

ui->tableView->setItemDelegate(&comboBoxDelegate)
creating all the necessary data structure depending on the length of Xmlmap and if i put the delegate then it'll create the combo box with in all the table cell which is what i don't want.

I am thinking to switch on QTableWidget but i have much data to view and on the other hand i am thinking that TableWidget will be slow.

I have tried a simple example through QTableWidget "Quick try"



QTableWidgetItem *newItem = new QTableWidgetItem("a");
ui->tableWidget->setItem(count, columns,newItem);
columns++;
QTableWidgetItem *newItem1 = new QTableWidgetItem("b");
ui->tableWidget->setItem(count, columns, newItem1);
columns++;
QTableWidgetItem *newItem2 = new QTableWidgetItem("c");
ui->tableWidget->setItem(count, columns, newItem2);
QComboBox* comboBox = new QComboBox();
comboBox->addItem("modeMap");

QComboBox* comboBox2 = new QComboBox();
comboBox2->addItem("valueMap");
ui->tableWidget->setCellWidget(count,3,comboBox);
ui->tableWidget->setCellWidget(count,4,comboBox2);



And in last i got a good result and it is too easy to handle, but the only problem that it is taking some time to load because my xml contain 1500 rows.

10883

anda_skoa
14th January 2015, 10:32
ui->tableView->setItemDelegate(&comboBoxDelegate)


Let me get this straight:
- you want the delegate in certain columns only
- you ignore the existance of setItemDelegateForColumn, which does exactly that
- you keep pretending that your problem is not solved

Only reason to do that is to troll, right?

Cheers,
_

uzairsaeed702
14th January 2015, 10:41
I am definitely not an expert, but I have successfully used a ProgressBar delegate as follows. In my MainWindow, I set the item delegate for the view as follows:


ui->tableView->setItemDelegate(new ProgressBarDelegate());

ProgressBarDelegate is a class derived from QStyledItemDelegate and here's the paint method:


void ProgressBarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (!index.isValid())
return;

int col = index.column();

if (col == 5)
{
int progress = index.data().toInt();
QStyleOptionProgressBar progressBarOption;
progressBarOption.rect = QRect(option.rect.x(), option.rect.y() + 5 , option.rect.width(), option.rect.height() / 1.5);
if (progress == 100)
{
QApplication::style()->drawItemText(painter, option.rect, Qt::AlignCenter|Qt::AlignVCenter, option.palette, true, QString::number(progress) + "%");
}
else
{
progressBarOption.minimum = 0;
progressBarOption.maximum = 100;
progressBarOption.progress = progress;
progressBarOption.text = QString::number(progress) + "%";
progressBarOption.textVisible = true;
progressBarOption.textAlignment = Qt::AlignCenter|Qt::AlignVCenter;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
}
}
else
{
QStyledItemDelegate::paint(painter, option, index);
}

return;
}

So you'll see that the delegate is set at the view level and then in the paint method, I only override how the progress bar I want in column 5 is drawn and I call the base class paint method for all other columns.

If I am following your thread correctly, I believe you should be able to take a similar approach.

Good luck.

Hey jthomps,

That looks helpful to me and it gives the clear idea to play for with custom columns . I will try to do it through delegates hopefully it will work.

Thanks

Regards
Uzair saeed


If you only want customised behaviour for a couple of columns (or rows) then you only apply a custom delegate to those columns (or rows). This is exactly what your own linked combo box delegate does.

Jthomps approach is also a way to achieve the goal. The paint() and createEditor() functions look at which column they are being asked to act on and modify their behaviour accordingly. This method ties the delegate fairly closely to this particular view, i.e. You cannot reuse this delegate class on another view with 8 columns to get customised behaviour on columns 3 and 6.

Yes i am getting the idea now but in actually Model View concept is bit confusing i guess i have to study that way more time to get the clear idea. But i will try today.

Thanks ChrisW67

Added after 5 minutes:


Let me get this straight:
- you want the delegate in certain columns only
- you ignore the existance of setItemDelegateForColumn, which does exactly that
- you keep pretending that your problem is not solved

Only reason to do that is to troll, right?

Cheers,
_

Hey,

It was my previous try but i am getting the combo box in each columns . Problem is only that Model View concept is bit confusing and if i go with QTableWidget its easy but takes time . Actually later from that combo box i have to attach the event which can invoke the dialog so for that complexity i am planing which method would be easy for me. So i am asking question and i am beginner that is why trying approaches .

Thanks

ChrisW67
14th January 2015, 11:12
Yes true but in that example everything is handled by QSqlTableModel class which automatically detects the queries and place the combo box where it is necessary , which is invalid in my case. My model

The example you linked to (http://programmingexamples.net/wiki/Qt/Delegates/ComboBoxDelegate) has nothing to do with an SQL model. The model data can come from anywhere as long as it is presented using the QAbstractItemModel interface. The model provides data only.

The rendering of data in a view has nothing to do with the model. The view passes the painting and editor creation for each cell to a delegate, either the default one or one you provide, and the delegate decides what to display for the data the model is providing. The model may provide data indicating a colour, font, icon etc. but the view delegate can choose how to use (or ignore) that information when displaying the data. The type of editor provided for a cell is entirely decided by the delegate.

anda_skoa
14th January 2015, 11:52
It was my previous try but i am getting the combo box in each columns

I highly doubt it.
Unless you are still calling setItemDelegate() instead of the correct method.

Cheers,
_

uzairsaeed702
14th January 2015, 12:14
The example you linked to (http://programmingexamples.net/wiki/Qt/Delegates/ComboBoxDelegate) has nothing to do with an SQL model. The model data can come from anywhere as long as it is presented using the QAbstractItemModel interface. The model provides data only.

The rendering of data in a view has nothing to do with the model. The view passes the painting and editor creation for each cell to a delegate, either the default one or one you provide, and the delegate decides what to display for the data the model is providing. The model may provide data indicating a colour, font, icon etc. but the view delegate can choose how to use (or ignore) that information when displaying the data. The type of editor provided for a cell is entirely decided by the delegate.

Hey ChrisW67,

Now i understand everything , i am sorry this model view concept is confusing . I will try to alter the delegate class with Paint and create editor and will try to play with it. Hopefully i will achieve my target. I will stick to model view concept instead of QtableWidget .

Thanks for the motivation.

uzairsaeed702
20th January 2015, 09:59
Finally problem solved after reading again n again about MVC and thanks "All" for the support !!

I have configured the model with Combo box delegate with openPersistentEditor in combo box enabled columns (4,5) .

In ComboBoxDelegate.cpp


]QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & option , const QModelIndex &index ) const
{


if(index.column() == 3 || index.column() == 4)
{

// Create the combobox and populate it
QComboBox *cb = new QComboBox(parent);
cb->addItem(QString("Please Select"));
cb->addItem(QString("1"));
cb->addItem(QString("2"));
cb->addItem(QString("3"));
connect(cb, SIGNAL(currentIndexChanged(QString)), this, SLOT(print(QString)));
return cb;
} else
return QStyledItemDelegate::createEditor(parent, option, index);
}]

And in my TableModel.cpp Class


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


if(index.isValid() && role == Qt::EditRole)
{
switch(col) {
case 3 :
{
qDebug() << "Value From Table Model = " <<value.toString() << "Row" <<row << "xCol"<< col ;
return true;
}
break;
case 4 :
{
qDebug() << "Value From Table Model = " <<value.toString() << "Row" <<row << "xCol"<< col ;
return true;
}
break;
default :
break;}}return false;}

]

and things are working perfectly , now i am working on a event that when i will select any index value in combo box it will populate a dialog which configure the data on my Map. I will ask later for further question .