QAbstractListModel中行的增删改实现及QML调用疑问
Hey there! Let's work through your questions step by step, using your ContactBook project context as a reference:
insertRow()并构造Contact实例 First, remember that the standard insertRow() from QAbstractListModel is meant to insert an empty row by default. To align with the documentation requirement while working with your Contact class, here's how to approach it:
Step 1: Maintain an internal data store
Assuming your model has a private QList<Contact> m_contacts; to hold all contacts, start by overriding the base insertRow() method:
bool ContactBookModel::insertRow(int row, const QModelIndex &parent) { // Validate the row index if (row < 0 || row > m_contacts.size()) return false; // Notify views about the upcoming insertion beginInsertRows(parent, row, row); // Insert a default-constructed Contact (make sure Contact has a default constructor!) m_contacts.insert(row, Contact()); // Finalize the insertion endInsertRows(); return true; }
Step 2: Add a QML-friendly overload for pre-filled contacts
If you want to insert a contact with name/number directly from QML (instead of inserting an empty row then editing it), create a custom Q_INVOKABLE method:
Q_INVOKABLE bool insertContact(int row, const QString &name, const QString &number) { if (row < 0 || row > m_contacts.size()) return false; beginInsertRows(QModelIndex(), row, row); // Use Contact's parameterized constructor to create a pre-filled instance m_contacts.insert(row, Contact(name, number)); endInsertRows(); return true; }
removeRow() and call it from QML Step 1: Override removeRow() in your model
This follows the same pattern as insertRow()—always wrap data changes with the begin/end methods to notify views:
bool ContactBookModel::removeRow(int row, const QModelIndex &parent) { if (row < 0 || row >= m_contacts.size()) return false; beginRemoveRows(parent, row, row); m_contacts.removeAt(row); endRemoveRows(); return true; }
Step 2: Call it from QML
You can either call the standard removeRow() directly, or wrap it in a Q_INVOKABLE method for clarity. For example, in your QML delegate:
ListView { model: contactBookModel delegate: Row { // ... your contact display elements ... Button { text: "Delete" onClicked: contactBookModel.removeRow(model.index.row) } } }
To make contacts editable, you need three key pieces: proper Q_PROPERTY definitions in Contact, an overridden setData() method in the model, and editable item flags.
Step 1: Update the Contact class
Add Q_PROPERTY for each editable field to enable QML binding and notifications:
class Contact : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QString number READ number WRITE setNumber NOTIFY numberChanged) public: // Constructor(s) Contact(QObject *parent = nullptr) : QObject(parent) {} Contact(const QString &name, const QString &number, QObject *parent = nullptr) : QObject(parent), m_name(name), m_number(number) {} // Getters QString name() const { return m_name; } QString number() const { return m_number; } // Setters with change notifications void setName(const QString &name) { if (m_name != name) { m_name = name; emit nameChanged(); } } void setNumber(const QString &number) { if (m_number != number) { m_number = number; emit numberChanged(); } } signals: void nameChanged(); void numberChanged(); private: QString m_name; QString m_number; };
Step 2: Override setData() and flags() in your model
First, ensure you've defined your model roles (if not already done):
enum ContactRoles { NameRole = Qt::UserRole + 1, NumberRole }; QHash<int, QByteArray> ContactBookModel::roleNames() const { QHash<int, QByteArray> roles; roles[NameRole] = "name"; roles[NumberRole] = "number"; return roles; }
Then implement setData() to modify contact fields and notify views:
bool ContactBookModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || index.row() >= m_contacts.size()) return false; Contact &contact = m_contacts[index.row()]; switch (role) { case NameRole: contact.setName(value.toString()); emit dataChanged(index, index, {NameRole}); return true; case NumberRole: contact.setNumber(value.toString()); emit dataChanged(index, index, {NumberRole}); return true; default: return false; } } Qt::ItemFlags ContactBookModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; // Add editable flag to the default flags return QAbstractListModel::flags(index) | Qt::ItemIsEditable; }
Step 3: Edit in QML
Now you can use editable controls like TextField with direct binding:
ListView { model: contactBookModel delegate: Row { TextField { text: model.name onTextChanged: model.name = text // Automatically calls setData() } TextField { text: model.number onTextChanged: model.number = text } } }
内容的提问来源于stack exchange,提问作者Bogdasar

