How can I resize QTableView row according to specific cell value? - c++

I want to resize the row height according to a specific column value. For example, the table I want to change is here:
At row 10, row height is resized by the third column value. But at row 11 it is resized by the second column value. I want to resize row height by only the third column value. Is there a way to do this?
Thank you.
This is my code for this QTableView
ui->tableView->setModel(modal);
ui->tableView->hideColumn(0);
ui->tableView->resizeColumnsToContents();
ui->tableView->resizeRowsToContents();
ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
ui->tableView->horizontalHeader()->setStyleSheet("QHeaderView { font-size: 18pt; color:#002B5B; font-weight: bold; }");

I haven't actually tried it myself, but from looking at the Qt source code (in particular the source code for QTableView::rowHeight(int) const and QHeaderView::sectionSizeFromContents(int) const), it looks like you could do something like:
const int addressColumnLogicalIndex = 1; // i.e. 2nd column in the table
const int fixedRowHeight = 18; // or whatever fixed height you want the address-column to use
model()->setHeaderData(addressColumnLogicalIndex, Qt::Vertical, QSize(0, fixedRowHeight), Qt::SizeHintRole);

Related

Resizing QTableView section with custom editor

I have an application with a QTableView and a model derived from QAbstractItemModel: the first column of the table contains a text (a label for each row), while the second column shows a value that can be selected using a QComboBox created from a custom item delegate. The content of the table may change dynamically (number of rows, language...).
I'd like to resize columns so the second one fits the content and the first one stretches occupying the remaining space.
My first attempt was:
tblData->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
tblData->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
Result:
The problem is that when the QComboBox is selected it doesn't fit the section and it is clipped:
I've managed to solve this issue by manually increasing the width:
tblData->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
tblData->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
tblData->resizeColumnToContents(1);
tblData->horizontalHeader()->resizeSection(1, tblData->horizontalHeader()->sectionSize(1) + 40);
Now the issue here is that by using such constant (40 in this case) the width of the section will vary depending on values displayed rather than in all the possible values (if the largest ones are already displayed vs if only the shortest). Also, that constant will be dependant to the style used, since it is also related to the space consumed by the QComboBox.
I've thought about using the Qt::SizeHintRole to manually compute the section width, but it is completely ignored. Even if it was, I cannot compute the actual width of the text (using QFontMetrics::width) because I don't have any font information in the model.
Another approach I've tried is to set the adjust size policy of the QComboBox in the QItemDelegate::createEditor method:
QWidget* myItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const {
auto comboBox = new QComboBox(parent);
comboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
// ...
}
But now the combo boxes are either clipped or shortened.
How can I solve set the section size based on the complete range of content instead of just visible data?
I'm self-answering the question with the best approach I've found so far, and the one I'm using in the project right now, but I'm not convinced with it (I detail reasons on the answer) so I'd like to know the correct way to do it. Thanks!
The sizeHint in the delegate is the right way to go but, instead of creating an editor, fill a QStyleOptionComboBox struct and use qApp->style()->sizeFromContents(QStyle::CT_ComboBox, &opt, sh, nullptr);, where sh is the size of the internal string. You can use QFontMetrics to calculate that or just call the base class QStyledItemDelegate::sizeHint(...).
The best option I've found so far is to re-implement the QItemDelegate::sizeHint: I have the font information from the QStyleOptionViewItem and the list of elements to be included in the QComboBox.
QSize myItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto hint = QItemDelegate::sizeHint(option, index);
QFontMetrics fm(option.font);
std::unique_ptr<QWidget> editor(createEditor(nullptr, option, index));
auto comboBox = qobject_cast<QComboBox*>(editor.get());
if (comboBox != nullptr) {
int width = 0;
for (int ii = 0; ii < comboBox->count(); ++ii) {
width = std::max(width, fm.width(comboBox->itemText(ii)) + 20);
}
hint.setWidth(std::max(hint.width(), width));
}
return hint;
}
Results:
Drawbacks of this solution are:
I don't have information regarding the additional space required by the QComboBox so it is not style-independant yet (as with the second approach on the question)
If new editors are added to the item delegate then I'd have to include them manually in the size hint computation too, which is not a terrible pain but feels like a bad design.
PS: using the QComboBox::sizeHint here doesn't work since size hint is computed using the QComboBox::sizeAdjustPolicy which, as highlighted in the question, doesn't adjust combo boxes correctly into the cell.
UPDATE
I've updated the solution following the indications from comments and accepted answer. Here is the complete code for future reference:
QStringList myItemDelegate::getPossibleValuesForIndex(const QModelIndex& index) const
{
// returns list of all possible values for given index (the content of the combo box)
}
QSize myItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
auto hint = QItemDelegate::sizeHint(option, index);
QFontMetrics fm(option.font);
QStyleOptionComboBox comboOption;
comboOption.rect = option.rect;
comboOption.state = option.state | QStyle::State_Enabled;
Q_FOREACH (const auto& value, getPossibleValuesForIndex(index)) {
hint = hint.expandedTo(qApp->style()->sizeFromContents(QStyle::CT_ComboBox,
&comboOption, QSize(fm.width(value), hint.height())));
}
return hint;
}

QListWidget - Removing Padding & Consistent Column Width

I created a QListWidget using setViewMode(QListView::ListMode) and setFlow(QListView::TopToBottom) to get this:
The large space between the icon and the text looks odd so I tried to remove it with:
list->setStyleSheet("QListView::icon { padding-right: 0px; }");
and also:
list->setStyleSheet("QListView::icon { padding: 0px; }");
but neither removes the space. Is there any way to remove the space between the icon and the text?
Also, you'll see from the above screenshot that QListWidget uses variable column widths, but I wanted all the columns to have the same width. I tried this solution:
int width = 0;
int numItems = list->count();
QFontMetrics metrics(list->font());
for (int i = 0 ; i < numItems ; ++i)
{
if (metrics.boundingRect(list->item(i)->text()).width() > width)
width = metrics.boundingRect(list->item(i)->text()).width();
}
list->setGridSize(QSize(width+30, 16));
However, it doesn't seem to work consistently and I had to use +30 to account for the icon area, which isn't a very good solution, particularly if the size of the icon changes.
Is there a better way to get all the columns the same width?

Qt4 QTableWidget make Colums resized to contents, interactive and aligned to tableborder

This code
horizontalHeader()->setResizeMode(QHeaderView::Stretch);
stretches the cloumns of a qtablewidget. I want them to be stretched, what means be aligned to the border of the qtablewidget, no matter how big it is.
I also want them not to be smaller than their contents and to be resizable by the user.
This means, I would have to use
horizontalHeader()->setResizeMode(QHeaderView::Stretch);
horizontalHeader()->setResizeMode(QHeaderView::Interactive);
horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
at once, which is not possible.
I know I can give every column another view, like
horizontalHeader()->setResizeMode(0, QHeaderView::Interactive);
horizontalHeader()->setResizeMode(1, QHeaderView::ResizeToContents);
but this ist not what I want. I want the colums to be
not smaller than their contents
resizable by the user
aligned to the border of the qtablewidget
Any ideas?
I think that you should reimplement sizeHintForColumn. The below code will give you a start.
int TableWidget::sizeHintForColumn(int column) const // to get resize on all rows in the column, i.e. not only visible rows.
{
if(d_resizeColumnsOnVisibleRowsOnly)
return QTableView::sizeHintForColumn(column);
if(!model())
return -1;
QStyleOptionViewItem option(viewOptions());
int hint(0);
QModelIndex index;
QWidget* w(0);
for(int row(0);row<rowCount();++row)
{
index=model()->index(row,column);
w=cellWidget(row,column);
int hint_for_row(qMax(itemDelegate(index)->sizeHint(option,index).width(),(w?w->sizeHint().width():0)));
hint=qMax(hint,hint_for_row);
}
return showGrid()?hint+1:hint;
}

QTableView Long Header String Not Fits

In Qt 4.6.2 although I make resizeMode Stretch and then resize contents to Columns and to Rows, a header string that is longer than the content of every item in that column does not fit.
Any idea?
Thanks.
After you resized your table by the contents you can check the single colums if they are too small for their header. When a column is too small resize the colum. The size of the header text you can get with the QFontMetric class:
QTableView tv;
QFontMetrics fontmetric(tv.font());
//...
//for every colum:
int minWidth = fontmetric.width(headertextofcolum);
if (tv.columnWidth(colNr) < minWidth){
tv.setColumnWidth(colNr,minWidth);
}

Row Background Color GtkTreeView Widget

I'm attempting to color disabled rows in a gtk tree view widget a light gray color. From what I've read, I'm supposed to set the background-gdk property of the corresponding cellrenderer and bind it to a model column. This sort of works.
Gtk::CellRendererText* textRenderer = manage(new Gtk::CellRendererText());
textRenderer->property_editable() = false;
Gtk::TreeViewColumn *col = manage(new Gtk::TreeViewColumn("Column1", *textRenderer));
col->add_attribute(*textRenderer, "background-gdk", m_treeview_columns.m_back_color);
my_treeview.append_column(*col);
Gtk::TreeModel::Row row;
for (int i = 0; i < NUMBER_OF_ROWS; iLane++){
row = *(treeview_liststore->append());
row[m_workListColumns.m_back_color] = Gdk::Color("#CCCCCC");
}
In the end though, I get only the cells colored properly. BUT I also get an ugly white-space in between the cells. Does anyone know of a way to fix this or a better way to achieve the effect I'm after?
Could you set the background of the row to match the cell background or set the bakground of the tree view all together ? Or maybe the cell with cell-background-gdk ?