diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,393 +1,401 @@ #include "exitcode.h" #include "mainwindow.h" #include "psettings.h" #include "updater.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) : QDialog(parent, flags) , _exitCode(0) , _errorCount(0) , _updater(new DatabaseUpdater) { setWindowTitle(QStringLiteral("Создание/обновление базы данных")); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setSizeGripEnabled(true); QHBoxLayout* mainLayout = new QHBoxLayout; QVBoxLayout* leftLayout = new QVBoxLayout; QVBoxLayout* rightLayout = new QVBoxLayout; QHBoxLayout* bottomRightLayout = new QHBoxLayout; leftLayout->setContentsMargins(0, 2 * style()->pixelMetric(QStyle::PM_LayoutTopMargin), style()->pixelMetric(QStyle::PM_LayoutRightMargin), 0); QLabel* picture = new QLabel; picture->setPixmap(QPixmap(":/picture.png")); //кнопки, скрытые до завершения процесса _btnSave = new QPushButton(QStringLiteral("Сохранить")); _btnClose = new QPushButton(QStringLiteral("Закрыть")); _btnSave->setVisible(false); _btnClose->setVisible(false); _btnClose->setDefault(true); leftLayout->addWidget(picture); leftLayout->addStretch(); leftLayout->addWidget(_btnSave); leftLayout->addWidget(_btnClose); QLabel* label = new QLabel(QStringLiteral("Протокол работы")); rightLayout->addWidget(label); _protocol = new QTextEdit(); _protocol->setReadOnly(true); label->setBuddy(_protocol); rightLayout->addWidget(_protocol); //индикаторы выполнения _progress = new QProgressBar; _progress->setTextVisible(false); _ai = new KActivityIndicator; _ai->setVisible(false); bottomRightLayout->addWidget(_progress); bottomRightLayout->addWidget(_ai); rightLayout->addLayout(bottomRightLayout); mainLayout->addLayout(leftLayout); mainLayout->addLayout(rightLayout); setLayout(mainLayout); //нажатие Esc не соединяется со слотом, чтобы окно не закрывалось new QShortcut(QKeySequence(Qt::Key_Escape), this); //соединить сигналы со слотами connect(_btnSave, SIGNAL(clicked()), this, SLOT(saveProtocol())); connect(_btnClose, SIGNAL(clicked()), this, SLOT(closeDialog())); QTimer::singleShot(0, this, SLOT(startExecution())); } MainWindow::~MainWindow() { delete _updater; } //--------------------------------------------------------------------------- void MainWindow::closeEvent(QCloseEvent* a_event) { if (_futureWatcher.isRunning()) a_event->ignore(); else a_event->accept(); } //--------------------------------------------------------------------------- void MainWindow::startExecution() { if (_futureWatcher.isRunning()) return; ProgramSettings pset = processArguments(); logon(pset); + _logFile = pset.logFile; connect(_updater, SIGNAL(progress(int)), _progress, SLOT(setValue(int))); connect(_updater, SIGNAL(error(const QString&)), this, SLOT(error(const QString&))); connect(_updater, SIGNAL(message(const QString&)), this, SLOT(message(const QString&))); connect(_updater, SIGNAL(sqlError(const QString&, const QString&, const QString&)), this, SLOT(sqlError(const QString&, const QString&, const QString&))); connect(_updater, SIGNAL(logConnectionParameters(const QString&, const QString&, const QString&)), this, SLOT(logConnectionParameters(const QString&, const QString&, const QString&))); connect(&_futureWatcher, SIGNAL(finished()), this, SLOT(finishExecution())); _ai->setVisible(true); _ai->start(); //int rc = _updater.run( pset ); QFuture rc = QtConcurrent::run(_updater, &DatabaseUpdater::run, pset); _futureWatcher.setFuture(rc); } //--------------------------------------------------------------------------- void MainWindow::finishExecution() { _ai->stop(); _ai->setVisible(false); //проверяем число ошибок, т.к. при выполнении без транзакции run() всегда возвращает 0 if (_errorCount == 0) { ExitCode rc(_updater->revisionBefore(), _updater->revisionAfter()); message(rc.message()); _exitCode = rc.exitCode(); closeDialog(); //при успешном завершении, то окно закрывается автоматически } else { _btnClose->show(); _btnSave->show(); _btnClose->setFocus(); message(QStringLiteral("Выполнение прервано в результате ошибки.\nБаза данных не изменилась.")); _exitCode = -1; } + + if (!_logFile.isEmpty()) + saveProtocol(_logFile); } //--------------------------------------------------------------------------- int MainWindow::executeAction() { ProgramSettings pset = processArguments(); logon(pset); _ai->start(); DatabaseUpdater updater; connect(&updater, SIGNAL(progress(int)), _progress, SLOT(setValue(int))); connect(&updater, SIGNAL(error(const QString&)), this, SLOT(error(const QString&))); connect(&updater, SIGNAL(message(const QString&)), this, SLOT(message(const QString&))); connect(&updater, SIGNAL(sqlError(const QString&, const QString&, const QString&)), this, SLOT(sqlError(const QString&, const QString&, const QString&))); connect(&updater, SIGNAL(logConnectionParameters(const QString&, const QString&, const QString&)), this, SLOT(logConnectionParameters(const QString&, const QString&, const QString&))); int rc = updater.run(pset); //проверяем число ошибок, т.к. при выполнении без транзакции run() всегда возвращает 0 if (_errorCount == 0) { int before = updater.revisionBefore(); int after = updater.revisionAfter(); if (before == after) message(QStringLiteral("База данных в актуальном состоянии.")); else if (before == 0) message(QStringLiteral("База данных создана и обновлена до версии %1.").arg(after)); else message(QStringLiteral("База данных версии %1 обновлена до версии %2.").arg(before).arg(after)); rc = (before & 0xFFFF) | ((after & 0xFFFF) << 16); } else { _btnClose->show(); _btnSave->show(); _btnClose->setFocus(); message(QStringLiteral("Выполнение прервано в результате ошибки." "
База данных не изменилась.")); rc = -1; } + + return rc; } //--------------------------------------------------------------------------- void MainWindow::logon(ProgramSettings& a_pset) { if (!a_pset.host.isEmpty() || !a_pset.username.isEmpty()) return; KLogon* logon = KLogon::create(); if (!logon) return; QString defaultDSN; QString ospoName; HKEY k; DWORD cb = 0; QString valuename("System Directory"); if ((RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\irs\\b04"), 0, KEY_QUERY_VALUE, &k) == ERROR_SUCCESS || RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\irs\\b03"), 0, KEY_QUERY_VALUE, &k) == ERROR_SUCCESS) && RegQueryValueEx(k, LPCTSTR(valuename.constData()), 0, 0, 0, &cb) == ERROR_SUCCESS && cb > 0) { QString systemdir; systemdir.resize((cb / sizeof(QChar)) - 1); RegQueryValueEx(k, LPCTSTR(valuename.constData()), 0, 0, LPBYTE(systemdir.data()), &cb); RegCloseKey(k); KSettings systemini(systemdir + "/system.ini"); systemini.beginGroup("Database"); defaultDSN = systemini.value("DefaultDSN").toString(); systemini.endGroup(); systemini.beginGroup("Common"); ospoName = systemini.value("SystemName").toString().replace(QLatin1Char('B'),QStringLiteral("ОСПО Б-")); } QString username = QString::fromLocal8Bit(qgetenv("USERNAME")); QHash aliases = loadAliases(ospoName); if (aliases.contains(username)) username = aliases[username]; logon->setParent(winId()); logon->setUsername(username); if (!defaultDSN.isNull()) logon->setDSN(defaultDSN); if (!logon->execute()) emit message(QStringLiteral("Отказ от ввода имени и пароля пользователя")); else { a_pset.host = logon->host(); a_pset.username = logon->username(); a_pset.password = logon->password(); } delete logon; } //--------------------------------------------------------------------------- QHash MainWindow::loadAliases(const QString& a_ospoName) { QHash result; QApplication::setOrganizationName(a_ospoName); QString filename = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).at(1); filename.replace(QApplication::applicationName(), QStringLiteral("aliases.conf")); QFile inf(filename); if (inf.open(QIODevice::ReadOnly)) { QTextStream is(&inf); is.setCodec(QTextCodec::codecForName("UTF-8")); while ( !is.atEnd() ) { QStringList chunks = is.readLine().split(QLatin1Char(':')); if (chunks.size() == 2) result[chunks.at(0).trimmed()] = chunks.at(1).trimmed(); } } return result; } //--------------------------------------------------------------------------- void MainWindow::logConnectionParameters(const QString& a_host, const QString& a_database, const QString& a_username) { message(QStringLiteral("Подключение к базе данных\n" "\tСервер: **%1**\n\tБаза данных: **%2**\n" "\tПользователь: **%3**") .arg(a_host, a_database, a_username)); } //--------------------------------------------------------------------------- void MainWindow::sqlError(const QString& a_dbError, const QString& a_commandDescription, const QString& a_command) { QString errorHtml; if (!a_commandDescription.isEmpty()) errorHtml.append(QStringLiteral("Операция:
")).append(a_commandDescription).append("
"); QString dbError = a_dbError; errorHtml.append(QStringLiteral("Сообщение об ошибке:
")).append(dbError.trimmed().replace('\n', "
")).append("
"); if (!a_command.isEmpty()) errorHtml.append(QStringLiteral("Текст команды:
")).append(a_command).append("
"); errorHtmlToLog(errorHtml); } //--------------------------------------------------------------------------- void MainWindow::error(const QString& a_error) { QString errorHtml = a_error; errorHtmlToLog(errorHtml.replace('<', "<").replace('>', ">").replace('\n', "
")); } //--------------------------------------------------------------------------- void MainWindow::errorHtmlToLog(const QString& a_errorHtml) { QString errorHtml = a_errorHtml; QTextCursor cursor = _protocol->textCursor(); cursor.insertHtml(QStringLiteral("Ошибка
")); cursor.insertHtml(errorHtml); cursor.insertHtml(QStringLiteral("
")); _protocol->moveCursor(QTextCursor::End); ++_errorCount; } QString MainWindow::toHtml(const QString& a_text) { QString result = a_text; static const QString BOLD = QStringLiteral("**"); static const QString HTML_BOLD_BEGIN = QStringLiteral(""); static const QString HTML_BOLD_END = QStringLiteral(""); static const QString HTML_BR = QStringLiteral("
"); static const QString HTML_NBSP = QStringLiteral("  "); static const QChar NL('\n'); static const QChar TAB('\t'); bool begin = true; int pos = result.indexOf(BOLD); while (pos >= 0) { result.replace(pos, BOLD.size(), begin ? HTML_BOLD_BEGIN : HTML_BOLD_END); begin = !begin; pos = result.indexOf(BOLD, pos + 1); } if (result.contains(NL)) result.replace(NL, HTML_BR); if (result.contains(TAB)) result.replace(TAB, HTML_NBSP); return result; } //--------------------------------------------------------------------------- void MainWindow::message(const QString& a_message) { QString message = a_message; _protocol->textCursor().insertHtml(toHtml(message).append(QStringLiteral("
"))); _protocol->moveCursor(QTextCursor::End); } //--------------------------------------------------------------------------- void MainWindow::closeDialog() { QDialog::done(_exitCode); } //--------------------------------------------------------------------------- -void MainWindow::saveProtocol() +void MainWindow::saveProtocol(const QString& a_fileName) { - QString filename = QFileDialog::getSaveFileName(this, - QStringLiteral("Сохранить протокол в файл…"), QString(), - QStringLiteral("Текстовые файлы (*.txt);;Все файлы (*.*)")); + QString filename = a_fileName; + if (filename.isEmpty()) + filename = QFileDialog::getSaveFileName(this, + QStringLiteral("Сохранить протокол в файл…"), QString(), + QStringLiteral("Текстовые файлы (*.txt);;Все файлы (*.*)")); if (!filename.isEmpty()) { QFile outf(filename); if (outf.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QTextStream os(&outf); os.setGenerateByteOrderMark(true); os.setCodec("UTF-8"); os << _protocol->toPlainText(); } } } //!главное окно приложения diff --git a/src/mainwindow.h b/src/mainwindow.h --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -1,63 +1,64 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include class QLabel; class QListWidget; class QPushButton; class QProgressBar; class QTextEdit; class ProgramSettings; class SqlErrorDialog; class SqlProcessor; class DatabaseUpdater; class ProgramSettings; class KActivityIndicator; class MainWindow : public QDialog { Q_OBJECT public: MainWindow(QWidget* parent = 0, Qt::WindowFlags flags = 0); ~MainWindow() override; private slots: - void saveProtocol(); + void saveProtocol(const QString& a_fileName = QString()); void closeDialog(); void startExecution(); void finishExecution(); void error(const QString& a_error); void message(const QString& a_message); void logConnectionParameters(const QString& a_host, const QString& a_database, const QString& a_username); void sqlError(const QString& a_dbError, const QString& a_commandDescription, const QString& a_command); protected: void closeEvent(QCloseEvent* a_event) override; private: int executeAction(); void logon(ProgramSettings& a_pset); void errorHtmlToLog(const QString& a_errorHtml); static QString toHtml(const QString& a_text); static QHash loadAliases(const QString &a_ospoName); int _exitCode; int _errorCount; + QString _logFile; QTextEdit* _protocol; QLabel* _lblWait; QPushButton* _btnSave; QPushButton* _btnClose; KActivityIndicator* _ai; QProgressBar* _progress; QFutureWatcher _futureWatcher; DatabaseUpdater* _updater; }; //!главное окно приложения #endif //MAINWINDOW_H diff --git a/src/psettings.cpp b/src/psettings.cpp --- a/src/psettings.cpp +++ b/src/psettings.cpp @@ -1,71 +1,73 @@ #include "psettings.h" #include ProgramSettings processArguments() { ProgramSettings result; QString USERNAME_VALUE = QStringLiteral("пользователь"); QString PASSWORD_VALUE = QStringLiteral("пароль"); QString DATABASE_VALUE = QStringLiteral("база_данных"); QString HOST_VALUE = QStringLiteral("адрес_сервера"); QString PORT_VALUE = QStringLiteral("номер_порта"); QString FILE_VALUE = QStringLiteral("file.dmv"); QString LOGFILE_VALUE = QStringLiteral("файл_журнала"); QString USERNAME_OPTION = QStringLiteral("U"); QString PASSWORD_OPTION = QStringLiteral("W"); QString DATABASE_OPTION = QStringLiteral("d"); QString DROPDB_OPTION = QStringLiteral("0"); QString HOST_OPTION = QStringLiteral("h"); QString PORT_OPTION = QStringLiteral("p"); QString FILE_OPTION = QStringLiteral("f"); QString LOGFILE_OPTION = QStringLiteral("L"); QString URIDB_OPTION = QStringLiteral("u"); QString HELP_OPTION = QStringLiteral("help"); //обработать аргументы командной строки QCommandLineParser parser; QCommandLineOption helpOption(HELP_OPTION, QStringLiteral("Показать справку") ); parser.addOption(helpOption); QCommandLineOption usernameOption(USERNAME_OPTION, QStringLiteral("Имя пользователя"), USERNAME_VALUE); parser.addOption(usernameOption); QCommandLineOption databaseOption(DATABASE_OPTION, QStringLiteral("Имя базы данных"), DATABASE_VALUE); parser.addOption(databaseOption); QCommandLineOption filenameOption(FILE_OPTION, QStringLiteral("Управляющий файл"), FILE_VALUE); parser.addOption(filenameOption); QCommandLineOption hostOption(HOST_OPTION, QStringLiteral("Адрес сервера"), HOST_VALUE); parser.addOption(hostOption); QCommandLineOption portOption(PORT_OPTION, QStringLiteral("Порт сервера"), PORT_VALUE); parser.addOption(portOption); QCommandLineOption passwordOption(PASSWORD_OPTION, QStringLiteral("Пароль"), PASSWORD_VALUE); parser.addOption(passwordOption); - QCommandLineOption logfileOption(LOGFILE_OPTION, QStringLiteral("Сохранить протокол работы в файл"), LOGFILE_VALUE); - parser.addOption(logfileOption); + QCommandLineOption logFileOption(LOGFILE_OPTION, QStringLiteral("Сохранить протокол работы в файл"), LOGFILE_VALUE); + parser.addOption(logFileOption); QCommandLineOption dropdbOption(DROPDB_OPTION, QStringLiteral("Сначала удалить базу данных")); parser.addOption(dropdbOption); QCommandLineOption uridbOption(URIDB_OPTION, QStringLiteral("Не создавать учебную базу данных")); parser.addOption(uridbOption); parser.addPositionalArgument(FILE_VALUE, QStringLiteral("Управляющий файл"), FILE_VALUE); parser.process(*qApp); //записать прочитанные значения в структуру ProgramSettings result.username = parser.value(usernameOption); result.password = parser.value(passwordOption); result.database = parser.value(databaseOption); result.host = parser.value(hostOption); result.port = parser.value(portOption); - result.logfile = parser.value(logfileOption); + result.logFile = parser.value(logFileOption); result.dropdb = parser.isSet(dropdbOption); result.uridb = !parser.isSet(uridbOption); if (parser.isSet(filenameOption)) result.controlFile = parser.value(filenameOption); else if (parser.positionalArguments().size() > 0) result.controlFile = parser.positionalArguments().at(0); + if (parser.isSet(logFileOption)) + result.logFile = parser.value(logFileOption); if (parser.isSet(helpOption)) result.helpText = parser.helpText(); return result; } diff --git a/src/psettings.h b/src/psettings.h --- a/src/psettings.h +++ b/src/psettings.h @@ -1,26 +1,26 @@ #if !defined PSETTINGS_H #define PSETTINGS_H #include class ProgramSettings { public: ProgramSettings() : dbVersion(0), dropdb(false), uridb(true) {} QString username; QString password; QString database; QString host; QString port; QString controlFile; - QString logfile; + QString logFile; QString packageId; QString helpText; int dbVersion; bool dropdb; bool uridb; }; ProgramSettings processArguments(); #endif