Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to update two items in an index with use of QStyledItemDelegate and QAbstractItemModel ?



  • I customized a QStyledItemDelegate and QAbstractItemModel for a QTreeView. I have a qwidget that contain a qcolor box and a qlineedit for a given row (index), I'm trying to save color and lineedit text to variables. However I can not update the text into variable... (see code below)

    I think the problem is that when the text item is edited, the first "model->setData" for color variable in QStyledItemDelegate "setModelData" function is always executed first, which triggers the "data" function, which returns the original text and QStyledItemDelegate "setEditorData" will overwrite modified text.

    I'm thinking I need to tell what is changed in QStyledItemDelegate "setModelData" function so the right setdata can be sent? How can I do that? Or what is the right way to do this?

    in QStyledItemDelegate .cpp:

    ...

    void MyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
    {
        MyWidget *widget = static_cast<MyWidget *>(editor);
        widget->blockSignals(true);
        widget->setMyText(index.data(Qt::EditRole).toString());
        widget->setMyColor(index.data(Qt::UserRole).value<QColor>());
        widget->blockSignals(false);
    }
    
    void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
              const QModelIndex &index) const
    {
    model->setData(index, QVariant(static_cast<MyWidget*>(editor)->getMyColor()), Qt::UserRole);
    model->setData(index, QVariant(static_cast<MyWidget*>(editor)->getMyText()), Qt::EditRole);
    }
    

    in QAbstractItemModel .cpp:

    QVariant MyModel::data(const QModelIndex &index, int role) const
    {
    if (role == Qt::EditRole){
    return myTextVariable;
    }
    
    if (role == Qt::UserRole){
    return myColorVariable;
    }
    
    return QVariant();
    }
    
    bool myModel::setData(const QModelIndex& index, const QVariant& value, int role)
    {
    if (role == Qt::UserRole){
        myColorVariable=value.value<QColor>();
    }else if (role == Qt::EditRole){
        myTextVariable=value.toString();
    }
    }
    Q_EMIT dataChanged(index, index);
    return true;
    }

  • Qt Champions 2019

    @Pauly said in How to update two items in an index with use of QStyledItemDelegate and QAbstractItemModel ?:

    setEditorData

    Here you're already using blockSignals() - you can try this for model too. Or remember in a member that you're currently setting the data.



  • Do you mean to block signal for setdata call in MyDelegate::setModelData? Won't that block updating one variable or the other?

    I think I would still need to know what got changed in MyDelegate to set such a member? How can I tell? Thanks.



  • Why do you need to block the signals? are you connecting the editor to the delegate? can you show MyDelegate::createEditor?

    Also, your myModel::setData and MyModel::data do not use the index supplied at all, is it a 1 row, 1 column model?



  • I do not recall why I block the signal... maybe I just copied this from somewhere...

    I did not think the createEditor matters to the question so I skip that code. But here is it:

    QWidget *MyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
            MyWidget *editor = new MyWidget(index, parent);
            editor->setEditorText("text");
            editor->setColor(QColor(Qt::black));
            return editor;
        }
    
    return new QWidget(parent);
    }
    

    In reality it is more than one row... I skipped the index here and let's just consider it as one row, it doesn't change the situation, does it? Thank you.


  • Lifetime Qt Champion

    Hi,

    Since you want to modify two elements in one shot, why not pass a QPair to the user role with the two values and let the model put that in the correct places ?

    As @VRonin suggests below: setItemData is the right tool.

    [edit: Fixed suggestions SGaist]



  • Great idea! Along with the same thought, I use QList (just in case if more than two variables to be updated) and it works. This is the new code. Thanks!

    in QStyledItemDelegate .cpp:

    void MyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
    {
    MyWidget *widget = static_cast<MyWidget *>(editor);
    widget->blockSignals(true);
    widget->setColor(index.data(Qt::EditRole).toList().at(0).value<QColor>());
    widget->setText(index.data(Qt::EditRole).toList().at(1).toString());
    widget->blockSignals(false);
    }
    
    void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
          const QModelIndex &index) const
    {
    listData<<QVariant(static_cast<MyWidget*>(editor)->getColor())<<QVariant(static_cast<MyWidget*>(editor)->getText());
    		
    model->setData(index, QVariant(listData), Qt::EditRole);
    }
    

    in QAbstractItemModel .cpp:

    QVariant MyModel::data(const QModelIndex &index, int role) const
    {
    QList<QVariant> listData;
    listData<<QVariant(myColor)<<QVariant(myText);
    return listData;
    }
    
    bool myModel::setData(const QModelIndex& index, const QVariant& value, int role)
    {
    myColor=value.toList().at(0).value<QColor>();
    myText=value.toList().at(1).toString();
    }
    Q_EMIT dataChanged(index, index);
    return true;
    }


  • @SGaist said in How to update two items in an index with use of QStyledItemDelegate and QAbstractItemModel ?:

    Since you want to modify two elements in one shot, why not pass a QPair

    Are you suggesting a reinvention of https://doc.qt.io/Qt-5/qabstractitemmodel.html#setItemData ?


  • Lifetime Qt Champion

    I knew I was forgetting something !


Log in to reply