diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d4320b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +Makefile +*.user diff --git a/README.md b/README.md index 42110dd..7dd74dd 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # XmlParser GUI program parse xml files, mapping in QTableView and save in SQLite. Qt5 + +#For build +qmake +make + +#For run +./XmlParser \ No newline at end of file diff --git a/XmlParser.pro b/XmlParser.pro new file mode 100644 index 0000000..993718e --- /dev/null +++ b/XmlParser.pro @@ -0,0 +1,29 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2018-11-21T12:15:31 +# +#------------------------------------------------- + +QT += core gui xml sql + +CONFIG += c++11 + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = XmlParser +TEMPLATE = app + + +SOURCES += main.cpp\ + mainwindow.cpp \ + database.cpp \ + parserxmlworker.cpp \ + dialogeditrecord.cpp + +HEADERS += mainwindow.h \ + database.h \ + parserxmlworker.h \ + dialogeditrecord.h + +FORMS += mainwindow.ui \ + dialogeditrecord.ui diff --git a/database.cpp b/database.cpp new file mode 100644 index 0000000..f9b61ae --- /dev/null +++ b/database.cpp @@ -0,0 +1,148 @@ +#include "database.h" + +DataBase::DataBase(QObject *parent) : QObject(parent) +{ +} + + +void DataBase::connectToDataBase() +{ + if(!QFile(DATABASE_NAME).exists()) + { + this->restoreDataBase(); + } + else + { + this->openDataBase(); + } +} + + +/* Методы восстановления базы данных + * */ +bool DataBase::restoreDataBase() +{ + if(this->openDataBase()) + { + if(!this->createTable()) + { + return false; + } + else + { + return true; + } + } + else + { + qDebug() << "Не удалось восстановить базу данных"; + return false; + } + return false; +} + +/* Метод для открытия базы данных */ +bool DataBase::openDataBase() +{ + /* База данных открывается по заданному пути + * и имени базы данных, если она существует + * */ + db = QSqlDatabase::addDatabase("QSQLITE"); + db.setHostName(HOST_NAME); + db.setDatabaseName(DATABASE_NAME); + + if(db.open()) + { + return true; + } + else + { + return false; + } +} + +bool DataBase::createTable() +{ + QSqlQuery query; + QString strCreate = "CREATE TABLE "; + strCreate +=TABLE; + strCreate += " (name INTEGER, value VARCHAR(255));"; + + if(!query.exec(strCreate)) + { + qDebug() << "Error crate table " << query.lastError(); + return false; + } + + return true; +} + +void DataBase::printTables() +{ + foreach(QString str, db.tables()) + { + qDebug() << "Tables:" << str; + } +} + +void DataBase::doInsert(QMultiMap *arrayXml) +{ + int count = 0; + + //doCleanData(); + createTable(); + + QSqlQuery query; + //Insert data in database + //QString strInsert = "INSERT INTO "; + //strInsert += TABLE; + //strInsert += " (name, value) VALUES('%1', '%2');"; + + QString strInsert = "INSERT INTO "; + strInsert += TABLE; + strInsert += " (name, value) VALUES(:name, :value)"; + + query.prepare(strInsert); + + for(QMultiMap::iterator iii = arrayXml->begin(); iii != arrayXml->end(); iii++) + { + //QString strInsert2 = strInsert.arg(iii.key()) + // .arg(iii.value()); + + query.bindValue(":name", iii.key()); + query.bindValue(":value", iii.value()); + + if(!query.exec()) + { + qDebug() << "Error insert data"; + break; + } + + if((count % 10) == 0) + { + + emit changeProgressInsert(count); + } + count++; + + } + + + + emit changeProgressInsert(arrayXml->size()); +} + +void DataBase::doCleanData() +{ + QSqlQuery query; + + QString strDrop = "DROP TABLE "; + strDrop += TABLE; + strDrop += " ;"; + + if(!query.exec(strDrop)) + { + qDebug() << "Error drop table " << query.lastError(); + } + +} diff --git a/database.h b/database.h new file mode 100644 index 0000000..9b7f67d --- /dev/null +++ b/database.h @@ -0,0 +1,40 @@ +#ifndef DATABASE_H +#define DATABASE_H + +#include +#include + +#define DATABASE_NAME "my_sqllite" +#define USER_NAME "user" +#define HOST_NAME "127.0.0.1" +#define PASSWORD "1" + +#define TABLE "xmldata" + + +/* Вспомогтаельный класс для работы с БД */ +class DataBase : public QObject +{ + Q_OBJECT + bool restoreDataBase(); + bool openDataBase(); + + QSqlDatabase db; + +public: + explicit DataBase(QObject *parent = 0); + void connectToDataBase(); + void printTables(); + + bool createTable(); + +signals: + void changeProgressInsert(int value); + +public slots: + void doInsert(QMultiMap *arrayXml); + void doCleanData(); + +}; + +#endif // DATABASE_H diff --git a/dialogeditrecord.cpp b/dialogeditrecord.cpp new file mode 100644 index 0000000..15003e6 --- /dev/null +++ b/dialogeditrecord.cpp @@ -0,0 +1,52 @@ +#include "dialogeditrecord.h" +#include "ui_dialogeditrecord.h" + +DialogEditRecord::DialogEditRecord(int row, QWidget *parent) : + QDialog(parent), + ui(new Ui::DialogEditRecord) +{ + ui->setupUi(this); + + setupModel(); + + if(row != -1) + { + mapper->setCurrentModelIndex(model->index(row,0)); + } + +} + +DialogEditRecord::~DialogEditRecord() +{ + delete ui; +} + +void DialogEditRecord::setupModel() +{ + // Инициализируем модель и делаем выборку + model = new QSqlTableModel(this); + model->setTable(TABLE); + model->setEditStrategy(QSqlTableModel::OnManualSubmit); + model->select(); + + // Инициализируем mapper и привязываем поля данных к объектам LineEdit + mapper = new QDataWidgetMapper(); + mapper->setModel(model); + + mapper->addMapping(ui->lineEditName, 0); + mapper->addMapping(ui->lineEditValue, 1); + + //mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); + //connect(ui->previousButton, SIGNAL(clicked()), mapper, SLOT(toPrevious())); + //connect(ui->nextButton, SIGNAL(clicked()), mapper, SLOT(toNext())); + + connect(mapper, SIGNAL(currentIndexChanged(int)), this, SLOT(updateButtons(int))); +} + +void DialogEditRecord::on_pushButton_clicked() +{ + mapper->submit(); + model->submitAll(); + emit readyToUpdate(); + this->close(); +} diff --git a/dialogeditrecord.h b/dialogeditrecord.h new file mode 100644 index 0000000..87e9237 --- /dev/null +++ b/dialogeditrecord.h @@ -0,0 +1,40 @@ +#ifndef DIALOGEDITRECORD_H +#define DIALOGEDITRECORD_H + +#include "database.h" + +#include +#include +#include +#include +#include + + + +namespace Ui { +class DialogEditRecord; +} + +class DialogEditRecord : public QDialog +{ + Q_OBJECT + +public: + explicit DialogEditRecord(int row, QWidget *parent = 0); + ~DialogEditRecord(); + +signals: + void readyToUpdate(); + +private slots: + void on_pushButton_clicked(); + +private: + void setupModel(); + + Ui::DialogEditRecord *ui; + QSqlTableModel *model; + QDataWidgetMapper *mapper; +}; + +#endif // DIALOGEDITRECORD_H diff --git a/dialogeditrecord.ui b/dialogeditrecord.ui new file mode 100644 index 0000000..8b5fd66 --- /dev/null +++ b/dialogeditrecord.ui @@ -0,0 +1,52 @@ + + + DialogEditRecord + + + + 0 + 0 + 247 + 157 + + + + Dialog + + + + + + + + + + + + + + Имя + + + + + + + Значение + + + + + + + + + Запись + + + + + + + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..b48f94e --- /dev/null +++ b/main.cpp @@ -0,0 +1,11 @@ +#include "mainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..9abea4d --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,235 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include +#include +#include +#include +#include + + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + workerBD = new DataBase(); + workerBD->connectToDataBase(); + workerBD->printTables(); + workerBD->moveToThread(&workerBDThread); + + connect(this, &MainWindow::startInsert, workerBD, &DataBase::doInsert); + connect(workerBD, &DataBase::changeProgressInsert, this, &MainWindow::setProgressInsert); + connect(this, &MainWindow::cleanData, workerBD, &DataBase::doCleanData); + + tableModel = new QSqlTableModel(this); + tableModel->setTable(TABLE); + + QStringList headers; + + headers << trUtf8("Имя") + << trUtf8("Значение"); + + for(int i = 0, j = 0; i < tableModel->columnCount(); i++, j++) + { + tableModel->setHeaderData(i,Qt::Horizontal,headers[j]); + } + tableModel->setEditStrategy(QSqlTableModel::OnRowChange); + // Устанавливаем сортировку по возрастанию данных по нулевой колонке + tableModel->setSort(0,Qt::AscendingOrder); + // Делаем выборку данных из таблицы + tableModel->select(); + + // Устанавливаем модель на TableView + ui->tableView->setModel(tableModel); + // Разрешаем выделение строк + ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + // Устанавливаем режим выделения лишь одно строки в таблице + ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection); + // Устанавливаем размер колонок по содержимому + ui->tableView->resizeColumnsToContents(); + ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->tableView->horizontalHeader()->setStretchLastSection(true); + // Устанавливаем Контекстное Меню + ui->tableView->setContextMenuPolicy(Qt::CustomContextMenu); + + //connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(slotEditRecord())); + // Подключаем СЛОТ вызова контекстного меню + connect(ui->tableView, &QTableView::customContextMenuRequested, this, &MainWindow::customMenuRequested); + + workerXML = new ParserXMLWorker(); + workerXML->moveToThread(&workerXMLThread); + + //connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); + connect(this, &MainWindow::startParseXML, workerXML, &ParserXMLWorker::doParser); + connect(workerXML, &ParserXMLWorker::changeProgressParseXML, this, &MainWindow::setProgressParseXML); + + workerXMLThread.start(); + workerBDThread.start(); + + fileList = NULL; +} + + +void MainWindow::customMenuRequested(QPoint pos) +{ + /* Создаем объект контекстного меню */ + QMenu * menu = new QMenu(this); + /* Создаём действия для контекстного меню */ + QAction * editDevice = new QAction(trUtf8("Редактировать"), this); + QAction * deleteDevice = new QAction(trUtf8("Удалить"), this); + /* Подключаем СЛОТы обработчики для действий контекстного меню */ + connect(editDevice, &QAction::triggered, this, &MainWindow::editRecord); // Обработчик вызова диалога редактирования + connect(deleteDevice, &QAction::triggered, this, &MainWindow::removeRecord); // Обработчик удаления записи + + /* Устанавливаем действия в меню */ + menu->addAction(editDevice); + menu->addAction(deleteDevice); + /* Вызываем контекстное меню */ + menu->popup(ui->tableView->viewport()->mapToGlobal(pos)); +} + +void MainWindow::removeRecord() +{ + // Выясняем, какая из строк была выбрана + int row = ui->tableView->selectionModel()->currentIndex().row(); + + // Проверяем, что строка была действительно выбрана + if(row >= 0) + { + tableModel->removeRow(row); + tableModel->select(); + ui->tableView->setCurrentIndex(tableModel->index(-1, -1)); + } +} + +void MainWindow::editRecord() +{ + DialogEditRecord *editDialog = new DialogEditRecord(ui->tableView->selectionModel()->currentIndex().row()); + connect(editDialog, &DialogEditRecord::readyToUpdate, this, &MainWindow::updateModel); + + editDialog->setWindowTitle(trUtf8("Редактировать запись")); + editDialog->exec(); +} + +void MainWindow::updateModel() +{ + tableModel->select(); + ui->tableView->resizeColumnsToContents(); +} + + +MainWindow::~MainWindow() +{ + workerXMLThread.quit(); + workerXMLThread.wait(); + + workerBDThread.quit(); + workerBDThread.wait(); + + delete workerXML; + delete ui; +} + +void MainWindow::on_loadButton_clicked() +{ + // QString fileName = QFileDialog::getOpenFileName(this, tr("Open xml file"), "./", tr("XML files (*.xml)")); + QString dirName = QFileDialog::getExistingDirectory(this, tr("Выбирите директорию с xml файлами"), + "./", + QFileDialog::ShowDirsOnly + | QFileDialog::DontResolveSymlinks); + if(dirName == "") + { + return; + } + + + QDir dir(dirName); + + dir.setFilter(QDir::NoDotAndDotDot | QDir::Files); + dir.setNameFilters(QStringList("*.xml")); + + fileList = new QFileInfoList (dir.entryInfoList()); + + if(fileList->size() == 0) + { + QMessageBox::warning(this, "Сообщение", "В выбранной директории нет xml файлов"); + return; + } + + progressDialog = new QProgressDialog("Parse file ...", QString(), 0, fileList->size(), this); + progressDialog->setMinimumDuration(0); + progressDialog->setWindowTitle("Пожалуйста подождите"); + progressDialog->setAutoClose(true); + + emit startParseXML(fileList, &arrayXml); +} + +void MainWindow::on_clearButton_clicked() +{ + qDebug () << "clear"; + tableModel->clear(); + + qDebug () << "reset"; + ui->tableView->reset(); + + emit cleanData(); +} + +void MainWindow::on_closeButton_clicked() +{ + this->close(); +} + + +void MainWindow::setProgressInsert(int value) +{ + QString mess = "Загрузка данных в БД"; + progressDialog->setValue(value); + progressDialog->setLabelText(mess); + + if(value == arrayXml.size()) + { + qDebug() << "!!! updateTableView"; + tableModel->setTable(TABLE); + tableModel->select(); + + // Устанавливаем размер колонок по содержимому + ui->tableView->resizeColumnsToContents(); + + } + + QApplication::instance()->processEvents(); +} + +void MainWindow::setProgressParseXML(int value, QString fileName, bool result) +{ + if(value == fileList->size()) + { + QString mess = "Загрузка данных в БД"; + + progressDialog->setLabelText(mess); + progressDialog->setValue(0); + progressDialog->setRange(0, arrayXml.size()); + + for(QMultiMap::iterator iii = arrayXml.begin(); iii != arrayXml.end(); iii++) + { + qDebug() << "key" << iii.key() << " value" << iii.value(); + } + + emit startInsert(&arrayXml); + + delete fileList; + } + else + { + QString mess = "Разбор файла " + fileName; + mess += (result)?" успешено":" ошибка"; + + progressDialog->setValue(value); + progressDialog->setLabelText(mess); + } + + QApplication::instance()->processEvents(); +} diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..75161de --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,57 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include + +#include "database.h" +#include "parserxmlworker.h" +#include "dialogeditrecord.h" + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + void on_loadButton_clicked(); + void on_clearButton_clicked(); + void on_closeButton_clicked(); + void setProgressInsert(int value); + void setProgressParseXML(int value, QString fileName, bool result); + void customMenuRequested(QPoint pos); + void removeRecord(); + void editRecord(); + void updateModel(); + +signals: + void startParseXML(QFileInfoList *fileList, QMultiMap *array); + void startInsert(QMultiMap *array); + void cleanData(); + +private: + DataBase *workerBD; + QSqlTableModel *tableModel; + + ParserXMLWorker *workerXML; + + Ui::MainWindow *ui; + QMultiMap arrayXml; + + QThread workerXMLThread; + QThread workerBDThread; + + QFileInfoList *fileList; + QProgressDialog *progressDialog; +}; + +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..aa6f6bd --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,90 @@ + + + MainWindow + + + + 0 + 0 + 902 + 702 + + + + Xml parser + + + + + + + + + + + + Загрузить + + + + + + + -3 + + + Qt::LeftToRight + + + Отчистить + + + + + + + Закрыть + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + 0 + 0 + 902 + 26 + + + + + + TopToolBarArea + + + false + + + + + + + + diff --git a/parserxmlworker.cpp b/parserxmlworker.cpp new file mode 100644 index 0000000..556e8e4 --- /dev/null +++ b/parserxmlworker.cpp @@ -0,0 +1,58 @@ +#include "parserxmlworker.h" + +#include +#include + +ParserXMLWorker::ParserXMLWorker() +{ +} + +void ParserXMLWorker::doParser(QFileInfoList *fileList, QMultiMap *arrayXml) +{ + + for(int iii = 0; iiisize(); iii++) + { + bool result = true; + + QString fileName(fileList->at(iii).absoluteFilePath()); + qDebug() << "Try work with file" << fileName; + + QFile file(fileName); + + if(file.open(QIODevice::ReadOnly)) + { + QXmlStreamReader sr(&file); + do + { + sr.readNext(); + if(sr.attributes().size() > 0) + { + for(auto jjj : sr.attributes()) + { + arrayXml->insert(jjj.name().toString(), jjj.value().toString()); + } + } + + } while(!sr.atEnd()); + + if(sr.hasError()) + { + qDebug() << "Error:" << sr.errorString(); + result = false; + } + file.close(); + } + else + { + qDebug() << "Error open file" << fileName; + result = false; + } + + emit changeProgressParseXML(iii, fileList->at(iii).fileName(), result); + + QThread::sleep(1); + } + + emit changeProgressParseXML(fileList->size(), QString(""), true); + +} diff --git a/parserxmlworker.h b/parserxmlworker.h new file mode 100644 index 0000000..64c401a --- /dev/null +++ b/parserxmlworker.h @@ -0,0 +1,23 @@ +#ifndef PARSERXMLWORKER_H +#define PARSERXMLWORKER_H + +#include +#include +#include +#include + +class ParserXMLWorker: public QObject +{ + Q_OBJECT + +public: + ParserXMLWorker(); + +public slots: + void doParser(QFileInfoList *fileList, QMultiMap *arrayXml); + +signals: + void changeProgressParseXML(int value, QString fileName, bool result); +}; + +#endif // PARSERXMLWORKER_H diff --git a/test_xml_dir/param1.xml b/test_xml_dir/param1.xml new file mode 100644 index 0000000..4c86001 --- /dev/null +++ b/test_xml_dir/param1.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/test_xml_dir/param2.xml b/test_xml_dir/param2.xml new file mode 100644 index 0000000..decf25c --- /dev/null +++ b/test_xml_dir/param2.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + diff --git a/test_xml_dir/param3.xml b/test_xml_dir/param3.xml new file mode 100644 index 0000000..6e0aec7 --- /dev/null +++ b/test_xml_dir/param3.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + +