crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

 
Ответить в данную темуНачать новую тему
> Хочу изменить порядок обновления данных в QSqlTableModel
mva
  опции профиля:
сообщение 19.6.2009, 21:37
Сообщение #1


Участник
**

Группа: Участник
Сообщений: 104
Регистрация: 15.3.2009
Из: Киров
Пользователь №: 615

Спасибо сказали: 3 раз(а)




Репутация:   0  


Доброго времени суток всем!

Работаю с моделью QSqlTableModel/QTableView.

EditStrategy == OnFieldChange.

Все работает хорошо, но есть одно НО...

Мне нужно, чтобы при изменении пользователем содержимого любой ячейки Grid эти изменения сразу же фиксировались на сервере без каких-либо задержек и нажатий дополнительных кнопок, просто при перемещении на другую ячейку.

Класс QSqlTableModel при изменении данных в одной строке (ячейке) отсылает изменения на сервер (у меня PostgreSQL), а затем ЗАГРУЖАЕТ ТАБЛИЦУ ЦЕЛИКОМ. С большой таблицей в локальной сети это будет работать сносно, а через Интернет - думаю, что очень медленно. Поэтому хочу изменить порядок обновления данных в QSqlTableModel - так, чтобы изменения на сервер отсылались, а последующая загрузка таблицы не происходила... Зачем загружать все данные, если пользователь изменил лишь одну ячейку?

Поясню на исходном коде класса QSqlTableModel.
bool QSqlTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    Q_D(QSqlTableModel);
    if (role != Qt::EditRole)
        return QSqlQueryModel::setData(index, value, role);

    if (!index.isValid() || index.column() >= d->rec.count() || index.row() >= rowCount())
        return false;

    bool isOk = true;
    switch (d->strategy) {
    case OnFieldChange: {
        if (index.row() == d->insertIndex) {
            d->editBuffer.setValue(index.column(), value);
            return true;
        }
        d->clearEditBuffer();
        d->editBuffer.setValue(index.column(), value);
        isOk = updateRowInTable(index.row(), d->editBuffer);
    /*>>*/  if (isOk)            // хочу убрать этот код
    /*>>*/        select();
        break; }
    case OnRowChange:
        if (index.row() == d->insertIndex) {
            d->editBuffer.setValue(index.column(), value);
            return true;
        }
        if (d->editIndex != index.row()) {
            if (d->editIndex != -1)
                submit();
            d->clearEditBuffer();
        }
        d->editBuffer.setValue(index.column(), value);
        d->editIndex = index.row();
        emit dataChanged(index, index);
        break;
    case OnManualSubmit: {
        QSqlTableModelPrivate::ModifiedRow &row = d->cache[index.row()];
        if (row.op == QSqlTableModelPrivate::None) {
            row.op = QSqlTableModelPrivate::Update;
            row.rec = d->rec;
            row.primaryValues = d->primaryValues(indexInQuery(index).row());
        }
        row.rec.setValue(index.column(), value);
        emit dataChanged(index, index);
        break; }
    }
    return isOk;
}


Пытаюсь создать наследника класса QSqlTableModel и переопределить в нем функцию setData:
bool MySqlTableModel::setData(const QModelIndex &index, const QVariant &value, int role) {
//    Q_D(QSqlTableModel);
    if (role != Qt::EditRole)
        return QSqlQueryModel::setData(index, value, role);

    if (!index.isValid() || index.column() >= d->rec.count() || index.row() >= rowCount())
        return false;

    bool isOk = true;
    if (editStrategy == QSqlTableModel::OnFieldChange) {
        if (index.row() == d->insertIndex) {
            d->editBuffer.setValue(index.column(), value);
            return true;
        }
        d->clearEditBuffer();
        d->editBuffer.setValue(index.column(), value);
        isOk = updateRowInTable(index.row(), d->editBuffer);
    }
    else
        isOk = QSqlTableModel::setData(index, value, role);
    return isOk;
}

Проблема в том, что здесь используются какие-то недокументированные функции и переменные (например макрос Q_D и переменная d ), к которым я не знаю, как добраться. Код в таком виде не компилируется.

А может быть есть более простой путь?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
MoPDoBoPoT
  опции профиля:
сообщение 20.6.2009, 0:28
Сообщение #2


Участник
**

Группа: Участник
Сообщений: 172
Регистрация: 7.5.2009
Из: Москва
Пользователь №: 738

Спасибо сказали: 44 раз(а)




Репутация:   9  


Цитата(mva @ 19.6.2009, 22:37) *
Проблема в том, что здесь используются какие-то недокументированные функции и переменные (например макрос Q_D и переменная d )

Эти вещи относятся к private-реализации класса (см. ичходники). Можешь конечно запихнуть Q_DECLARE_PRIVATE(SqlTableModel) в область private своей MySqlTableModel и тогда приватный класс становится доступным, НО это не панацея... Завтра поменяют реализацию приватной части и будешь локти кусать.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Litkevich Yuriy
  опции профиля:
сообщение 20.6.2009, 2:03
Сообщение #3


разработчик РЭА
*******

Группа: Сомодератор
Сообщений: 9669
Регистрация: 9.1.2008
Из: Тюмень
Пользователь №: 64

Спасибо сказали: 807 раз(а)




Репутация:   94  


mva, как вариант, можно попробовать сделать средствами API.
Т.е. политику ставить "OnManualSubmit" ловить факт изменения данных, и собственно ручно их фиксировать в БД. Тогда, возможно, перечитывания данных из БД не будет.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
mva
  опции профиля:
сообщение 20.6.2009, 10:31
Сообщение #4


Участник
**

Группа: Участник
Сообщений: 104
Регистрация: 15.3.2009
Из: Киров
Пользователь №: 615

Спасибо сказали: 3 раз(а)




Репутация:   0  


Цитата(Litkevich Yuriy @ 20.6.2009, 3:03) *
mva, как вариант, можно попробовать сделать средствами API.
Т.е. политику ставить "OnManualSubmit" ловить факт изменения данных, и собственно ручно их фиксировать в БД. Тогда, возможно, перечитывания данных из БД не будет.


Установил политику в "OnManualSubmit". Исходя из документации в этом случае я должен использовать функцию QSqlTablemodel::submitAll(). Сделал, решил проверить сниффером какие данные идут на сервер и обратно. Получается, что при использовании фунции submitAll() на сервер ТРИЖДЫ отправляется один и тот же запрос на закачку данных и ТРИЖДЫ одинаковые данные передаются обратно.

Этот вариант мне не подходит...
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
mva
  опции профиля:
сообщение 24.6.2009, 11:02
Сообщение #5


Участник
**

Группа: Участник
Сообщений: 104
Регистрация: 15.3.2009
Из: Киров
Пользователь №: 615

Спасибо сказали: 3 раз(а)




Репутация:   0  


Решил я эту задачку... Пришлось изучать исходный код в части работы с базами данных. Как оказалось в этом плане Qt работает довольно примитивно. При использовании политики OnFieldChange результат изменений ячейки данных в Grid сначала отправляется на сервер, а затем для того, чтобы отобразить эти изменения, посылается запрос на загрузку всех данных... Это очень неэффективно... При использовании других политик происходит примерно то же самое, только с задержкой по времени. Для OnManualSubmit - изменения загружаются после команды submilAll. Но в этом случае изменения сохраняются в буфере и отображаются в виджете Grid. Этот момент я и использовал.

После множества экспериментов все свелось к простому дочернему классу:
class MySqlTableModel : public QSqlTableModel {
public:
    MySqlTableModel();
    bool setData(const QModelIndex &, const QVariant &, int);
};

MySqlTableModel::MySqlTableModel() : QSqlTableModel() {
    setEditStrategy(QSqlTableModel::OnManualSubmit);
}

bool MySqlTableModel::setData(const QModelIndex &index, const QVariant &value, int role) {
    bool lResult = false;
    lResult = QSqlTableModel::setData(index, value, role);
    if (lResult) {
        QSqlRecord rec = record(index.row());
        rec.setValue(index.column(), value);
        rec.setGenerated(index.column(), true);
        lResult = updateRowInTable(index.row(), rec);
    }
    return lResult;
}


При использовании этого класса вместо QSqlTableModel нет необходимости вызова submitAll для сохранении данных на сервере, хотя здесь и используется политика OnManualSubmit. Изменения сохраняются на сервере сразу, как только мы перемещаемся на другую ячейку, при этом фокус курсора не теряется (не изменяется). Это то, что мне и требовалось.


Чем-то этот вариант похож на предложение Литкевича Юрия.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
SABROG
  опции профиля:
сообщение 24.6.2009, 12:21
Сообщение #6


Профессионал
*****

Группа: Участник
Сообщений: 1207
Регистрация: 8.12.2008
Из: Russia, Moscow
Пользователь №: 446

Спасибо сказали: 229 раз(а)




Репутация:   34  


Цитата(mva @ 24.6.2009, 12:02) *
Это очень неэффективно...


В таблицах БД могут быть вычисляемые поля, данные в которых зависят от содержания других полей. Интересно DBGrid в дельфи работает по такому же принципу или нет.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
BRE
  опции профиля:
сообщение 24.6.2009, 13:11
Сообщение #7


Профессионал
*****

Группа: Участник
Сообщений: 1112
Регистрация: 6.3.2009
Из: Ростов-на-Дону
Пользователь №: 591

Спасибо сказали: 264 раз(а)




Репутация:   44  


Цитата(SABROG @ 24.6.2009, 13:21) *
В таблицах БД могут быть вычисляемые поля, данные в которых зависят от содержания других полей.

Плюс к этому, если по содержимому этого поля идет сортировка, то порядок следования записи может измениться и должен быть изменени на экране, также интересные эффекты могут случиться при работе с фильтрами (изменили поле и оно перестало попадать под фильт в запросе, строка должна удалиться с экрана). :rolleyes:
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Гость_Гость_mva_*_*
сообщение 24.6.2009, 14:01
Сообщение #8





Гости








    


Цитата(SABROG @ 24.6.2009, 12:21) *
В таблицах БД могут быть вычисляемые поля, данные в которых зависят от содержания других полей.

Ничто не мешает использовать вычисляемые поля, если таковые имеются. Если эти поля входят в запрос - то вычислениями занимается сервер. Клиент всего лишь их отображает. А если они вычисляются в клиентском приложении, то в этом тоже нет никакой проблемы, т.к. этот дочерний класс сохраняет все свойства класса QSqlTableModel. Что касается фильтрации или сортировки, то никто не мешает вызывать функцию submitAll() для обновления отображения, как это предусмотрено использованием политики OnManualSubmit. Просто с использованием этого дочернего класса есть возможность сильно сократить трафик и ускорить работу приложения.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Гость_asg128_*
сообщение 13.9.2011, 15:59
Сообщение #9





Гости








    


Можно также подключиться к сигналам CommitData (чтобы узнать, какая запись текущая) и CloseEditor (чтобы перейти к текущей записи после обновления QSqlTableModel) делегата (получается функцией itemDelegate модели). Потребуется чуть больше кода, зато корректно будет работать при наличии фильтров/сортировки.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 25.4.2024, 5:05