QTreeView Edit UserRole Instead of DisplayRole Upon Double Click

1.5k views Asked by At

In my project, I have a QTreeView displaying items from a QStandardItemModel. Each item has data stored in several UserRoles.

QStandardItem* item = new QStandardItem();
item->setIcon(iconByte);
item->setData(3, Qt::UserRole+1);
item->setData(name, Qt::UserRole+2);
item->setData(data, Qt::UserRole+3);
... and so on

When the user double clicks on an item, a dialog with two line edits displays allowing the user to edit parts of the UserRole data. When editing ceases, the edits run through some logic and a display name is generated based on the new UserRole data.

However, this gets very tedious very quickly. With dialogs constantly popping up and whatnot, it's a slow and ugly solution.

I now would like to remove the dialog completely and show the line edit widgets WITHIN the item itself. By default, double clicking an item to edit it only shows one line edit widget to change the DISPLAY role. However I want two line edits to change the two USER roles. And then the normal logic continues.

How would I go about modifying the edit item portion of a QTreeView?

Thanks for your time!

1

There are 1 answers

3
Dmitry On BEST ANSWER

I would use a custom subclass of QStyledItemDelegate to solve this. Somewhere near your QTreeView you could have a QComboBox switching between the user roles; your custom delegate would somehow be informed which user role is currently selected and would intercept the method updating the data in the model to set the proper role.

An example implementation (not tested, may contain typos and errors):

class RoleSwitchingDelegate: public QStyledItemDelegate
{
public:
    explicit RoleSwitchingDelegate(QComboBox * roleSwitcher, QObject * parent = 0);

    virtual void setEditorData(QWidget * editor, const QModelIndex & index) const Q_DECL_OVERRIDE;
    virtual void setModelData(QWidget * editor, QAbstractItemModel * model,
               const QModelIndex & index) const Q_DECL_OVERRIDE;
private:
    QComboBox * m_roleSwitcher;
};

RoleSwitchingDelegate::RoleSwitchingDelegate(QComboBox * roleSwitcher, QObject * parent) :
    QItemDelegate(parent),
    m_roleSwitcher(roleSwitcher)
{}

void RoleSwitchingDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const
{
    // Assuming the model stores strings for both roles so that the editor is QLineEdit
    QLineEdit * lineEdit = qobject_cast<QLineEdit*>(editor);
    if (!lineEdit) {
        // Whoops, looks like the assumption is wrong, fallback to the default implementation
        QStyledItemDelegate::setEditorData(editor, index);
        return;
    }

    int role = m_roleSwitcher->currentIndex();
    QString data = index.model()->data(index, role).toString();
    lineEdit->setText(data);
}

void RoleSwitchingDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const
{
    // Again, assuming the model stores strings for both roles so that the editor is QLineEdit
    QLineEdit * lineEdit = qobject_cast<QLineEdit*>(editor);
    if (!lineEdit) {
        // Whoops, looks like the assumption is wrong, fallback to the default implementation
        QStyledItemDelegate::setModelData(editor, model, index);
        return;
    }

    int role = m_roleSwitcher->currentIndex();
    QString data = lineEdit->text();
    model->setData(index, data, role);
}

Once you have the delegate, you just need to set it to the view:

view->setItemDelegate(new RoleSwitchingDelegate(roleSwitchingComboBox, view));