2
votes

I have C++ class inherited from QAbstractTableModel with next functions overriden:

virtual QHash<int, QByteArray> roleNames() const noexcept override;
virtual Qt::ItemFlags flags(const QModelIndex& index) const noexcept override;

virtual int rowCount(const QModelIndex& parent = QModelIndex()) const noexcept override;
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const noexcept override;
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const noexcept override;

virtual bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) noexcept override;
virtual bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) noexcept override;
virtual bool setData(const QModelIndex& index, const QVariant& data, int role = Qt::EditRole) noexcept override;

model has 3 columns, first is readonly, last is editable, so this is flags() method implementation:

Qt::ItemFlags ObjectInitialStateModel::flags(const QModelIndex& index) const noexcept
{
    if (index.column() == 0)
    {
        return Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;
    }
    else
    {
        return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemNeverHasChildren;
    }
}

In QML part model is displayed fine, but i have no idea how i can edit model data for 2 and 3 columns in TableView. I've tried to write column delegate:

Item {
    id: item
    state: "labelMode"

    Text {
        id: textLabel
        text: styleData.value
        anchors.fill: parent
        renderType: Text.NativeRendering
    }

    TextField {
        id: textField
        text: styleData.value
        anchors.fill: parent

        Keys.onEnterPressed: commit()
        Keys.onReturnPressed: commit()
        Keys.onEscapePressed: rollback()

        function commit() {
            item.state = "labelMode"
        }

        function rollback() {
            item.state = "labelMode"
        }
    }

    MouseArea {
        id: mouseArea
        anchors.fill: parent
        onDoubleClicked: item.state = "editMode"
    }

    states: [
        State {
            name: "labelMode"
            PropertyChanges {
                target: textLabel
                visible: true
            }
            PropertyChanges {
                target: mouseArea
                visible: true
            }
            PropertyChanges {
                target: textField
                visible: false
            }
        },

        State {
            name: "editMode"
            PropertyChanges {
                target: textLabel
                visible: false
            }
            PropertyChanges {
                target: mouseArea
                visible: false
            }
            PropertyChanges {
                target: textField
                visible: true
                focus: true
            }
        }
    ]
}

but i don't know how to set new data to the model in commit() function correctly. Or may be there are another right way to implement table in QML with editable columns and C++ model?

2

2 Answers

3
votes

I've found one solution:

  1. add property to the delegate:

property var cppModel

  1. set this property in column definition:

TableViewColumn {
    role: "u"
    title: qsTr("u(t)")
    width: initialStateTableView.width / 3
    delegate: EditableDelegate {
        cppModel: DataSetService.currentDataSet ? DataSetService.currentDataSet.initialStateModel : null
    }
}

  1. implement new method in C++ model:

Q_INVOKABLE bool setData(int row, int column, const QVariant& data) noexcept;

which calls default setData method

  1. and call it from commit() function in delegate:

function commit() {
    cppModel.setData(styleData.row, styleData.column, text)
    item.state = "labelMode"
}

But i think this is big ugly hack and if anybody knows more elegant solution, please share it...

1
votes

Besides the roleNames() and data(), the editable models must reimplement the setData() function to save changes to existing data. The following version of the method checks if the given model index is valid and the role is equal to Qt::EditRole, before executing the actual update. Depending on the model you can/must also call the parent class version of the function:

bool EditableModel::setData(const QModelIndex &item, const QVariant &value, int role)
  {
      if (item.isValid() && role == Qt::EditRole) {
          // update logic
          emit dataChanged(item, item);
          return true;
      }
      return false;

  }

It should be noted that unlike the C++ item views, such as QListView or QTableView, the setData() method must be explicitly invoked from QML whenever appropriate.