/
MaximU
/
application
Обзор
Документация
Войти
/
MaximU
/
application
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
mainwindow.cpp
2 085 строк
69 KB
user
Добавлена заготовка меню удаления истории.
30 июл 2026, 08:55
30 июл 2026, 08:55
fde8b31
Код
Авторство
О чём код?
#include "mainwindow.h" #include <QCloseEvent> #include <QCommandLineParser> #include <QDateTime> #include <QDir> #include <QHeaderView> #include <QMenu> #include <QMessageBox> #include <QMultiMap> #include <QScrollBar> #include <QSettings> #include <QTextStream> #include <typeinfo> #include "QtEnumEditorFactory" #include "addressinputdialog.h" #include "application.h" #include "applicationspecifics.h" #include "connectionaddresswidget.h" #include "connectionstatewidget.h" #include "lockingqueue.h" #include "qtvariantproperty.h" #include "savepropertiesvaluesdialog.h" #include "ui_mainwindow.h" const QString HistoryDir = "History"; //!< Имя каталога, хранящего файлы истории операций. const QString HistoryFilenameFormat = "yyyy-MM-dd"; //!< Формат имени файла истории операций для форматирования QDateTime.toStirng(...). extern bool gWork; //!< Признак выполнения обработки. extern QList<ReplayWidget *> gReplayWidgetsList; //!< Список виджетов записи/воспроизведения. extern ProfilerInfo gProfilerInfo; //!< Информация о выполнении алгоритмических блоков. extern LockingQueue<std::shared_ptr<char[]>> gInputPool; //!< Пул массивов входной информации. Используется для сокращения операций выделения памяти под массивы входной информации. extern LockingQueue<std::shared_ptr<char[]>> gPendingQueue; //!< Очередь заполненных массивов входной информации. extern LockingQueue<std::shared_ptr<char[]>> gInputQueue; //!< Очередь массивов входной информации, ожидающих обработки. /*! * \brief Конструктор. */ MainWindow::MainWindow() : QMainWindow(), ui(new Ui::MainWindowClass) { ui->setupUi(this); setWindowTitle(App->applicationName()); // Установка заголовка приложения. connect(App, SIGNAL(ui()), this, SLOT(updateUi())); connect(App, SIGNAL(log(const QString &, bool)), this, SLOT(logString(const QString &, bool))); ui->tabWidget->setCornerWidget(ui->toolbuttonToggleEmbedded, Qt::TopLeftCorner); ui->toolbuttonToggleEmbedded->setVisible(false); // Если будет добавлено более одного встраиваемого виджета, то кнопка будет видима. ui->toolbuttonOptions->setVisible(false); // Если будут добавлены свойства properties с значениями по умолчанию, то кнопка будет видима. addAction(ui->actionVersion); // Для Ctrl+V. addAction(ui->actionReplay); // Для отображения панели записи/воспроизведения. addAction(ui->actionWork); ui->toolbuttonWork->setDefaultAction(ui->actionWork); ui->statusBar->addWidget(ui->labelWork); addAction(ui->actionDelete); ui->toolbuttonDelete->setDefaultAction(ui->actionDelete); //// Виджет отображения времени. ui->statusBar->addPermanentWidget(ui->frameClock); connect(&mTimerClock, &QTimer::timeout, this, &MainWindow::slot_timerClock_timeout); mTimerClock.start(1000); } /*! * \brief Деструктор. */ MainWindow::~MainWindow() { delete ui; } /*! * \brief Инициализирует главное окно приложения. */ void MainWindow::initMainWindow() { connect(App, &Application::messageReceived, this, &MainWindow::raise); connect(App, &Application::updateConnectionsTable, this, &MainWindow::updateConnectionsTable); connect(App, &Application::iterationDone, this, &MainWindow::iterationDone); connect(App, &Application::updateProfilerTable, this, &MainWindow::updateProfilerTable); connect(App, &Application::taskProgressBegin, this, &MainWindow::slot_taskProgressBegin); connect(App, &Application::taskProgressStep, this, &MainWindow::slot_taskProgressStep); connect(App, &Application::taskProgressEnd, this, &MainWindow::slot_taskProgressEnd); // Создание виджетов записи/воспроизведения. createReplays(); // Создание дерева свойств QtProperty программы. createProperties(); // Создание дополнительных виджетов. createWidgets(); // Создание таблицы профайлера. createProfilerTable(); // Восстановление настроек из ini-файла. readSettings(); // Создание меню установки/удаления наборов значений свойств properties. readStoredProperiesValues(); // Создание таблицы соединений. createConnectionsTable(); // Обновление таблицы профайлера на случай выполнения обработки данных, загруженных при старте программы. updateProfilerTable(); // Обновление Ui. updateUi(); //// Разбор командной строки. QCommandLineParser parser; parser.addOption(QCommandLineOption({"i", "input"}, tr("Имя файла для воспроизведения"), "input")); parser.addOption(QCommandLineOption({"r", "rate"}, tr("Темп воспроизведения [мс]"), "rate")); parser.addOption(QCommandLineOption({"q", "quit"}, tr("Закрыть приложение по завершении воспроизведения"), "quit")); parser.addOption(QCommandLineOption({"o", "output"}, tr("Типы массивов записываемой выходной информации (через запятую, без пробелов)"), "output")); parser.parse(App->arguments()); logString(QCoreApplication::arguments().join(" ")); QStringList unparsedOptions = parser.unknownOptionNames(); if (!unparsedOptions.isEmpty()) { if (unparsedOptions.count() == 1) { logString(tr(" Неизвестный аргумент командной строки: ") + unparsedOptions.first(), false); } else { logString(tr(" Неизвестные аргументы командной строки:"), false); for (auto option : unparsedOptions) { logString(" " + option, false); } } } if (parser.isSet("input")) { QString fileName = parser.value("input"); ReplayWidget *replayWidget = nullptr; Replay::Header header; if (Replay::getHeader(fileName, header)) { for (auto replay : gReplayWidgetsList) { if (replay->signature() == header.signature && replay->version() == header.version) { replayWidget = replay; break; } } if (replayWidget != nullptr) { int rate = -1; if (parser.isSet("rate")) { bool ok; int value = parser.value("rate").toInt(&ok); if (ok) { rate = value; } } if (parser.isSet("quit")) { mQuitOnEndRead = true; } replayWidget->openPlayFile(fileName, true, rate); replayWidget->show(); } else { QMessageBox::information(this, tr("Воспроизведение"), tr("Неизвестный формат файла!")); } } else { QMessageBox::information(this, tr("Воспроизведение"), tr("Ошибка чтения файла!")); } if (parser.isSet("output")) { QString text = parser.value("output"); QStringList typesStrings = text.split(","); for (auto typeString : typesStrings) { bool ok; int type = typeString.toInt(&ok); if (ok) { for (auto replay : gReplayWidgetsList) { if (replay->types().contains(type)) { if (!replay->isRecording()) { replay->openRecordFile(fileName + "." + replay->extension(), true); replay->show(); } } } } } } } // Журнал. beginLog(); } /*! * \brief Вызывается при попытке закрытия программы. Запрашивает разрешение на закрытие программы. * \param event Объект события. */ void MainWindow::closeEvent(QCloseEvent *event) { QMessageBox messageBox(QMessageBox::Question, windowTitle(), tr("Выход?"), QMessageBox::Yes | QMessageBox::No, this); messageBox.setButtonText(QMessageBox::Yes, tr("Да")); messageBox.setButtonText(QMessageBox::No, tr("Нет")); if (messageBox.exec() == QMessageBox::Yes) { event->accept(); } else { event->ignore(); } if (event->isAccepted()) { // Сохранение настроек в ini-файл. writeSettings(); // Журнал. endLog(); // Удаление встраиваемых окон. deleteWidgets(); // Удаление дерева свойств (параметров) программы. deleteProperties(); // Удаление виджетов записи/воспроизведения. deleteReplays(); // Принудительное закрытие приложения необходимо потому что в App создаются ReplayWidget-ы с parent = 0. App->quit(); } } /*! * \brief Восстанавливает настройки приложения из ini-файла. */ void MainWindow::readSettings() { QSettings settings1(App->applicationCfgPath(), QSettings::IniFormat); //// Свойства. settings1.beginGroup("Properties"); for (QString key : mPropertiesMap.keys()) { QtProperty *property = mPropertiesMap[key]; if (mPropertyManager->properties().contains(property)) { QVariant value = settings1.value(key, mPropertyManager->value(property)); mPropertyManager->setValue(property, value); } if (mEnumPropertyManager->properties().contains(property)) { QVariant value = settings1.value(key, mEnumPropertyManager->value(property)); mEnumPropertyManager->setValue(property, value.toInt()); } } ui->toolbuttonApplyChange->setEnabled(false); ui->toolbuttonCancelChange->setEnabled(false); applyPropertiesValues(mChangedPropertiesList); resetPropertiesHighlighting(mChangedPropertiesList); mChangedPropertiesList.clear(); settings1.endGroup(); QSettings settings2(QString("%1/%2").arg(qApp->applicationDirPath()).arg("settings.ini"), QSettings::IniFormat); //// Главное окно settings2.beginGroup("MainWindow"); restoreGeometry(settings2.value("geometry").toByteArray()); restoreState(settings2.value("bars").toByteArray()); ui->tabWidget->setCurrentIndex(settings2.value("widgetIndex", ui->tabWidget->currentIndex()).toInt()); settings2.endGroup(); ui->toolbarReplay->setVisible(false); // Кнопки записи должны быть невидимы при запуске программы. ui->dockwidgetProfiler->setVisible(false); // Панель профайлера должна быть невидима при запуске программы. ui->actionSettings->setChecked(ui->dockwidgetSettings->isVisible()); ui->actionHistory->setChecked(ui->dockwidgetHistory->isVisible()); ui->actionConnections->setChecked(ui->dockwidgetConnections->isVisible()); //// Виджеты записи/воспроизведения. settings2.beginGroup("Replays"); for (ReplayWidget *replay : gReplayWidgetsList) { replay->readSettings(settings2); } settings2.endGroup(); //// Встраиваемые виджеты. settings2.beginGroup("Embeddables"); for (EmbeddableWidget *widget : mEmbeddableWidgets) { settings2.beginGroup(widget->label()); widget->readSettings1(settings2); settings2.endGroup(); } settings2.endGroup(); //// readSettingsSpecific(settings2); } /*! * \brief Сохраняет настройки приложения в ini-файле. */ void MainWindow::writeSettings() { QSettings settings1(App->applicationCfgPath(), QSettings::IniFormat); //// Свойства. // Сброс значений непримененных свойств. setPropertiesValues(mChangedPropertiesList); settings1.beginGroup("Properties"); for (QString key : mPropertiesMap.keys()) { QtProperty *property = mPropertiesMap[key]; if (mPropertyManager->properties().contains(property) && mPropertyManager->propertyType(property) != QtVariantPropertyManager::groupTypeId()) { settings1.setValue(key, mPropertyManager->value(property)); } if (mEnumPropertyManager->properties().contains(property)) { settings1.setValue(key, mEnumPropertyManager->value(property)); } } settings1.endGroup(); QSettings settings2(QString("%1/%2").arg(qApp->applicationDirPath()).arg("settings.ini"), QSettings::IniFormat); //// Виджеты записи/воспроизведения. settings2.beginGroup("Replays"); for (ReplayWidget *replay : gReplayWidgetsList) { replay->writeSettings(settings2); } settings2.endGroup(); //// Встраиваемые виджеты. settings2.beginGroup("Embeddables"); for (EmbeddableWidget *widget : mEmbeddableWidgets) { settings2.beginGroup(widget->label()); widget->writeSettings1(settings2); settings2.endGroup(); } settings2.endGroup(); //// Главное окно settings2.beginGroup("MainWindow"); settings2.setValue("widgetIndex", ui->tabWidget->currentIndex()); settings2.setValue("bars", saveState()); settings2.setValue("geometry", saveGeometry()); settings2.endGroup(); //// writeSettingsSpecific(settings2); } /*! * \brief Начинает журнал. */ void MainWindow::beginLog() { logString(tr("Начало работы")); QList<QtProperty *> properties = ui->treePropertyBrowser->properties(); if (!properties.isEmpty()) { logString(tr(" Начальные значения настроек:"), false); logPropertiesValues(properties, " "); } beginLogSpecific(); } /*! * \brief Завершает журнал. */ void MainWindow::endLog() { endLogSpecific(); logString(tr("Завершение работы")); } /*! * \brief Сохраняет свойства QtProperties в ini-файле. */ void MainWindow::writeProperties() { QSettings settings(QString("%1/%2").arg(qApp->applicationDirPath()).arg("settings.ini"), QSettings::IniFormat); //// Свойства. settings.beginGroup("Properties"); for (QString key : mPropertiesMap.keys()) { QtProperty *property = mPropertiesMap[key]; if (mPropertyManager->properties().contains(property)) { settings.setValue(key, mPropertyManager->value(property)); } if (mEnumPropertyManager->properties().contains(property)) { settings.setValue(key, mEnumPropertyManager->value(property)); } } settings.endGroup(); } /*! * \brief Обновляет индикатор текущего времени в строке состояния. Вызывается таймером. */ void MainWindow::slot_timerClock_timeout() { QDateTime dt = QDateTime::currentDateTime(); ui->labelDate->setText(dt.toString("dd.MM.yyyy")); ui->labelTime->setText(dt.toString("hh:mm:ss")); } /*! * \brief Вызывается при нажатии кнопки встраивания/вынесения встраиваемого окна. */ void MainWindow::on_toolbuttonToggleEmbedded_clicked() { EmbeddableWidget *widget = mEmbeddableWidgets.at(ui->tabWidget->currentIndex()); widget->setEmbedded(!widget->isEmbedded()); } /*! * \brief Вызывается встраиваемым окном при необходимости встройки/вынесения. Само окно не производит встройку/несение. * \param embed Признак встроить/вынести. */ void MainWindow::slot_embeddableWidget_embedMe(bool embed) { EmbeddableWidget *widget = qobject_cast<EmbeddableWidget *>(sender()); int position = mEmbeddableWidgets.indexOf(widget); if (embed) { ui->tabWidget->widget(position)->layout()->addWidget(widget); } else { ui->tabWidget->widget(position)->layout()->removeWidget(widget); widget->setParent(this); widget->setWindowFlags(Qt::Window); } } /*! * \brief Отображает диалог изменения адреса и порта соединения. * \param id Идентификатор соединения. */ void MainWindow::slot_widgetConnectionAddress_change(int id) { Connection *connection = App->connectionById(id); if (connection != nullptr) { AddressInputDialog *dialog = new AddressInputDialog(this, connection->name()); dialog->setAddress(connection->address()); dialog->setPort(connection->port()); if (dialog->exec() == QDialog::Accepted) { int port = dialog->port(); QString address = dialog->address(); connection->close(); connection->setAddress(address); connection->setPort(port); connection->open(); updateConnectionsTable(Connection::EventUpdate, connection->id()); } delete dialog; } } /*! * \brief Сохраняет текущие значения свойств properties. */ void MainWindow::on_actionSavePropertiesValues_triggered() { SavePropertiesValuesDialog *dialog = new SavePropertiesValuesDialog(this); connect(dialog, SIGNAL(accepted()), this, SLOT(slot_storePropertiesValuesDialog_accepted())); dialog->show(); } /*! * \brief Возвращает значения свойств properties к исходным значениям. */ void MainWindow::on_actionRestoreDefaults_triggered() { for (QtProperty *property : mPropertyDefaultValuesMap.keys()) { QVariant value = mPropertyDefaultValuesMap[property]; if (mPropertyManager->properties().contains(property)) { if (!value.isNull()) { mPropertyManager->setValue(property, value); } } if (mEnumPropertyManager->properties().contains(property)) { mEnumPropertyManager->setValue(property, value.toInt()); } } } /*! * \brief Отображает/скрывает диалог Записи/Воспроизведения. */ void MainWindow::on_actionReplay_triggered() { if (!gReplayWidgetsList.isEmpty()) { if (gReplayWidgetsList.size() == 1) { // Если только один менеджер, то отображается сразу. gReplayWidgetsList.first()->setVisible(!gReplayWidgetsList.first()->isVisible()); } else { // Если менеджеров несколько, то отображается тулбар выбора. ui->toolbarReplay->setVisible(!ui->toolbarReplay->isVisible()); } } } /*! * \brief Отображает/скрывает окно профайлера. */ void MainWindow::on_actionProfiler_triggered() { ui->dockwidgetProfiler->setVisible(!ui->dockwidgetProfiler->isVisible()); } /*! * \brief Устанавливает признак обработки в true и скрывает кнопку отключения обработки. */ void MainWindow::setAlwaysWork() { gWork = true; ui->toolbuttonWork->hide(); ui->actionWork->setEnabled(false); } /*! * \brief Скрывает кнопку очистки данных. */ void MainWindow::setCantDelete() { ui->toolbuttonDelete->hide(); ui->actionDelete->setEnabled(false); } /*! * \brief Создает виджеты записи/воспроизведения. */ void MainWindow::createReplays() { createReplaysSpecific(); //// Создание QAction. for (ReplayWidget *replay : gReplayWidgetsList) { QAction *action = new QAction(this); action->setCheckable(true); action->setIcon(replay->windowIcon()); action->setIconText(replay->description()); action->setToolTip(replay->windowTitle()); action->setData(QVariant::fromValue(replay)); connect(action, SIGNAL(triggered(bool)), replay, SLOT(setVisible(bool))); connect(replay, SIGNAL(visibilityChanged(bool)), action, SLOT(setChecked(bool))); ui->toolbarReplay->addAction(action); } } /*! * \brief Удаляет виджеты записи/воспроизведения. */ void MainWindow::deleteReplays() { for (auto replayWidget : gReplayWidgetsList) { delete replayWidget; } gReplayWidgetsList.clear(); } /*! * \brief Добавляет виджет записи/воспроизведения. * \param name Название виджета. * \param extension Расширение файла. * \param signature Сигнатура файла. * \param version Версия файла. * \param types Список типов массивов. * \param mode Режим работы (чтение/запись). * \return Указатель на созданный ReplayWidget. */ ReplayWidget * MainWindow::addReplay(const QString &name, const QString &extension, const QString &signature, int version, const QList<int> &types, QIODevice::OpenMode mode) { ReplayWidget *replay = new ReplayWidget(this, signature, version, name, extension, types, mode); connect(replay, SIGNAL(endReadPortion()), qApp, SLOT(slot_replay_endReadPortion())); connect(replay, SIGNAL(beginRead(const QString &)), this, SLOT(slot_replayWidget_beginRead(const QString &))); connect(replay, SIGNAL(endRead(const QString &)), this, SLOT(slot_replayWidget_endRead(const QString &))); connect(replay, SIGNAL(beginWrite(const QString &)), this, SLOT(slot_replayWidget_beginWrite(const QString &))); connect(replay, SIGNAL(endWrite(const QString &)), this, SLOT(slot_replayWidget_endWrite(const QString &))); gReplayWidgetsList.append(replay); return replay; } /*! * \brief Создает дерево свойств QtProperties программы. */ void MainWindow::createProperties() { //// Кнопка удаления данных ч меню удаления истории. if (!App->histories().isEmpty()) { QMenu *menu = new QMenu(this); for (auto history : App->histories()) { menu->addAction(tr("Удалить историю ") + history->name(), [history]() { }); } ui->toolbuttonDelete->setMenu(menu); } //// mPropertyManager = new QtVariantPropertyManager(); mEnumPropertyManager = new QtEnumPropertyManager(); QtVariantEditorFactory *variantFactory = new QtVariantEditorFactory(); QtEnumEditorFactory *enumFactory = new QtEnumEditorFactory(); ui->treePropertyBrowser->setHeaderVisible(false); ui->treePropertyBrowser->setFactoryForManager(mPropertyManager, variantFactory); ui->treePropertyBrowser->setFactoryForManager(mEnumPropertyManager, enumFactory); //// createPropertyBindingsSpecific(); createPropertiesSpecific(); connect(App, SIGNAL(setPropertyValue(const QString &, const QVariant &, bool, bool &)), this, SLOT(setPropertyValue(const QString &, const QVariant &, bool, bool &))); //// connect(mPropertyManager, SIGNAL(propertyChanged(QtProperty *)), this, SLOT(slot_propertyChanged(QtProperty *))); connect(mEnumPropertyManager, SIGNAL(propertyChanged(QtProperty *)), this, SLOT(slot_propertyChanged(QtProperty *))); setPropertiesValues(mPropertiesMap.values()); // Обеспечивает вызов propertyChanged для обновления properties, зависящих от других properties. //// Если настроек property в программе не предусмотрено, то докбар и кнопка "Настройки" скрыты. if (mPropertiesMap.isEmpty()) { ui->actionSettings->setVisible(false); ui->actionSettings->setEnabled(false); ui->dockwidgetSettings->setVisible(false); } } /*! * \brief Удаляет дерево свойств QtProperties программы. */ void MainWindow::deleteProperties() { delete mPropertyManager; delete mEnumPropertyManager; } /*! * \brief Добавляет группу QtProperty. * \param name Название. * \param tooltip Описание. * \param label Метка. * \param root Корневой экземпляр QtProperty. nullptr если QtProperty добавляется в верхний уровень. * \return Созданный экземпляр QtProperty. */ QtProperty * MainWindow::addPropertyGroup(const QString &name, const QString &tooltip, const QString &label, QtProperty *root) { QtProperty *property = mPropertyManager->addProperty(QtVariantPropertyManager::groupTypeId(), name); property->setToolTip(tooltip); if (root == nullptr) { ui->treePropertyBrowser->addProperty(property); } else { root->addSubProperty(property); } if (label != QString()) { mPropertiesMap.insert(label, property); } return property; } /*! * \brief Добавляет QtProperty. * \param type Тип значения. * \param name Название. * \param tooltip Описание. * \param label Метка. * \param root Корневой экземпляр QtProperty. nullptr если QtProperty добавляется в верхний уровень. * \param initialValue Исходное значение. * \return Созданный экземпляр QtProperty. */ QtProperty * MainWindow::addProperty(int type, const QString &name, const QString &tooltip, const QString &label, QtProperty *root, QVariant initialValue) { QtProperty *property = mPropertyManager->addProperty(type, name); mPropertyManager->setValue(property, initialValue); property->setToolTip(tooltip); if (root == nullptr) { ui->treePropertyBrowser->addProperty(property); } else { root->addSubProperty(property); } if (label != QString()) { mPropertiesMap.insert(label, property); } if (!initialValue.isNull()) { mPropertyDefaultValuesMap.insert(property, initialValue); ui->toolbuttonOptions->setVisible(true); } return property; } /*! * \brief Добавляет QEnumProperty. * \param name Название. * \param values Значения перечисления. * \param tooltip Описание. * \param label Метка. * \param root Корневой экземпляр QtProperty. 0 если QtProperty добавляется в верхний уровень. * \param initialValue Исходное значение. * \return Созданный экземпляр QtProperty. */ QtProperty * MainWindow::addEnumProperty(const QString &name, const QStringList &values, const QString &tooltip, const QString &label, QtProperty *root, QVariant initialValue) { QtProperty *property = mEnumPropertyManager->addProperty(name); mEnumPropertyManager->setEnumNames(property, values); mEnumPropertyManager->setValue(property, initialValue.toInt()); property->setToolTip(tooltip); if (root == nullptr) { ui->treePropertyBrowser->addProperty(property); } else { root->addSubProperty(property); } if (label != QString()) { mPropertiesMap.insert(label, property); } if (!initialValue.isNull()) { mPropertyDefaultValuesMap.insert(property, initialValue); ui->toolbuttonOptions->setVisible(true); } return property; } /*! * \brief Добавляет привязку свойства QtProperty. * \param label Метка свойства (ключ в mPropertiesMap и application.cfg). * \param getter Чтение значения в UI. * \param setter Запись значения из UI. */ void MainWindow::addPropertyBinding(const QString &label, std::function<QVariant()> getter, std::function<void(const QVariant &)> setter) { mPropertyBindingsList.append({ label, getter, setter }); } /*! * \brief Устанавливает значения свойств QtProperty. * \param list Список свойств QtProperty. */ void MainWindow::setPropertiesValues(const QList<QtProperty *> &list) { // Отключение сигналов для исключения вызовов отбработчиков события изменения. disconnect(mPropertyManager, SIGNAL(propertyChanged(QtProperty *))); disconnect(mEnumPropertyManager, SIGNAL(propertyChanged(QtProperty *))); ///// for (QtProperty *property : list) { for (const PropertyBinding &binding : mPropertyBindingsList) { if (property == mPropertiesMap[binding.label]) { if (mEnumPropertyManager->properties().contains(property)) { mEnumPropertyManager->setValue(property, binding.getter().toInt()); } else { mPropertyManager->setValue(property, binding.getter()); } break; } } } // Подключение сигналов для вызовов отбработчиков события изменения. connect(mPropertyManager, SIGNAL(propertyChanged(QtProperty *)), this, SLOT(slot_propertyChanged(QtProperty *))); connect(mEnumPropertyManager, SIGNAL(propertyChanged(QtProperty *)), this, SLOT(slot_propertyChanged(QtProperty *))); } /*! * \brief Применяет измененные значения свойств QtProperty. * \param list Список свойств QtProperty. */ void MainWindow::applyPropertiesValues(const QList<QtProperty *> &list) { for (QtProperty *property : list) { for (const PropertyBinding &binding : mPropertyBindingsList) { if (property == mPropertiesMap[binding.label]) { QVariant value; if (mEnumPropertyManager->properties().contains(property)) { value = mEnumPropertyManager->value(property); } else { value = mPropertyManager->value(property); } binding.setter(value); break; } } } } /*! * \brief Выводит в журнал значения свойств QtProperty. * \param list Список свойств QtProperty. * \param indent Отступ. */ void MainWindow::logPropertiesValues(const QList<QtProperty *> &list, const QString &indent) { std::function<bool(QtProperty *root, QtProperty *property, QString &path)> tryBuildPath = [&tryBuildPath](QtProperty *root, QtProperty *property, QString &path)->bool { QList<QtProperty *> properties = root->subProperties(); if (properties.contains(property)) { path = root->propertyName() + " // " + path; return true; } for (QtProperty *subProperty : properties) { if (tryBuildPath(subProperty, property, path)) { path = root->propertyName() + " // " + path; return true; } } return false; }; auto buildPropertyPath = [=](const QList<QtProperty *> &propertyList, QtProperty *property) { QString path; for (QtProperty *p : propertyList) { if (tryBuildPath(p, property, path)) { break; } } return path; }; for (QtProperty *i : list) { QString propertyString; QString propertyPath; if (typeid(*i) == typeid(QtVariantProperty)) { QtVariantProperty *property = dynamic_cast<QtVariantProperty *>(i); if (property->propertyType() == QtVariantPropertyManager::groupTypeId()) { //// variantProperty является корнем ветки свойств QtProperty. QList<QtProperty *> subProperties = property->subProperties(); logPropertiesValues(subProperties, indent); continue; } else { if (property->propertyType() == QVariant::Bool) { // variantProperty содержит значение типа bool. propertyString = QString("%1: %2").arg(property->propertyName()).arg(property->value().toBool() ? tr("да") : tr("нет")); } else { // В остальных случаях отображается стандартное текстовое представление. propertyString = QString("%1: %2").arg(property->propertyName()).arg(property->value().toString()); } propertyPath = buildPropertyPath(ui->treePropertyBrowser->properties(), property); } } else { // enumProperty. propertyString = QString("%1: %2").arg(i->propertyName()).arg(i->valueText()); propertyPath = buildPropertyPath(ui->treePropertyBrowser->properties(), i); } logString(indent + propertyPath + propertyString, false); } } /*! * \brief Выполняет сброс выделения и очистку списка измененных свойств QtProperty. * \param list Список свойств QtProperty. */ void MainWindow::resetPropertiesHighlighting(const QList<QtProperty *> &list) { for (QtProperty *i : list) { ui->treePropertyBrowser->setBackgroundColor(ui->treePropertyBrowser->items(i).first(), QColor::Invalid); } } /*! * \brief Создает встраиваемые виджеты. */ void MainWindow::createWidgets() { createWidgetsSpecific(); updateUi(); } /*! * \brief Удаляет встраиваемые виджеты. */ void MainWindow::deleteWidgets() { for (EmbeddableWidget *widget : mEmbeddableWidgets) { delete widget; } mEmbeddableWidgets.clear(); deleteWidgetsSpecific(); } /*! * \brief Добавляет встраиваемый виджет. * \param widget Указатель на встраиваемый виджет. * \param name Заголовок встраиваемого виджета. * \param label Метка встраиваемого виджета. * \return Встраиваемый виджет. */ EmbeddableWidget * MainWindow::addEmbeddable(EmbeddableWidget *widget, const QString &name, const QString &label) { int index = ui->tabWidget->addTab(new QWidget, name); ((QTabWidget *)ui->tabWidget->widget(index))->setLayout(new QVBoxLayout); ((QTabWidget *)ui->tabWidget->widget(index))->layout()->setContentsMargins(0, 0, 0, 0); mEmbeddableWidgets.append(widget); widget->setWindowTitle(name); widget->setLabel(label); connect(widget, SIGNAL(embedMe(bool)), this, SLOT(slot_embeddableWidget_embedMe(bool))); widget->setEmbedded(true); if (index > 0) { ui->toolbuttonToggleEmbedded->setVisible(true); } return widget; } /*! * \brief Возвращает встраиваемый виджет с указанной меткой. * \param label Метка. * \return Встраиваемый виджет с указанной меткой. */ EmbeddableWidget * MainWindow::embeddableByLabel(const QString &label) { for (EmbeddableWidget *widget : mEmbeddableWidgets) { if (widget->label() == label) { return widget; } } return nullptr; } /*! * \brief Добавляет виджет отображения прогресса задачи. * \param id Идентификатор. * \param taskName Название задачи. * \return Виджет отображения прогресса задачи. */ TaskProgressWidget * MainWindow::addTaskProgressWidget(int id, const QString &taskName) { TaskProgressWidget *taskProgressWidget = new TaskProgressWidget(this, taskName); ui->statusBar->addWidget(taskProgressWidget); taskProgressWidget->hide(); mTaskProgressWidgetsMap.insert(id, taskProgressWidget); return taskProgressWidget; } /*! * \brief Создает таблицу соединений. */ void MainWindow::createConnectionsTable() { ui->tablewidgetConnections->verticalHeader()->hide(); ui->tablewidgetConnections->setColumnWidth(0, 75); ui->tablewidgetConnections->setColumnWidth(1, 90); ui->tablewidgetConnections->setColumnWidth(2, 50); ui->tablewidgetConnections->setColumnWidth(3, 200); ui->tablewidgetConnections->setColumnWidth(4, 90); ui->tablewidgetConnections->setColumnWidth(5, 90); ui->tablewidgetConnections->setColumnWidth(6, 90); ui->tablewidgetConnections->setColumnWidth(7, 90); int row = 0; //// Клиентские соединения. for (ConnectionClient *client : App->connectionClients()) { ui->tablewidgetConnections->insertRow(row); // Название. ui->tablewidgetConnections->setItem(row, 0, new QTableWidgetItem(client->name())); // Состояние. ui->tablewidgetConnections->setItem(row, 1, new QTableWidgetItem()); ui->tablewidgetConnections->setCellWidget(row, 1, new ConnectionStateWidget()); // Порт. ui->tablewidgetConnections->setItem(row, 2, new QTableWidgetItem()); // Адрес. ui->tablewidgetConnections->setItem(row, 3, new QTableWidgetItem()); QWidget *widget = new ConnectionAddressWidget(client->id()); connect(widget, SIGNAL(change(int)), this, SLOT(slot_widgetConnectionAddress_change(int))); ui->tablewidgetConnections->setCellWidget(row, 3, widget); // Время соед. ui->tablewidgetConnections->setItem(row, 4, new QTableWidgetItem()); // Время разр. ui->tablewidgetConnections->setItem(row, 5, new QTableWidgetItem()); // Время приема. ui->tablewidgetConnections->setItem(row, 6, new QTableWidgetItem()); // Время выдачи. ui->tablewidgetConnections->setItem(row, 7, new QTableWidgetItem()); row ++; } //// Серверные соединения. for (ConnectionServer *server : App->connectionServers()) { ui->tablewidgetConnections->insertRow(row); // Название. ui->tablewidgetConnections->setItem(row, 0, new QTableWidgetItem(server->name())); // Состояние. ui->tablewidgetConnections->setItem(row, 1, new QTableWidgetItem()); // Порт. ui->tablewidgetConnections->setItem(row, 2, new QTableWidgetItem()); // Адрес. ui->tablewidgetConnections->setItem(row, 3, new QTableWidgetItem()); ui->tablewidgetConnections->item(row, 3)->setBackground(palette().color(QPalette::Window)); QWidget *widget = new ConnectionAddressWidget(server->id()); connect(widget, SIGNAL(change(int)), this, SLOT(slot_widgetConnectionAddress_change(int))); ui->tablewidgetConnections->setCellWidget(row, 3, widget); // Время соед. ui->tablewidgetConnections->setItem(row, 4, new QTableWidgetItem()); ui->tablewidgetConnections->item(row, 4)->setBackground(palette().color(QPalette::Window)); // Время разр. ui->tablewidgetConnections->setItem(row, 5, new QTableWidgetItem()); ui->tablewidgetConnections->item(row, 5)->setBackground(palette().color(QPalette::Window)); // Время приема. ui->tablewidgetConnections->setItem(row, 6, new QTableWidgetItem()); ui->tablewidgetConnections->item(row, 6)->setBackground(palette().color(QPalette::Window)); // Время выдачи. ui->tablewidgetConnections->setItem(row, 7, new QTableWidgetItem()); ui->tablewidgetConnections->item(row, 7)->setBackground(palette().color(QPalette::Window)); row ++; } //// ui->tablewidgetConnections->resizeColumnToContents(0); // Обновление таблицы соединений. updateConnectionsTable(Connection::EventUpdate); } /*! * \brief Обновляет таблицу соединений. * \param eventId Идентификатор события. * \param senderId Идентификатор соединения. */ void MainWindow::updateConnectionsTable(int eventId, int senderId) { int row = 0; //// Клиентские соединения. for (ConnectionClient *client : App->connectionClients()) { if (client->id() == senderId || senderId == -1) { // Название. // Состояние. ui->tablewidgetConnections->item(row, 1)->setText(ConnectionClient::toString(client->state())); if (client->state() == ConnectionClient::StateConnecting) { ui->tablewidgetConnections->item(row, 1)->setText(ui->tablewidgetConnections->item(row, 1)->text() + QString(" (%1)").arg(client->attempts())); } ui->tablewidgetConnections->item(row, 1)->setBackground((client->state() == ConnectionClient::StateConnected) ? Qt::transparent : Qt::red); ConnectionStateWidget *widget = (ConnectionStateWidget *)ui->tablewidgetConnections->cellWidget(row, 1); widget->setReading(client->isReading()); widget->setWriting(client->isWriting()); // Порт. ui->tablewidgetConnections->item(row, 2)->setText(QString::number(client->port())); // Адрес. ui->tablewidgetConnections->item(row, 3)->setText(client->address()); // Время соед. ui->tablewidgetConnections->item(row, 4)->setText(client->timeConnect().toString("hh:mm:ss")); // Время разр. ui->tablewidgetConnections->item(row, 5)->setText(client->timeDisconnect().toString("hh:mm:ss")); // Время приема. ui->tablewidgetConnections->item(row, 6)->setText(client->timeReceive().toString("hh:mm:ss")); // Время выдачи. ui->tablewidgetConnections->item(row, 7)->setText(client->timeSend().toString("hh:mm:ss")); } row ++; } //// Серверные соединения. for (ConnectionServer *server : App->connectionServers()) { if (server->id() == (senderId & 0x7FFF0000) || senderId == -1) { // Название. // Состояние. ui->tablewidgetConnections->item(row, 1)->setText(server->connections().isEmpty() ? tr("Ожидание...") : tr("Есть: %1").arg(server->connections().count())); ui->tablewidgetConnections->item(row, 1)->setBackground(server->connections().isEmpty() ? Qt::red : Qt::transparent); // Порт. ui->tablewidgetConnections->item(row, 2)->setText(QString::number(server->port())); // Адрес. // Время соед. // Время разр. // Время приема. // Время выдачи. if (eventId == Connection::EventConnect && server->id() == (senderId & 0x7FFF0000)) { ui->tablewidgetConnections->insertRow(row + 1); // Название. ui->tablewidgetConnections->setItem(row + 1, 0, new QTableWidgetItem()); ui->tablewidgetConnections->item(row + 1, 0)->setBackground(palette().color(QPalette::Window)); // Состояние. ui->tablewidgetConnections->setItem(row + 1, 1, new QTableWidgetItem()); ui->tablewidgetConnections->item(row + 1, 1)->setBackground(palette().color(QPalette::Window)); ui->tablewidgetConnections->setCellWidget(row + 1, 1, new ConnectionStateWidget()); // Порт. ui->tablewidgetConnections->setItem(row + 1, 2, new QTableWidgetItem()); ui->tablewidgetConnections->item(row + 1, 2)->setBackground(palette().color(QPalette::Window)); // Адрес. ui->tablewidgetConnections->setItem(row + 1, 3, new QTableWidgetItem()); // Время соед. ui->tablewidgetConnections->setItem(row + 1, 4, new QTableWidgetItem()); // Время разр. ui->tablewidgetConnections->setItem(row + 1, 5, new QTableWidgetItem()); // Время приема. ui->tablewidgetConnections->setItem(row + 1, 6, new QTableWidgetItem()); // Время выдачи. ui->tablewidgetConnections->setItem(row + 1, 7, new QTableWidgetItem()); senderId = -1; // Обновить все соединения. } if (eventId == Connection::EventDisconnect && server->id() == (senderId & 0x7FFF0000)) { ui->tablewidgetConnections->removeRow(row + 1); senderId = -1; // Обновить все соединения. } } row ++; //// Соединения. if (!server->connections().isEmpty()) { for (ConnectionClient *client : server->connections()) { if (client->id() == senderId || senderId == -1) { // Название. // Состояние. ConnectionStateWidget *widget = (ConnectionStateWidget *)ui->tablewidgetConnections->cellWidget(row, 1); widget->setReading(client->isReading()); widget->setWriting(client->isWriting()); // Порт. // Адрес. ui->tablewidgetConnections->item(row, 3)->setText(client->address()); // Время соед. ui->tablewidgetConnections->item(row, 4)->setText(client->timeConnect().toString("hh:mm:ss")); // Время разр. ui->tablewidgetConnections->item(row, 5)->setText(client->timeDisconnect().toString("hh:mm:ss")); // Время приема. ui->tablewidgetConnections->item(row, 6)->setText(client->timeReceive().toString("hh:mm:ss")); // Время выдачи. ui->tablewidgetConnections->item(row, 7)->setText(client->timeSend().toString("hh:mm:ss")); } row ++; } } } } /*! * \brief Создает таблицу полей профайлера. */ void MainWindow::createProfilerTable() { addAction(ui->actionProfiler); // Для Ctrl+P. ui->tablewidgetProfiler->verticalHeader()->hide(); int row = 0; createProfilerTableSpecific(row); row = ui->tablewidgetProfiler->rowCount(); ui->tablewidgetProfiler->insertRow(row); ui->tablewidgetProfiler->setItem(row, 0, new QTableWidgetItem(tr("Всего [мс]"))); ui->tablewidgetProfiler->setItem(row, 1, new QTableWidgetItem()); row ++; ui->tablewidgetProfiler->insertRow(row); ui->tablewidgetProfiler->setItem(row, 0, new QTableWidgetItem(tr("Очереди"))); ui->tablewidgetProfiler->setItem(row, 1, new QTableWidgetItem()); row ++; for (auto history : App->histories()) { ui->tablewidgetProfiler->insertRow(row); ui->tablewidgetProfiler->setItem(row, 0, new QTableWidgetItem(tr("История ") + history->name())); ui->tablewidgetProfiler->setItem(row, 1, new QTableWidgetItem()); row ++; } } /*! * \brief Заполняет таблицу полей профайлера. */ void MainWindow::updateProfilerTable() { int row = 0; updateProfilerTableSpecific(row); row = ui->tablewidgetProfiler->rowCount() - 2 - App->histories().count(); ui->tablewidgetProfiler->item(row, 1)->setText(QString::number(gProfilerInfo.timeTotal * 1000)); row ++; ui->tablewidgetProfiler->item(row, 1)->setText(QString("%1 / %2 / %3").arg(gInputPool.size()).arg(gPendingQueue.size()).arg(gInputQueue.size())); row ++; for (auto history : App->histories()) { ui->tablewidgetProfiler->item(row, 1)->setText(QString::number(history->count()) + tr(" из ") + QString::number(history->maxCount())); row ++; } } /*! * \brief Обновляет пользовательский интерфейс. */ void MainWindow::updateUi() { ui->actionWork->setChecked(gWork); ui->labelWork->setVisible(gWork); updateUiSpecific(); } /*! * \brief Выводит строку в окно отображения истории операций и сохраняет её в файл истории. * \param string Отображаемая строка. * \param time Признак добавления времени. */ void MainWindow::logString(const QString &string, bool time) { const int HistoryLinesMax = 250; //// Удаление избыточных строк. static int lineCount = 0; if (lineCount == HistoryLinesMax) { // Удаление первой записи в логе. QTextCursor cursor = ui->texteditLog->textCursor(); cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, 1); cursor.removeSelectedText(); lineCount --; } //// Добавление строк в отображение. QString str = string; if (time) { str = QDateTime::currentDateTime().toString("hh:mm:ss") + " " + str; } ui->texteditLog->append(str); lineCount ++; // Прокрутка лога для отображения добавленного текста. ui->texteditLog->verticalScrollBar()->setSliderPosition(ui->texteditLog->verticalScrollBar()->maximum()); // Сохранение строки в файл истории. saveString(str); } /*! * \brief Сохраняет строку в файл истории. * \param string Сохраняемая строка. */ void MainWindow::saveString(const QString &string) { //// Проверка наличия каталога. QDir dir(QString("%1/%2").arg(qApp->applicationDirPath()).arg(HistoryDir)); if (!dir.exists()) { dir.mkdir(dir.path()); } //// Проверка смены даты для изменения имени файла истории операций. static int dayOfYear = -1; if (dayOfYear != QDateTime::currentDateTime().date().dayOfYear()) { QString fileName = QString("%1/%2/%3.txt").arg(qApp->applicationDirPath()).arg(HistoryDir).arg(QDateTime::currentDateTime().toString(HistoryFilenameFormat)); mLogFile.setFileName(fileName); mLogFile.open(QIODevice::WriteOnly | QIODevice::Append); dayOfYear = QDateTime::currentDateTime().date().dayOfYear(); } //// Запись в файл. if (mLogFile.isOpen()) { QTextStream(&mLogFile) << string << endl; } } /*! * \brief Вызывается при начале воспроизведения. * \param fileName Имя файла. */ void MainWindow::slot_replayWidget_beginRead(const QString &fileName) { logString(tr("Начало воспроизведения файла \"") + fileName + "\"."); } /*! * \brief Вызывается при завершении воспроизведения. * \param fileName Имя файла. */ void MainWindow::slot_replayWidget_endRead(const QString &fileName) { logString(tr("Завершено воспроизведение файла \"") + fileName + "\"."); if (mQuitOnEndRead) { App->quit(); } } /*! * \brief Вызывается при начале записи в файл. * \param fileName Имя файла. */ void MainWindow::slot_replayWidget_beginWrite(const QString &fileName) { logString(tr("Начало записи файла \"") + fileName + "\"."); } /*! * \brief Вызывается при завершении записи в файл. * \param fileName Имя файла. */ void MainWindow::slot_replayWidget_endWrite(const QString &fileName) { logString(tr("Завершена запись файла \"") + fileName + "\"."); } /*! * \brief Вызывается при изменении выделенного элемента дерева свойств QtProperty. Устанавливает описание свойства в панели отображения описания. * \param item Указатель на выделенный элемент. */ void MainWindow::on_treePropertyBrowser_currentItemChanged(QtBrowserItem *item) { if (item != nullptr) { QtProperty *property = item->property(); if (property != nullptr) { ui->texteditPropertyDescription->setPlainText(property->toolTip()); } else { ui->texteditPropertyDescription->setPlainText(QString()); } } } /*! * \brief Вызывается при изменении свойства QtProperty. * \param property Измененное свойство QtProperty. */ void MainWindow::slot_propertyChanged(QtProperty *property) { if (mPropertyManager->propertyType(property) != QtVariantPropertyManager::groupTypeId()) { ui->toolbuttonApplyChange->setEnabled(true); ui->toolbuttonCancelChange->setEnabled(true); //// Изменение цвета изменённого параметра и запоминание параметра в списке изменённых. mChangedPropertiesList.removeOne(property); // Если property уже в списке, то удалить. QtBrowserItem *item = ui->treePropertyBrowser->items(property).first(); ui->treePropertyBrowser->setBackgroundColor(item, QColor(Qt::green)); mChangedPropertiesList.append(property); static QtProperty *root = nullptr; // Обязательно static!!! if (root == nullptr) // Исключение рекурсии. { root = property; propertyChangedSpecific(property); } if (root == property) { root = nullptr; } } } /*! * \brief Вызывается при нажатии кнопки применения измененных свойств QtProperty. */ void MainWindow::on_toolbuttonApplyChange_clicked() { ui->toolbuttonApplyChange->setEnabled(false); ui->toolbuttonCancelChange->setEnabled(false); // Применение. applyPropertiesValues(mChangedPropertiesList); // Вывод в журнал информации об изменённых свойствах QtProperty. if (!mChangedPropertiesList.isEmpty()) { logString(tr("Изменение параметров:")); logPropertiesValues(mChangedPropertiesList, QString(" ")); } //// resetPropertiesHighlighting(mChangedPropertiesList); mChangedPropertiesList.clear(); // Сохранение настроек на случай аварийного выхода из программы после изменения настроек. writeProperties(); } /*! * \brief Вызывается при нажатии кнопки отмены измененных свойств QtProperty. */ void MainWindow::on_toolbuttonCancelChange_clicked() { setPropertiesValues(mChangedPropertiesList); resetPropertiesHighlighting(mChangedPropertiesList); mChangedPropertiesList.clear(); ui->toolbuttonApplyChange->setEnabled(false); ui->toolbuttonCancelChange->setEnabled(false); } /*! * \brief Вызывается при активации QAction включения/выключения обработки. * \param checked Признак нажатия кнопки. */ void MainWindow::on_actionWork_triggered(bool checked) { gWork = checked; updateUi(); } /*! * \brief Вызывается при активации QAction удаления данных. */ void MainWindow::on_actionDelete_triggered() { if (QMessageBox::question(this, App->applicationName(), tr("Удалить данные?"), tr("Да"), tr("Нет")) == 0) { App->clearData(); // Обновление таблицы профайлера. updateProfilerTable(); // Обновление окон отображения. for (EmbeddableWidget *widget : mEmbeddableWidgets) { widget->updateView(); } } } /*! * \brief Вызывается после завершения обработки массива входной информации. * \param results Список кодов результата завершения цикла обработки информации. */ void MainWindow::iterationDone(const QSet<int> &results) { // Обновление таблицы профайлера. updateProfilerTable(); // Обновление окон отображения. for (EmbeddableWidget *widget : mEmbeddableWidgets) { widget->checkUpdateView(results); } } /*! * \brief Устанавливает или проверяет возможность установки значения property. * \param propertyName Название property. * \param propertyValue Значение property. * \param execute Признак установки значения. * \param result Результат. */ void MainWindow::setPropertyValue(const QString &propertyName, const QString &propertyValue, bool execute, bool &result) { QtProperty *property = mPropertiesMap.value(propertyName); if (property != nullptr) { QList<QtProperty *> backup = mChangedPropertiesList; mChangedPropertiesList.clear(); result = false; if (mPropertyManager->properties().contains(property)) { int propertyType = mPropertyManager->valueType(property); if (propertyType == QVariant::Bool) { bool value = propertyValue.toInt(&result) == 1; if (result) { if (execute) { mPropertyManager->setValue(property, value); } } } else if (propertyType == QVariant::Int) { int value = propertyValue.toInt(&result); if (result) { if (execute) { mPropertyManager->setValue(property, value); } } } } if (mEnumPropertyManager->properties().contains(property)) { int value = propertyValue.toInt(&result); if (result) { if (execute) { mEnumPropertyManager->setValue(property, value); } } } if (result) { on_toolbuttonApplyChange_clicked(); } mChangedPropertiesList = backup; mChangedPropertiesList.removeAll(property); } else { result = false; } } /*! * \brief Устанавливает или проверяет возможность установки значения property. * \param propertyName Название property. * \param propertyValue Значение property. * \param execute Признак установки значения. * \param result Результат. */ void MainWindow::setPropertyValue(const QString &propertyName, const QVariant &propertyValue, bool execute, bool &result) { QtProperty *property = mPropertiesMap.value(propertyName); if (property != nullptr) { QList<QtProperty *> backup = mChangedPropertiesList; mChangedPropertiesList.clear(); result = false; int propertyType = mPropertyManager->valueType(property); if (propertyType == QVariant::Bool) { bool value = propertyValue.toInt(&result) == 1; if (result) { if (execute) { mPropertyManager->setValue(property, value); } } } else if (propertyType == QVariant::Int) { int value = propertyValue.toInt(&result); if (result) { if (execute) { mPropertyManager->setValue(property, value); } } } if (result) { on_toolbuttonApplyChange_clicked(); } mChangedPropertiesList = backup; mChangedPropertiesList.removeAll(property); } else { result = false; } } /*! * \brief Выполняется при нажатии кнопки "ОК" диалога сохранения значений свойств properties. */ void MainWindow::slot_storePropertiesValuesDialog_accepted() { SavePropertiesValuesDialog *dialog = (SavePropertiesValuesDialog *)sender(); QString label = dialog->label(); QString description = dialog->description(); if (!label.isEmpty()) { //// Сохранение значений в cfg-файл. QSettings settings(App->applicationCfgPath(), QSettings::IniFormat); settings.beginGroup("StoredValues"); settings.beginGroup(label); settings.setValue("description", description); for (QtProperty *property : mPropertyDefaultValuesMap.keys()) { QString key = mPropertiesMap.key(property); if (mPropertyDefaultValuesMap.contains(property)) { if (mPropertyManager->properties().contains(property)) { settings.setValue(key, mPropertyManager->value(property)); } if (mEnumPropertyManager->properties().contains(property)) { settings.setValue(key, mEnumPropertyManager->value(property)); } } } settings.endGroup(); settings.endGroup(); //// Обновление меню. readStoredProperiesValues(); } } /*! * \brief Выполняется при выборе для восстановления набора сохраненных значений свойств properties. */ void MainWindow::slot_restoreStoredPropertiesValues_triggered() { QAction *action = (QAction *)sender(); QString label = action->data().toString(); //// Восстановление значений свойств properties из cfg-файла. QSettings settings(App->applicationCfgPath(), QSettings::IniFormat); settings.beginGroup("StoredValues"); settings.beginGroup(label); for (QtProperty *property : mPropertyDefaultValuesMap.keys()) { QString key = mPropertiesMap.key(property); if (mPropertyDefaultValuesMap.contains(property)) { if (mPropertyManager->properties().contains(property)) { QVariant value = settings.value(key); if (!value.isNull()) { mPropertyManager->setValue(property, value); } } if (mEnumPropertyManager->properties().contains(property)) { bool ok; int value = settings.value(key).toInt(&ok); if (ok) { mEnumPropertyManager->setValue(property, value); } } } } settings.endGroup(); settings.endGroup(); } /*! * \brief Выполняется при выборе для удаления набора сохраненных значений свойств properties. */ void MainWindow::slot_removeStoredPropertiesValues_triggered() { QAction *action = (QAction *)sender(); QString label = action->data().toString(); //// Удаление из cfg-файла группы с меткой label. QSettings settings(App->applicationCfgPath(), QSettings::IniFormat); settings.beginGroup("StoredValues"); settings.remove(label); settings.endGroup(); //// Обновление меню. readStoredProperiesValues(); } /*! * \brief Создает меню установки/удаления сохраненных в cfg-файле наборов значений свойств properties. */ void MainWindow::readStoredProperiesValues() { //// Удаление старого меню. ui->toolbuttonOptions->setMenu(0); //// Создание нового меню. QMenu *menu = new QMenu(this); menu->insertAction(0, ui->actionSavePropertiesValues); menu->insertAction(0, ui->actionRestoreDefaults); menu->setToolTipsVisible(true); ui->toolbuttonOptions->setMenu(menu); QSettings settings(App->applicationCfgPath(), QSettings::IniFormat); settings.beginGroup("StoredValues"); QStringList groups = settings.childGroups(); for (QString label : groups) { settings.beginGroup(label); QString description = settings.value("description").toString(); settings.endGroup(); QMenu *submenu = new QMenu(label); submenu->setToolTip(description); QAction *actionRestore = submenu->addAction(tr("Установить"), this, SLOT(slot_restoreStoredPropertiesValues_triggered())); actionRestore->setData(label); QAction *actionRemove = submenu->addAction(tr("Удалить"), this, SLOT(slot_removeStoredPropertiesValues_triggered())); actionRemove->setData(label); menu->insertMenu(menu->actions().first(), submenu); } settings.endGroup(); //// Вставка разделителя если есть сохраненные наборы. if (menu->actions().count() > 2) { menu->insertSeparator(ui->actionSavePropertiesValues); } } /*! * \brief Отображает версию программы. */ void MainWindow::on_actionVersion_triggered() { QMessageBox::information(this, QString(), tr("Версия программы: ") + App->applicationVersion()); } /*! * \brief Выполняет сигнал о начале прогресса задачи. * \param id Идентификтор задачи. * \param total Полный прогресс задачи. */ void MainWindow::slot_taskProgressBegin(int id, int total) { TaskProgressWidget *taskProgressWidget = mTaskProgressWidgetsMap[id]; if (taskProgressWidget != nullptr) { taskProgressWidget->progressBegin(total); } } /*! * \brief Выполняет сигнал об изменении прогресса задачи. * \param id Идентификтор задачи. * \param count Текущий прогресс задачи. * \param total Полный прогресс задачи. */ void MainWindow::slot_taskProgressStep(int id, int count, int total) { TaskProgressWidget *taskProgressWidget = mTaskProgressWidgetsMap[id]; if (taskProgressWidget != nullptr) { taskProgressWidget->progressStep(count, total); } } /*! * \brief Выполняет сигнал о завершении прогресса задачи. * \param id Идентификтор задачи. */ void MainWindow::slot_taskProgressEnd(int id) { TaskProgressWidget *taskProgressWidget = mTaskProgressWidgetsMap[id]; if (taskProgressWidget != nullptr) { taskProgressWidget->progressEnd(); } }