Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Обработка крупного QTreeWidget гео-локаций, Сохранение дерева в базу данных SQLite 
:(
    Опции темы
loneybibi
Дата 3.12.2012, 05:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Любитель
**


Профиль
Группа: Участник
Сообщений: 257
Регистрация: 28.5.2010
Где: Донецк (Украина)

Репутация: 3
Всего: 3



Сразу скажу спасибо тем, кто осилит прочитать и понять мою тему, а так же за ваши ответы,
помощь и подсказки! smile 

Есть дерево QTreeWidget с набором элементов чуть больше 12000 в трех уровнях, которое содержит
список географических локаций (страны, регионы, города), выглядит примерно вот так:
Код

Россия
|
-----Московская область
        |
         --------Москва

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

Вообщем то все работает, но не так как хотелось бы.

Полный проект вместе с базой гео-локаций прикрепил к теме.
Функции чтения и записи в файл две последние в mainwindow.cpp.
Исходники с комментариями! smile

Есть несколько вопросов:
1. Как проверить что все элементы в дереве имеют 3 уровня вложений,
то есть как приведено выше (страна, регион, город). Я конечно понимаю что можно
перебрать вручную все дерево в цикле, но может можно как то это сделать более оптимально?

2. В программе есть функция записи в файл. Запись осуществляется перебором всех элементов
в цикле(рекурсивно), где берется его имя, индекс и заноситься в базу. Но при таком количестве это происходит
ужасно медленно (приблизительно 2 минуты), учитывая то что это файловая база!
Те же действия в MySql происходят на половину быстрее. Почему так? Особенности драйвера или работы
с QSQLITE?

Возможно кто то подскажет как оптимизировать мою функцию или это нормально для такого количества?
ЧЯДНТ ? smile

3. Тоже самое и с функцией чтения! Опять же происходит это в цикле (рекурсивно) где читаются
элементы и каждый добавляется в дерево. Хотя это и занимает до 5 секунд, я думаю что можно как то
сделать это намного быстрее! Подскажите пожалуйста! smile

4. Сталкивались вы с таким же вопросом? Как хранили данные? Какие форматы использовали?
Например я думал о XML но с ним никогда не работал! Будет ли он быстрее и сложно ли с ним построить
такое дерево? 
Еще думал про CSV формат но не хотелось писать парсер файла загоняющий данные в модель
таблицы (с ней удобнее работать и строить дерево).

Что не понятно спрашивайте попробую правильней объяснить.

Вообщем кто что подскажет или поделиться чем то, буду признателен! smile

Функция записи дерева в файл:
Код

void MainWindow::saveFile(const QString &fileName)
{
    QFile f(fileName);
    if(f.exists()){
        QFile::remove(f.fileName());
        f.close();
    }

    /** [0] Create tables **/
    QSqlDatabase db=QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(fileName);
    db.open();

    QSqlQuery query;

    query.prepare("CREATE TABLE \"country\" (\"country-id\" TEXT,"
           "\"country-name\" TEXT)");

    query.exec();

    query.prepare("CREATE TABLE \"region\" (\"country-id\" TEXT,"
           "\"region-id\" TEXT, \"region-name\" TEXT)");

    query.exec();

    query.prepare("CREATE TABLE \"city\" (\"region-id\" TEXT,"
           "\"city-id\" TEXT, \"city-name\" TEXT)");

    query.exec();

    /** [1] Top level items section **/
    for(int i=0;i<ui->treeWidget->topLevelItemCount();++i){
        query.prepare("INSERT INTO `main`.`country` (`country-id`,`country-name`) VALUES "
                      "('"+ui->treeWidget->topLevelItem(i)->data(0,Qt::UserRole).toString()+
                      "','"+ui->treeWidget->topLevelItem(i)->text(0)+"')");
        query.exec();

        QTreeWidgetItem *topItem=ui->treeWidget->topLevelItem(i);

        /** [2] Second level items section **/
        for(int i=0;i<topItem->childCount();++i){
            query.prepare("INSERT INTO `main`.`region` (`country-id`,`region-id`,`region-name`)"
                          "VALUES('"+topItem->data(0,Qt::UserRole).toString()+
                          "','"+topItem->child(i)->data(0,Qt::UserRole).toString()+
                          "','"+topItem->child(i)->text(0)+"')");
            query.exec();

            QTreeWidgetItem *secItem=topItem->child(i);

            /** [3] Third level items section **/
            for(int i=0;i<secItem->childCount();++i){
                query.prepare("INSERT INTO `main`.`city` (`region-id`,`city-id`,`city-name`)"
                              "VALUES('"+secItem->data(0,Qt::UserRole).toString()+
                              "','"+secItem->child(i)->data(0,Qt::UserRole).toString()+
                              "','"+secItem->child(i)->text(0)+"')");
                query.exec();
            }
        }
    }

    db.close();
}

Функция чтения базы из файла и построения дерева:
Код

void MainWindow::openFile(const QString &fileName)
{
    /** [0] **/
    QSqlDatabase db=QSqlDatabase::addDatabase("QSQLITE",createKey());
    db.setDatabaseName(fileName);
    db.open();

    QSqlTableModel topItem(this,db);
    topItem.setTable("country");
    topItem.select();

    /** [1] Top level items section [1] **/
    if(topItem.rowCount()>0){
        for(int i=0;i<topItem.rowCount();++i){
            QTreeWidgetItem *item=new QTreeWidgetItem;
            item->setText(0,topItem.record(i).value("country-name").toString());
            item->setData(0,Qt::UserRole,createKey());
            ui->treeWidget->addTopLevelItem(item);

            /** [2] Second level items section **/
            QSqlTableModel secItem(this,db);
            secItem.setTable("region");
            secItem.setFilter("`country-id`='"+topItem.record(i).value("country-id").toString()+"'");
            secItem.select();

            if(secItem.rowCount()>0){
                for(int i=0;i<secItem.rowCount();++i){
                    QTreeWidgetItem *sItem=new QTreeWidgetItem;
                    sItem->setText(0,secItem.record(i).value("region-name").toString());
                    sItem->setData(0,Qt::UserRole,createKey());
                    item->addChild(sItem);

                    /** [3] Third level items section **/
                    QSqlTableModel thirdItem(this,db);
                    thirdItem.setTable("city");
                    thirdItem.setFilter("`region-id`='"+secItem.record(i).value("region-id").toString()+"'");
                    thirdItem.select();

                    if(thirdItem.rowCount()>0){
                        for(int i=0;i<thirdItem.rowCount();++i){
                            QTreeWidgetItem *tItem=new QTreeWidgetItem;
                            tItem->setText(0,thirdItem.record(i).value("city-name").toString());
                            tItem->setData(0,Qt::UserRole,createKey());
                            sItem->addChild(tItem);
                        }
                    }
                }
            }
        }
    }

    db.close();
}


Это сообщение отредактировал(а) loneybibi - 3.12.2012, 05:18

Присоединённый файл ( Кол-во скачиваний: 5 )
Присоединённый файл  location_project.zip 476,74 Kb


--------------------
Red Hat Fedora 17
Qt 4.8.1 (x64), GCC 4.4.3,
G++ 4.4.3, QtCreator 2.4.1
PM MAIL ICQ   Вверх
loneybibi
Дата 4.12.2012, 11:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Любитель
**


Профиль
Группа: Участник
Сообщений: 257
Регистрация: 28.5.2010
Где: Донецк (Украина)

Репутация: 3
Всего: 3



Up! smile
Удалось улучшить алгоритм записи дерева в базу данных! Время записи сократилось
с 2 минут до 1.2 секунды. 

Проблема со скоростью чтения осталась так что подсказывайте! smile 
Полный проект прикрепил к посту! 

Все проблема была в особенностях настроек драйвера SQLITE.
Вообщем как известно все настройки по умолчанию да и цель разработчиков этой БД,
это очень высокая надежность и отказоустойчивость!

Вообщем суть в том что при каждой операции вставки строки данных в таблицу,
SQLITE вызывает не явную транзакцию для надежности сохранения данный.
А так как драйвер SQLITE физически не может делать более ~50 транзакций в
секунду, соответственно и была такая низкая скорость записи.

Отсюда вывод начинать свою транзакцию вручную явно, и только после этого
запихивать данные! 

Еще рекомендуется (но я не сделал, кто сделает напишите об улучшениях или изменениях)
вставлять данные порциями не более 1000 записей например.

Чтиво рекомендуемое к прочтению для познания тонких моментов SQLITE :
http://habrahabr.ru/post/149356/

Код новой функции записи:
Код

/** Save tree to SQLite database **/
void MainWindow::saveFile(const QString &fileName)
{
    /** [0] Check file exists and remove if exists **/
    if(QFile::exists(fileName)){
        QFile::remove(fileName);
    }
    /** [0] End of check file exists and remove if exists **/


    /** [1] Create database connection **/
    QSqlDatabase db=QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(fileName);
    if(!db.open()){
        QMessageBox::critical(this,"Critical error",
                              "Can't open database.");
        return;
    }
    /** [1] End of create database connection **/


    /** [2] Prepare query to exeute **/
    QStringList queryList;
    queryList.append("PRAGMA encoding=\"UTF-8\"");
    queryList.append("BEGIN");
    queryList.append("CREATE TABLE \"country\" (\"country-id\" TEXT,"
                     "\"country-name\" TEXT)");
    queryList.append("CREATE TABLE \"region\" (\"country-id\" TEXT,"
           "\"region-id\" TEXT, \"region-name\" TEXT)");
    queryList.append("CREATE TABLE \"city\" (\"region-id\" TEXT,"
           "\"city-id\" TEXT, \"city-name\" TEXT)");
    /** [2] End of query prepare **/


    /** [3] Prepare top level items **/
    if(ui->treeWidget->topLevelItemCount()>0){
        for(int i=0;i<ui->treeWidget->topLevelItemCount();++i){
            queryList.append("INSERT INTO `main`.`country`"
                             "(`country-id`,`country-name`) VALUES ('"+
                             ui->treeWidget->topLevelItem(i)->data(0,Qt::UserRole).toString()+
                             "','"+ui->treeWidget->topLevelItem(i)->text(0)+"')");
            QTreeWidgetItem *topItem=ui->treeWidget->topLevelItem(i);
            /** [3] End of prepare top level items **/


            /** [4] Prepare second level items **/
            if(topItem->childCount()>0){
                for(int i=0;i<topItem->childCount();++i){
                    queryList.append("INSERT INTO `main`.`region`"
                                     "(`country-id`,`region-id`,`region-name`) VALUES ('"+
                                     topItem->data(0,Qt::UserRole).toString()+"','"+
                                     topItem->child(i)->data(0,Qt::UserRole).toString()+"','"+
                                     topItem->child(i)->text(0)+"')");
                    QTreeWidgetItem *secondItem=topItem->child(i);
                    /** [4] End of prepare second level items **/


                    /** [5] Prepare third level items **/
                    if(secondItem->childCount()>0){
                        for(int i=0;i<secondItem->childCount();++i){
                            queryList.append("INSERT INTO `main`.`city`"
                                             "(`region-id`,`city-id`,`city-name`) VALUES ('"+
                                             secondItem->data(0,Qt::UserRole).toString()+"','"+
                                             secondItem->child(i)->data(0,Qt::UserRole).toString()+"','"+
                                             secondItem->child(i)->text(0)+"')"
                                             );
                            /** [5] End of prepare third level items **/
                        }
                    }else return;
                }
            }else return;
        }
    }else return;


    /** [6] Other query commands **/
    queryList.append("COMMIT");
    /** [6] End other query commands **/


    /** [7] Execute query **/
    if(queryList.count()>0){
        QSqlQuery query;
        for(int i=0;i<queryList.count();++i){
            query.prepare(queryList.at(i));
            query.exec();
        }
    }else return;
    /** [7] End of query execute **/

    db.close();
}


Это сообщение отредактировал(а) loneybibi - 4.12.2012, 11:30

Присоединённый файл ( Кол-во скачиваний: 8 )
Присоединённый файл  location_project.zip 477,01 Kb


--------------------
Red Hat Fedora 17
Qt 4.8.1 (x64), GCC 4.4.3,
G++ 4.4.3, QtCreator 2.4.1
PM MAIL ICQ   Вверх
borisbn
Дата 4.12.2012, 13:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 48
Всего: 135



Цитата(loneybibi @  4.12.2012,  11:30 Найти цитируемый пост)
Проблема со скоростью чтения осталась так что подсказывайте! 

Есть подозрение, что тормозит не БД, а перерисовка списка при каждой вставке. Попробуй перед циклом создать QList<QTreeWidgetItem *>, в цикле вставлять только в него, а после цикла вызвать addChildren

И ещё: есть подозрение, что 12000 айтемов одновременно никакому юзеру не нужно. М.б. сделать комбо-бокс со странами, а в лист-вью уже выводить только данные выбранной страны (желательно загрузить их из БД сразу все, а при изменении комбо-бокса только делать clear и addChildren)


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
loneybibi
Дата 4.12.2012, 13:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Любитель
**


Профиль
Группа: Участник
Сообщений: 257
Регистрация: 28.5.2010
Где: Донецк (Украина)

Репутация: 3
Всего: 3



Цитата(borisbn @ 4.12.2012,  12:28)
Цитата(loneybibi @  4.12.2012,  11:30 Найти цитируемый пост)
Проблема со скоростью чтения осталась так что подсказывайте! 

Есть подозрение, что тормозит не БД, а перерисовка списка при каждой вставке. Попробуй перед циклом создать QList<QTreeWidgetItem *>, в цикле вставлять только в него, а после цикла вызвать addChildren

И ещё: есть подозрение, что 12000 айтемов одновременно никакому юзеру не нужно. М.б. сделать комбо-бокс со странами, а в лист-вью уже выводить только данные выбранной страны (желательно загрузить их из БД сразу все, а при изменении комбо-бокса только делать clear и addChildren)

Да я тоже почти уверен что пользователю это не надо! smile Просто в данный момент так и использую
QComboBox для страны, региона и города(и пока этот вариант полностью устраивает)! Но думал
вместо трех комбо сделать один с выпадающим деревом и поиском по нему, так сказать что бы
сэкономить место UI.. smile Но если он будет открываться по 3 секунды то это не кайф ! smile
Да и еще не решил будет ли это полезно и удобней!
А вообщем просто интересно как и что по крайней мере для себя знать что и почему.

Спасибо за ответ буду что то пробовать вечером делать! smile 


--------------------
Red Hat Fedora 17
Qt 4.8.1 (x64), GCC 4.4.3,
G++ 4.4.3, QtCreator 2.4.1
PM MAIL ICQ   Вверх
loneybibi
Дата 6.12.2012, 01:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Любитель
**


Профиль
Группа: Участник
Сообщений: 257
Регистрация: 28.5.2010
Где: Донецк (Украина)

Репутация: 3
Всего: 3



UP! 
Пробовал вариант предложеный borisbn.. Не помогло и даже ухудшило время загрузки списка .. 
Может кто еще что подскажет...

Кстати у кого нить нет случаем такого же списка только на английском? smile
Поделитесь пожалуйста или подскажите где взять ? 


--------------------
Red Hat Fedora 17
Qt 4.8.1 (x64), GCC 4.4.3,
G++ 4.4.3, QtCreator 2.4.1
PM MAIL ICQ   Вверх
math64
Дата 6.12.2012, 08:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 23
Всего: 72



Обычно при отрытии дерева (используйте QTreeView + свою модель, а не QTreeWidget) загружаются только корневые узлы, если их много - только первые 100 или 256. остальные загружаются через fetchMore(), когда пользователь скроллит вниз.
Дочерние элементы загружаются при раскрытии их узлов.
Для загрузки можно пользоваться QSqlTableModel.
NB: она по select()  тоже загружает не всё, для загрузки остатка вызывайте canFetchMore() и fetchMore().

PM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С/С++: Кроссплатформенное программирование, QT/Gtk+/wxWidgets"
JackYF
Любитель
  • В заголовке темы в квадратных скобках обозначьте используемую вами библиотеку, например: [QT],[GTK],[wx].
  • Если вопрос актуален только для некоторой версии библиотеки, либо, если вы пользуетесь не самой последней версией, укажите это. Например: [QT4], [GTK2].
  • Все начинающие изучать Qt - не забудьте зайти сюда.
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • В вопросе укажите полную версию версию библиотеки, а также все дополнительные используемые программные пакеты.
  • Не забывайте пользоваться кнопкой "Код".
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к тематике этого раздела. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, Любитель.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets | Следующая тема »


 




[ Время генерации скрипта: 0.0477 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.