PDA

View Full Version : Strange behavior with QTableWidget



franco.amato
10th January 2022, 21:21
Hi Community,
I'm having trouble with a QTableWidget that's part of a dialog.
The table has 6 columns; The cells belonging to the first columns contains only text while the others contains a QComboBox widget (each combobox has 3 text items).
In my code I need to detect which combobox widget was changed so I connected the cellChanged signal to my slot in this way:


connect( tableBiasRadar_, SIGNAL(cellChanged(int, int)), this, SLOT(tableBiasRadar__cellChanged(int, int)));

And the relative slot:


void OtrParametersDialogCtrl::tableBiasRadar__cellChang ed(int row, int col)
{
std::cerr << "[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: " << row << " Col: " << col << std::endl;

// If the column changed is the time offset, change all other modes of the radar
if (static_cast<RadarBiasTableColums_>(col) != radarBiasTableTimeOffsetC_)
{
return;
}

// Get the value set by the user
QComboBox *item = qobject_cast<QComboBox*>(tableBiasRadar_->cellWidget(row, col));
if (item == nullptr)
{
return;
}

// Look for the radar that is being edited by this item
const int radarId = tableBiasRadar_Column2RadarId_[row];

// Change the value of the combo box in all other rows that edit this item
for (size_t i = 0; i < tableBiasRadar_Column2RadarId_.size(); ++i)
{
if (i != static_cast<size_t>(row) && tableBiasRadar_Column2RadarId_[i] == radarId)
{
QComboBox *itemToBeModified = qobject_cast<QComboBox*>(tableBiasRadar_->cellWidget(static_cast<int>(i), col));
if (itemToBeModified != nullptr)
{
itemToBeModified->setCurrentIndex(item->currentIndex());
}
}
}
}


It doesn't matter which combobox I change, I always get the same output:


[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 0 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 1 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 2 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 3 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 4 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 5 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 6 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 7 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 8 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 9 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 10 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 11 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 12 Col: 0
[DEBUG] OtrParametersDialogCtrl::tableBiasRadar__valueChan ged : Row: 13 Col: 0

I would like to have a help in discovering why I always get the column 0 (the column 0 does not contain any combobox, only text) even when I change the combobox of other columns and why I get the output of all the rows also if I change only 1 row.

The look&feel of the dialog looks correct, all the cells belonging to column 1 to 6 contains the combobox, so I really don't understand what's happening here. I am using 5.9.7, unfortunately this is a constraint.
Could it be a bug in this Qt version?

I hope to get help.
Franco

d_stranz
10th January 2022, 22:27
The table widget does not know anything about the cell widgets except that the cell has one. It does not respond to any signals from the cell widgets, and the cellChanged() signal is not being emitted in response to a change in the selection in the combo box. There is no connection between the cell widget and the table unless you make it. The cell widget is basically just a decoration unless you connect to its signals. I don't know what is triggering the cellChanged() signal.

So, you have to keep track of which combobox is in which cell, implement slot(s) to handle and map the combo box selection changes to update the appropriate cells in your table.

You should look at QItemDelegate / QStyledItemDelegate and example code using combo boxes. (https://wiki.qt.io/Combo_Boxes_in_Item_Views)

franco.amato
10th January 2022, 22:30
But when I change the combobox value it should trigger a change in the respective table cell or not?

d_stranz
10th January 2022, 23:06
But when I change the combobox value it should trigger a change in the respective table cell or not?

No, not automatically. The table widget does not know anything about the combo box except that it was told to store a QWidget in that cell. There is no automatic connection between the combo box selectionChanged() signal and some slot in the table widget.

The QItemDelegate / QStyledItemDelegate classes provide a way to automate this - look at the example link I posted.

franco.amato
11th January 2022, 20:58
Thank you for the useful reference.
How can I initialize the tablewidget cells with the first item of the combobox ?

Thank you

d_stranz
12th January 2022, 16:04
How can I initialize the tablewidget cells with the first item of the combobox ?

Are you using the delegate idea? If you are, then in the delegate's createEditor() method, you fill the combobox with the list of choices, right? Take that code and turn it into a method that can be called from outside the class, something like this:



QStringList MyComboDelegate::initialContents( int row, int column ) const
{
QStringList contents;
// fill the list
return contents;
}

QWidget * MyComboDelegate::createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index )
{
QComboBox * editor = new QComboBox( parent );
editor->addItems( initialContents( index.row(), index.column() );

// further initialization
return editor;
}


When you create the delegate to put into the tablewidget, call this method, then pull the first item from the list and use it initialize each of the cells in the column when you create the QTableWidgetItem instances.

Alternatively, you could provide a function to set the combobox contents from outside the delegate:



void MyComboDelegate::setContents( const QStringList & contents )
{
mContents = contents;
}

QWidget * MyComboDelegate::createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index )
{
QComboBox * editor = new QComboBox( parent );
editor->addItems( mContents );

// further initialization
return editor;
}


and use that same list to initialize the table cells.

There are other possibilities as well, depending on whether each combobox starts with the same contents or if each combobox can have a different set of choices, and also whether users can add to the list of choices or they are a fixed set. Qt is very flexible in the way you can use all of these classes together.