Пример объемный, но минимально полный, содержащий всю необходимую инфраструктуру. Допустим, мы хотим создать редактор для некоторых статей (блога?). Нам нужен простой класс
Article
, чтобы собрать в кучу данные о каждой отдельной статье:
class Article
{
public:
Article(int id, const QString& title, const QString& content)
: _id(id), _title(title), _content(content) {}
int id() const { return _id; }
void setId(int id) { _id = id; }
QString title() const { return _title; }
void setTitle(const QString& title) { _title = title; }
QString content() const { return _content; }
void setContent(const QString& content) { _content = content; }
private:
int _id;
QString _title;
QString _content;
};
Для него напишем модель хранения этих статей - ArticlesModel, содержащей массив статей и методы для их добавления/удаления. Кроме того, для нее мы реализуем стандартный интерфейс QAbstractTableModel (методы
rowCount
,
columnCount
,
data
и
setData
), чтобы виджеты знали, как оттуда брать и как обновлять данные.
class ArticlesModel :
public QAbstractTableModel
{
public:
ArticlesModel(QObject *parent=nullptr);
void addArticle(const Article& art);
void removeArticle(int id);
//QAbstractTableModel interface implamentation
int rowCount(const QModelIndex &/*parent*/) const override;
int columnCount(const QModelIndex &/*parent*/) const override;
bool data(QModelIndex& index, const QVariant &value, int role) const override;
QVariant setData(QModelIndex& index, int role) override;
private:
std::vector<Article> _articles;
};
// =================================================================
ArticlesModel(QObject *parent/*=nullptr*/)
: QAbstractTableModel(parent)
{
}
void ArticlesModel::addArticle(const Article& art) {
_articles.push_back(art);
}
void ArticlesModel::removeArticle(int id) {
auto found = std::find_if(begin(_articles), end(_articles),
[id](const Article& art) {
art.id() == id;
});
if (found != std::end(_articles)) {
_articles.erase(artIt);
}
}
int ArticlesModel::rowCount(const QModelIndex &/*parent*/) const override {
return _articles.size();
}
int ArticlesModel::columnCount(const QModelIndex &/*parent*/) const override {
return 2;
}
bool ArticlesModel::data(QModelIndex& index, const QVariant &value, int role) const override {
if (!index.isValid) return QVariant();
if (role == Qt::EditRole) {
switch (column) {
case 0:
QVariant(_articles[index.row()].setTitle(value.toString()));
return true;
case 1:
QVariant(_articles[index.row()].setContent(value.toString()));
return true;
}
}
return false;
}
QVariant ArticlesModel::setData(QModelIndex& index, int role) override {
if (!index.isValid) return QVariant();
if (role == Qt::DisplayRole || role == Qt::EditRole) {
switch (column) {
case 0: return QVariant(_articles[index.row()].title());
case 1: return QVariant(_articles[index.row()].content());
}
}
return QVariant();
}
Тогда можно сделать так:
// this - это какой-то родительский виджет
auto titleEditor = new QLineEdit(this);
auto contentEditor = new QTextEdit(this);
QAbstractTableModel *model = new ArticlesModel(this);
model->addArticle(Article(1, "Hello title", "Hello content"));
model->addArticle(Article(2, "Another title", "Another content"));
auto mapper = new QDataWidgetMapper(this);
mapper->setModel(model);
mapper->addMapping(titleEditor, 0);
mapper->addMapping(contentEditor, 1);
mapper->toFirst(); // Переключаемся на первый элемент модели
Теперь мы можем добавлять/удалять статьи в
model->add/removeArticle(const Article&)
, переходить с заметки на заметку с помощью
mapper->setCurrentIndex(int index)
, менять содержимое заметок в редакторе.
Можно добавить
QListView
, так же передать туда модель через
setModel()
и связать сигнал изменения текущего элемента списка с тем же действием mapper, добавить пару кнопок для добавления/удаления заметок и мы получим полноценный редактор.