4

I was trying to insert a QCombobox only in some specific cells of my QTreeview. As I read, I think that I need to create my delegate (that I've created). But I don't understand how to insert this in my treeview.

I want to realize this:

enter image description here

This is my code:

#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItem>
#include "mainwindow.h"
#include "comboboxdelegate.h"


const int ROWS = 2;
const int COLUMNS = 3;

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    treeView = new QTreeView(this);
    setCentralWidget(treeView);

    standardModel = new QStandardItemModel ;
    standardModel->setColumnCount(2);

    QStandardItem *root = new QStandardItem("ROOT");
    root->setCheckable(true);
    root->setCheckState(Qt::Checked);
    root->setEditable(false);

    standardModel->setItem(0, 0, root);

    QList< QStandardItem * > listOne ;
    QStandardItem  *f1 = new QStandardItem( "Field_1" );
    f1->setCheckable(true);
    f1->setCheckState(Qt::Checked);
    f1->setEditable(false);

    listOne.append(f1) ;
    listOne.append( new QStandardItem( "<Free text>" ) ) ;
    root->appendRow(listOne);


    QList< QStandardItem * > listTwo ;
    QStandardItem  *f2 = new QStandardItem( "Field_2" );
    listTwo.append(f2) ;
    listTwo.append( new QStandardItem( "<HERE COMBOBOX!>" ) ) ;
    root->appendRow(listTwo);

    treeView->setModel(standardModel);
    treeView->expandAll();
}

I managed to create an entire column with QCombobox (using custom delegate). But I don't know how to set only specific cell. Anyone can help me?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Tex'N'Duet
  • 301
  • 2
  • 13

3 Answers3

5

QTreeWidget makes widget items convenient.

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    treeWidget = new QTreeWidget(this);
    setCentralWidget(treeWidget);

    treeWidget->setColumnCount(2);

    auto root = new QTreeWidgetItem({"Root"});
    root->setCheckState(0, Qt::Checked);
    treeWidget->addTopLevelItem(root);

    auto child1 = new QTreeWidgetItem({"Field 1", "<Free Text>"});
    child1->setCheckState(0, Qt::Checked);
    child1->setFlags(child1->flags() | Qt::ItemIsEditable);
    root->addChild(child1);

    auto child2 = new QTreeWidgetItem({"Field 2"});
    child2->setFlags(child2->flags() | Qt::ItemIsEditable);
    root->addChild(child2);

    auto comboBox = new QComboBox();
    comboBox->addItems({"Red", "Blue", "Yellow"});
    treeWidget->setItemWidget(child2, 1, comboBox);

    connect(treeWidget, &QTreeWidget::itemDoubleClicked, treeWidget, &QTreeWidget::editItem);

    treeWidget->expandAll();
}

There are a few differences to note.

  • You'll need QTreeWidget* treeWidget; in your class declaration. And include the QTreeWidget header.

  • By default, TreeWidgetItems aren't checkable (no checkbox), but calling QTreeWidgetItem::setCheckState with Qt::Checked or Qt::Unchecked will make it checkable.

  • Items are not editable by default. Whole rows can be made editable by calling treeWidgetItem->setFlags(treeWidgetItem->flags() | Qt::ItemIsEditable). To filter what rows/columns can be edited, you can define your own itemDoubleClicked slot and use an if-statement (example).

TrebledJ
  • 8,713
  • 7
  • 26
  • 48
2

You need to store combobox items in model item, for example using Qt::UserRole

QStringList options = {"one", "two", "three"};
QStandardItem* item = new QStandardItem(options[0]);
item->setData(QVariant(options),Qt::UserRole);
listTwo.append(item);

Then you need assign delegate to view. You can assign it for the whole table and return default delegate if index.data(Qt::UserRole).isNull().

Delegate* delegate = new Delegate(treeView);
treeView->setItemDelegate(delegate);

It's probably a good idea to set edit triggers to all, so dropdown occurs not only on doubleclick but also on single click

treeView->setEditTriggers(QAbstractItemView::AllEditTriggers);

Delegate must implement createEditor, setEditorData and setModelData

QWidget *Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if (index.data(Qt::UserRole).isNull()) {
        return QStyledItemDelegate::createEditor(parent, option, index);
    }
    return new QComboBox(parent);
}

void Delegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
    if (!comboBox) {
        return QStyledItemDelegate::setEditorData(editor, index);
    }
    QStringList options = index.data(Qt::UserRole).toStringList();
    comboBox->addItems(options);
    QString value = index.data().toString();
    int current = options.indexOf(value);
    if (current > -1) {
        comboBox->setCurrentIndex(current);
    }
    comboBox->showPopup();
}

void Delegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    QComboBox* comboBox = qobject_cast<QComboBox*>(editor);
    if (!comboBox) {
        return QStyledItemDelegate::setModelData(editor, model, index);
    }
    model->setData(index, comboBox->currentText());
}

By default delegate doesn't change how item is displayed and show editor only if edit is triggered: no combobox is shown. But you can override it with custom paintEvent.

void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if (index.data(Qt::UserRole).isNull()) {
        return QStyledItemDelegate::paint(painter, option, index);
    }
    QStyle* style = qApp->style();
    QStyleOptionComboBox opt;
    opt.rect = option.rect;
    opt.currentText = index.data().toString();
    opt.palette = option.palette;
    opt.state = option.state;
    opt.subControls = QStyle::SC_All;
    opt.activeSubControls = QStyle::SC_All;
    opt.editable = false;
    opt.frame = true;
    style->drawComplexControl(QStyle::CC_ComboBox, &opt, painter, 0);
    style->drawControl(QStyle::CE_ComboBoxLabel, &opt, painter, 0);
}

Full source here: combobox-delegate

mugiseyebrows
  • 4,138
  • 1
  • 14
  • 15
1

You can use setIndexWidget on any QAbstractItemView

auto comboBox = new QComboBox();
comboBox->addItems({"Red", "Blue", "Yellow"});
view->setItemWidget(idx, comboBox);

You dont need to use QTreeWidget, all it does is call indexFromItem

treeWidget->setItemWidget( treeWidget->indexFromItem( item, column ), comboBox );