Возможно, данная тема обсуждалась большое количество раз, но я не смог найти однозначного ответа и рабочей реализации задачи.
Имеется модель, унаследованная от QAbstractTableModel. Модель редактируемая. Пользователь может изменять некоторые данные в ней, так же добавлять/удалять строки.
Источником данных выступает класс Scanner для каждой строки. Данные в объекте изменяются внутри. Вообще это отображение данных api запросов, но для примера написал некий счетчик, который обновляется по таймеру.
Scanner::Scanner(QObject * parent)
: QObject (parent)
{
auto timer = new QTimer();
connect(timer, &QTimer::timeout, this, [this] {
this->setDifference(getDifference() + 1);
emit dataChanged();
});
timer->start(1000);
}
Для каждой строки в view создается отдельный объект Scanner.
bool ScanningModel::insertRows(int position, int rows, const QModelIndex &parent)
{
beginInsertRows(parent, position, position + rows - 1);
for (int row = 0; row < rows; row++) {
auto scanner = new Scanner();
connect(scanner, &Scanner::dataChanged, this, [this] {
emit beginResetModel();
emit endResetModel();
}, Qt::QueuedConnection);
m_scanners.insert(position, scanner);
}
endInsertRows();
return true;
}
Собственно вопрос: как уведомить модель о том, что данные в Scanner изменились и нужно их отобразить в view?
Вариант с beginResetModel и endResetModel работает. Но при этом модель сбрасывается и выделенная строка в view тоже. Т.е, чтобы внести изменения в строку через интерфейс, пользователю нужно успеть это сделать.
Для данной задачи предназначен сигнал в модели dataChanged. Но для него нужно передавать QModelIndex, которого нет в Scanner и сам index может изменится в любой момент (когда пользователь удалил/добавил новые строки).
В вашем коде вы уведомили модель о том, что данные изменились: вы соединили сигнал Scanner::dataChanged с лямбда-слотом модели. Тут всё правильно. Стало быть, ваш вопрос в том, как уведомить представление о том, что данные изменились. И опять вы сами же ответили: сигналом dataChanged модели. Осталось понять, как сформировать левый верхний и правый нижний индексы.
Сигнал dataChanged имеет следующие параметры:
void QAbstractItemModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles = ...)
Одним из способов решения (не буду утверждать, что самым оптимальным) будет добавить в класс Scanner числовое поле, показывающее номер строки. Например, так:
class Scanner : public QObject {
Q_OBJECT
int m_rowNumber;
public:
int rowNumber();
void setRowNumber();
};
В модели при добавлении и удалении строк вам нужно будет правильно заполнять номер строк у созданных объектов, а также обновлять номера существующих объектов при их изменении. Когда вы это сделаете, у вас будут все необходимые данные для создания индекса. Тогда вы можете переписать ваш слот следующим образом:
connect(scanner, &Scanner::dataChanged, this, [this] {
Scanner *scanner = sender();//Возвращает объект, который отправил сигнал
dataChanged(index(scanner->rowNumber(), 0), index(scanner->rowNumber(), columnCount()));
}, Qt::QueuedConnection);
Я не проверял, будет ли работать метод sender в лямбда-функции. Но если не будет, вы можете вынести её в отдельный слот.
Продвижение своими сайтами как стратегия роста и независимости