使用自定义编辑器调整 QTableView 部分的大小

Resizing QTableView section with custom editor

我有一个带有 QTableView 的应用程序和一个从 QAbstractItemModel 派生的模型:table 的第一列包含一个文本(每行一个标签),而第二列显示一个值,可以使用从自定义项目委托创建的 QComboBox 选择。 table 的内容可能会动态变化(行数、语言...)。

我想调整列的大小,使第二个适合内容,第一个延伸占据剩余 space。

我的第一次尝试是:

tblData->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
tblData->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents);

结果:

The problem is that when the QComboBox is selected it doesn't fit the section and it is clipped:

我已经通过手动增加宽度解决了这个问题:

tblData->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
tblData->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
tblData->resizeColumnToContents(1);
tblData->horizontalHeader()->resizeSection(1, tblData->horizontalHeader()->sectionSize(1) + 40);

现在的问题是,通过使用这样的常量(在本例中为 40),部分的宽度将根据显示的值而不是所有可能的值而变化(如果已经显示了最大的值,则仅显示)最短的)。此外,该常量将取决于所使用的样式,因为它还与 QComboBox.

消耗的 space 有关

我想过使用Qt::SizeHintRole手动计算截面宽度,但完全被忽略了。即使是,我也无法计算文本的实际宽度(使用 QFontMetrics::width),因为我在模型中没有任何字体信息。

我尝试过的另一种方法是在 QItemDelegate::createEditor 方法中设置 QComboBox 的调整大小策略:

QWidget* myItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const {
  auto comboBox = new QComboBox(parent);
  comboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
  // ...
}

但现在组合框被剪裁或缩短了。


如何解决基于完整内容范围而不是仅可见数据设置部分大小的问题?


我正在用迄今为止发现的最佳方法以及我现在在项目中使用的方法自我回答这个问题,但我不相信它(我详细说明了原因答案)所以我想知道正确的方法。谢谢!

到目前为止我发现的最佳选择是重新实现 QItemDelegate::sizeHint:我有来自 QStyleOptionViewItem 的字体信息和要包含在 [=] 中的元素列表14=].

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;
}

结果:


此解决方案的缺点是:

  • 我没有关于 QComboBox 所需的额外 space 的信息,因此它还不是样式独立的(与问题的第二种方法一样)
  • 如果将新的编辑器添加到项目委托中,那么我也必须手动将它们包含在尺寸提示计算中,这不是一个可怕的痛苦,但感觉像是一个糟糕的设计。

PS:在此处使用 QComboBox::sizeHint 不起作用,因为大小提示是使用 QComboBox::sizeAdjustPolicy 计算的,正如问题中突出显示的那样,无法正确调整组合框进入牢房。


更新

我已经根据评论中的指示更新了解决方案并接受了答案。下面是完整的代码供以后参考:

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;
}

委托中的 sizeHint 是正确的方法,但是不要创建编辑器,而是填充 QStyleOptionComboBox 结构并使用 qApp->style()->sizeFromContents(QStyle::CT_ComboBox, &opt, sh, nullptr);,其中 sh是内部字符串的大小。您可以使用 QFontMetrics 来计算,或者只调用基数 class QStyledItemDelegate::sizeHint(...).