73 / 7 / 0
Регистрация: 08.08.2012
Сообщений: 223
Записей в блоге: 1
1

QSqlTableModel перезапись

21.01.2016, 18:15. Показов 2079. Ответов 8
Метки нет (Все метки)

Здравствуйте!
Вероятно некорректно назвал тему, но сейчас опишу проблему.
Есть база данных SQLite, в ней содержится несколько таблиц, которые отображаются QTableView с помощью модели QSqlTableModel, а переключение между ними должны происходить по изменению значения QComboBox.
Удалось достичь желаемого, но происходит утечка памяти.
Понимаю, что нужно каждый раз удалять и освобождать память из под существующей модели и только потом выделять для новой модели: но освобождение не происходит. (наверное, из-за того что коннект к базе еще есть)

Подскажите, пожалуйста, как лучше написать и исправить косяк?
editlistdialog.h
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <QDialog>
#include <QDir>
#include <QSqlQuery>
#include <QSqlDatabase>
#include <QSqlTableModel>
 
namespace Ui {
class EditListDialog;
}
 
class EditListDialog : public QDialog
{
    Q_OBJECT
 
public:
    explicit EditListDialog(QWidget *parent = 0);
    ~EditListDialog();
 
public slots:
    void getCategorie(int index);
 
private:
    Ui::EditListDialog *ui;
 
    QSqlTableModel *model = nullptr;
};
editlistdialog.cpp
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "editlistdialog.h"
#include "ui_editlistdialog.h"
 
EditListDialog::EditListDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::EditListDialog)
{
    ui->setupUi(this);
    connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(getCategorie(int)));
}
 
EditListDialog::~EditListDialog()
{
    delete ui;
    delete model;
}
 
void EditListDialog::getCategorie(int index)
{
    QSqlDatabase *dbase = new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE"));
    dbase->setDatabaseName(QDir::currentPath() + "/base/base.sqlite");
    dbase->open();
 
    QSqlQuery query;
    QString str = "CREATE TABLE " + categories[index] + " ("
                  "Имя text, "
                  "Пришло double, "
                  "Ушло double, "
                  ");";
    query.exec(str);
 
    delete model;
    model = nullptr;
 
    model = new QSqlTableModel;
    model->setTable(categories[index]);
    model->setEditStrategy(QSqlTableModel::OnFieldChange);
    model->select();
 
    ui->tableView->setModel(model);
    ui->tableView->setEditTriggers(QAbstractItemView::DoubleClicked);
 
    delete dbase;
}
*QString categories[] существует и определен, но в коде опущен из-за большого количества букв
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
21.01.2016, 18:15
Ответы с готовыми решениями:

QSqlTableModel
Делаю наследника от QSqlTableModel. Встал вопрос, как удалить строку из таблицы, не используя...

QSqlTableModel
Исходные данные: msvs 2010, qt 5.0.2 и 5.2.1, ms sql server 2008 В Бд есть табличка с полями...

QTableView и QSqlTableModel
Пытаюсь вывести в QTableView модель из QSqlTableModel. Буквально на пол секунды в TableView...

QSqlTableModel выборка
Здравствуйте! В общем у меня есть файл с избранными блюдами, там их мб великое множество. Я...

8
44 / 44 / 12
Регистрация: 05.04.2015
Сообщений: 345
21.01.2016, 20:59 2
Не нужно создавать указатель на добавляемую базу данных.
C++ (Qt)
1
2
3
QSqlDatabase dbase = QSqlDatabase::addDatabase("QSQLITE");
dbase->setDatabaseName(QDir::currentPath() + "/base/base.sqlite");
dbase->open();
Обращение к определенной базе происходит через функцию QSqlDatabase::database(<имя подключения>). Если база одна то имя подключения задавать не нужно, оно будет по умолчанию.
Я думаю в вашем случае и базу и модель лучше создать в конструкторе. При переключении таблиц просто делаете
C++ (Qt)
1
2
3
model->setTable(categories[index]);
//model->setEditStrategy(QSqlTableModel::OnFieldChange); это можно в конструкторе указать 1 раз
model->select();
Закрытие соединения происходит само при выходе из программы(в каком то из деструкторов видимо)
1
73 / 7 / 0
Регистрация: 08.08.2012
Сообщений: 223
Записей в блоге: 1
22.01.2016, 13:20  [ТС] 3
kolts, действительно вы правы. Я думал, что модель строго привязывается к таблицы базы, но оказалось, что в ней коннект только к базе и можно легко переключатся на таблицы и их редактировать.

Можно ли еще вопрос?
Получается я работаю с несколькими базами и доступ к ним получаю с разных форм: т.е. существует основная база, таблица которой всегда хранится на главной форме (тоже модель), коннект и модель хранятся на форме (указатели).
Если жмем какую-нибудь кнопку попадаем на другую форму, а там уже новый коннект и новая модель (внутренняя, база тоже другая). В деструкторе я освобождаю память (delete dbase; delete model).

Внимание вопрос! Qt при нажатии той самой кнопки в Debug пишет о перезаписи соединения к базе (dbase) и мы можем работать с новой базой в новой форме. Нужно ли закрывать QSqlDatabase и как-то стирать? Или он спокойно перезаписывается ? Не хотелось бы еще утечки с этой стороны. Пока базы маленькие и это не заметно. Но с увеличением - это может стать проблемой.

Добавлено через 1 час 19 минут
kolts, вопрос по теме. Как правильно очистить память после использования базы ?
C++ (Qt)
1
delete dbase; delete model;
Эти delete срабатывают, но вот только при закрытии формы, на которой была работа и были созданы модель и коннект, память очищается не полностью (т.е. не до исходного - состояния до отображения формы)
Нужно закрывать коннект и тогда чистка будет полной? Или я что-то упустил?
0
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
22.01.2016, 13:30 4
Лучший ответ Сообщение было отмечено mr_free как решение

Решение

Итак, снова, базы хранятся в недрах Qt, получать доступ к ним можно из любой точки кода по имени соединения (В рамках единого потока).

Соединение открывается и живёт до тех пор, пока работает приложение или пока явно не вызовут close. Использовать указатели на базы категорически нельзя!
В вашем случае получается, что нужно проверять, не открыто ли уже соединение перед тем, как открывать заново.
0
73 / 7 / 0
Регистрация: 08.08.2012
Сообщений: 223
Записей в блоге: 1
22.01.2016, 13:38  [ТС] 5
Вот, что происходит с памятью:
23,2 MB (Запуск приложения) -> 24,2 MB (Открытие формы (диалоговое окно)) -> 23,5 MB (Закрытие диалогового окна) -> 24,2 (Повторное открытие диалогового окна) -> 24,5 (Выбрана конкретная таблица БД и отображена) -> 23,6 (Закрытие диалогового окна)

Это ведь утечка, хоть и не значительная.
0
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
22.01.2016, 13:58 6
Возможно. А может, нет, внутри полно аллокаций и освобождение ресурсов не влечёт за собой освобождение кучи.
Если работаете под MSVC, воспользуйтесь Visual Leak Detector. За MinGW сказать о подобных инструментах ничего не имею.
0
73 / 7 / 0
Регистрация: 08.08.2012
Сообщений: 223
Записей в блоге: 1
22.01.2016, 14:11  [ТС] 7
icpu, ясно. Вот только не пойму QSqlDatabase::addDatabase, если не указывать имени соединения, плодит еще одно соединение? Ведь, как я понял, если не указывать имени для соединения, то всегда работаешь с qt_sql_dafault_connection, который по-идеи должен удалятся и создаваться новый.
В Debug пишет:
Код
QSqlDatabasePrivate::removeDatabase: connection 'qt_sql_default_connection' is still in use, all queries will cease to work.
QSqlDatabasePrivate::addDatabase: duplicate connection name 'qt_sql_default_connection', old connection removed.
Но судя по всему он не переписывает соединение, а плодит новое.
0
72 / 72 / 33
Регистрация: 07.11.2015
Сообщений: 281
22.01.2016, 16:20 8
Цитата Сообщение от mr_free Посмотреть сообщение
Но судя по всему он не переписывает соединение, а плодит новое.
По-моему old connection removed намекает об обратном
Вообще kolts правильно написал. Соединение и модель создаются в конструкторе. В слоте лишь меняется таблица и происходит обновление.
0
487 / 365 / 93
Регистрация: 10.03.2011
Сообщений: 1,513
Записей в блоге: 5
22.01.2016, 16:24 9
http://doc.qt.io/qt-5/qsqldata... ddDatabase
Warning: If you add a connection with the same name as an existing connection, the new connection replaces the old one. If you call this function more than once without specifying connectionName, the default connection will be the one replaced.
Внимание: Если вы добавите соединение с тем же именем, что существующее, новое соединение заменит старое. Если вы вызовите функцию без указания connectionName, соединение по умолчанию будет заменено.
Он заменяет существующие. Более того, он его утилизирует и все экземпляры QSqlDatabase, полученные до переназначения, становятся невалидными, как и многие другие SQL классы. Работа с двумя базами одновременно
Если для вас не критична продолжительность сессии, открывайте соединение заранее или воспользуйтесь паттерном RAII.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
22.01.2016, 16:24
Помогаю со студенческими работами здесь

QSqlTableModel вычисляемое поле
Здравствуйте. Подскажите как реализовать в QSqlTableModel вычисляемое поле? Мне нужно отобразить в...

Не работает insertRecord в QSqlTableModel
Здравствуйте. Kubuntu 14.10, Qt 4.8 Не пишется строка в таблицу. А в QTableView, в лучшем случае...

QSqlTableModel редактирование ячейки
Использую QSqlTableModel в связке с QTableView для вывода таблицы БД и ее редактирования. Таблицу...

Базы данных и Qt. QSqlTableModel
Здравствуйте. Создаю базу данных, таблицу. На форму добавляю элемент QTableView. Хочу связать его с...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2023, CyberForum.ru