File indexing completed on 2024-04-28 15:09:13

0001 /*
0002     SPDX-FileCopyrightText: 2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003     SPDX-FileCopyrightText: 2017 Robert Lancaster <rlancaste@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "stellarsolverprofileeditor.h"
0009 
0010 #include "kstars.h"
0011 #include "Options.h"
0012 #include "kspaths.h"
0013 #include "ksmessagebox.h"
0014 #include "stellarsolverprofile.h"
0015 
0016 #include <KConfigDialog>
0017 #include <QInputDialog>
0018 #include <QFileDialog>
0019 #include <QSettings>
0020 #include <stellarsolver.h>
0021 
0022 namespace Ekos
0023 {
0024 StellarSolverProfileEditor::StellarSolverProfileEditor(QWidget *parent, ProfileGroup group,
0025         KConfigDialog *dialog) : QWidget(KStars::Instance())
0026 {
0027     Q_UNUSED(parent);
0028     setupUi(this);
0029 
0030     //Get a pointer to the KConfigDialog
0031     m_ConfigDialog = dialog;
0032 
0033     setProfileGroup(group);
0034     optionsProfileGroup->setEnabled(false);
0035 
0036     /* // we may want to do this eventually
0037     connect(optionsProfileGroup, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index){
0038         setProfileGroup((ProfileGroup)index);
0039     });
0040     */
0041 
0042     openProfile->setIcon(
0043         QIcon::fromTheme("document-open"));
0044     openProfile->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0045     connect(openProfile, &QPushButton::clicked, this, &StellarSolverProfileEditor::openSingleProfile);
0046 
0047     saveProfile->setIcon(
0048         QIcon::fromTheme("document-save"));
0049     saveProfile->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0050     connect(saveProfile, &QPushButton::clicked, this, &StellarSolverProfileEditor::saveSingleProfile);
0051 
0052     /*  This is not implemented yet.
0053     copyProfile->setIcon(
0054         QIcon::fromTheme("edit-copy"));
0055     copyProfile->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0056     */
0057     copyProfile->setVisible(false);  //until we enable it.
0058 
0059     saveBackups->setIcon(
0060         QIcon::fromTheme("document-save"));
0061     saveBackups->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0062     connect(saveBackups, &QPushButton::clicked, this, &StellarSolverProfileEditor::saveBackupProfiles);
0063 
0064     loadBackups->setIcon(
0065         QIcon::fromTheme("document-open"));
0066     loadBackups->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0067     connect(loadBackups, &QPushButton::clicked, this, &StellarSolverProfileEditor::openBackupProfiles);
0068 
0069     reloadProfiles->setIcon(
0070         QIcon::fromTheme("system-reboot"));
0071     reloadProfiles->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0072     connect(reloadProfiles, &QPushButton::clicked, this, &StellarSolverProfileEditor::loadProfiles);
0073 
0074     loadDefaults->setIcon(
0075         QIcon::fromTheme("go-down"));
0076     loadDefaults->setAttribute(Qt::WA_LayoutUsesWidgetRect);
0077     connect(loadDefaults, &QPushButton::clicked, this, &StellarSolverProfileEditor::loadDefaultProfiles);
0078 
0079     connect(addOptionProfile, &QAbstractButton::clicked, this, [this]()
0080     {
0081         bool ok;
0082         QString name = QInputDialog::getText(this, tr("New Options Profile"),
0083                                              tr("What would you like your profile to be called?"), QLineEdit::Normal,
0084                                              "", &ok);
0085         if (ok && !name.isEmpty())
0086         {
0087             disconnectOptionsProfileComboBox();
0088             SSolver::Parameters params = getSettingsFromUI();
0089             params.listName = name;
0090             optionsList.append(params);
0091             optionsProfile->addItem(name);
0092             optionsProfile->setCurrentText(name);
0093             openOptionsProfileNum = optionsProfile->count() - 1;
0094             settingJustChanged();
0095             connectOptionsProfileComboBox();
0096         }
0097     });
0098 
0099     connect(removeOptionProfile, &QAbstractButton::clicked, this, [this]()
0100     {
0101         if(optionsList.count() == 0)
0102             return;
0103         int item = optionsProfile->currentIndex();
0104         disconnectOptionsProfileComboBox();
0105         optionsProfile->removeItem(item);
0106         optionsList.removeAt(item);
0107         if(optionsProfile->count() > 0)
0108         {
0109             if(item > optionsList.count() - 1) //So that it doesn't try to set the new list to a nonexistant one
0110                 item--;
0111             openOptionsProfileNum = item;
0112             loadOptionsProfileIgnoreOldSettings(item); //Because the old one no longer exists
0113         }
0114         settingJustChanged();
0115         connectOptionsProfileComboBox();
0116     });
0117 
0118     connect(description, &QTextEdit::textChanged, this, &StellarSolverProfileEditor::settingJustChanged);
0119 
0120     QList<QLineEdit *> lines = this->findChildren<QLineEdit *>();
0121     for(QLineEdit *line : lines)
0122         connect(line, &QLineEdit::textEdited, this, &StellarSolverProfileEditor::settingJustChanged);
0123 
0124     QList<QCheckBox *> checks = this->findChildren<QCheckBox *>();
0125     for(QCheckBox *check : checks)
0126         connect(check, &QCheckBox::stateChanged, this, &StellarSolverProfileEditor::settingJustChanged);
0127 
0128     QList<QComboBox *> combos = this->findChildren<QComboBox *>();
0129     for(QComboBox *combo : combos)
0130     {
0131         if(combo != optionsProfile)
0132             connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &StellarSolverProfileEditor::settingJustChanged);
0133     }
0134 
0135     QList<QSpinBox *> spins = this->findChildren<QSpinBox *>();
0136     for(QSpinBox *spin : spins)
0137         connect(spin, QOverload<int>::of(&QSpinBox::valueChanged), this, &StellarSolverProfileEditor::settingJustChanged);
0138 
0139     if(selectedProfileGroup != AlignProfiles)
0140         astrometryOptions->setVisible(false);
0141     connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply()));
0142     connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply()));
0143 }
0144 
0145 void StellarSolverProfileEditor::setProfileGroup(ProfileGroup group)
0146 {
0147     selectedProfileGroup = group;
0148     optionsProfileGroup->setCurrentIndex(static_cast<int>(group));
0149     QString profileGroupFileName;
0150     switch(selectedProfileGroup)
0151     {
0152         case AlignProfiles:
0153             profileGroupFileName = "SavedAlignProfiles.ini";
0154             break;
0155         case FocusProfiles:
0156             profileGroupFileName = "SavedFocusProfiles.ini";
0157             break;
0158         case GuideProfiles:
0159             profileGroupFileName = "SavedGuideProfiles.ini";
0160             break;
0161         case HFRProfiles:
0162             profileGroupFileName = "SavedHFRProfiles.ini";
0163             break;
0164     }
0165 
0166     savedOptionsProfiles = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(profileGroupFileName);
0167     loadProfiles();
0168 }
0169 
0170 void StellarSolverProfileEditor::connectOptionsProfileComboBox()
0171 {
0172     connect(optionsProfile, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
0173             &StellarSolverProfileEditor::loadOptionsProfile);
0174 }
0175 
0176 void StellarSolverProfileEditor::disconnectOptionsProfileComboBox()
0177 {
0178     disconnect(optionsProfile, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
0179                &StellarSolverProfileEditor::loadOptionsProfile);
0180 }
0181 
0182 void StellarSolverProfileEditor::settingJustChanged()
0183 {
0184     optionsAreSaved = false;
0185     m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(true);
0186 }
0187 
0188 void StellarSolverProfileEditor::loadProfile(const QString &profile)
0189 {
0190     optionsProfile->setCurrentText(profile);
0191 }
0192 
0193 void StellarSolverProfileEditor::loadOptionsProfile()
0194 {
0195     if(optionsProfile->count() == 0)
0196         return;
0197     if(openOptionsProfileNum < optionsList.count() && openOptionsProfileNum > 0)
0198     {
0199         SSolver::Parameters editorOptions = getSettingsFromUI();
0200         SSolver::Parameters currentProfile = optionsList.at(openOptionsProfileNum);
0201         if(!(editorOptions == currentProfile) || editorOptions.description != currentProfile.description)
0202         {
0203             editorOptions.listName = currentProfile.listName;
0204             optionsList.replace(openOptionsProfileNum, editorOptions);
0205         }
0206     }
0207     SSolver::Parameters newProfile = optionsList.at(optionsProfile->currentIndex());
0208     QList<QWidget *> controls = this->findChildren<QWidget *>();
0209     for(QWidget *control : controls)
0210         control->blockSignals(true);
0211     sendSettingsToUI(newProfile);
0212     for(QWidget *control : controls)
0213         control->blockSignals(false);
0214     openOptionsProfileNum = optionsProfile->currentIndex();
0215 }
0216 
0217 void StellarSolverProfileEditor::loadOptionsProfileIgnoreOldSettings(int index)
0218 {
0219     if(index >= optionsProfile->count())
0220         return;
0221     SSolver::Parameters newProfile = optionsList.at(index);
0222     QList<QWidget *> controls = this->findChildren<QWidget *>();
0223     for(QWidget *control : controls)
0224         control->blockSignals(true);
0225     sendSettingsToUI(newProfile);
0226     optionsProfile->setCurrentIndex(index);
0227     openOptionsProfileNum = optionsProfile->currentIndex();
0228     for(QWidget *control : controls)
0229         control->blockSignals(false);
0230 }
0231 
0232 //This sets all the settings for either the internal or external sextractor
0233 //based on the requested settings in the mainwindow interface.
0234 //If you are implementing the StellarSolver Library in your progra, you may choose to change some or all of these settings or use the defaults.
0235 SSolver::Parameters StellarSolverProfileEditor::getSettingsFromUI()
0236 {
0237     SSolver::Parameters params;
0238     params.description = description->toPlainText();
0239     //These are to pass the parameters to the internal sextractor
0240     params.apertureShape = (SSolver::Shape) apertureShape->currentIndex();
0241     params.kron_fact = kron_fact->text().toDouble();
0242     params.subpix = subpix->text().toInt() ;
0243     params.r_min = r_min->text().toFloat();
0244     params.magzero = magzero->text().toFloat();
0245     params.threshold_bg_multiple = threshMultiple->text().toFloat();
0246     params.threshold_offset = threshOffset->text().toFloat();
0247     params.minarea = minarea->text().toFloat();
0248     params.deblend_thresh = deblend_thresh->text().toInt();
0249     params.deblend_contrast = deblend_contrast->text().toFloat();
0250     params.clean = (cleanCheckBox->isChecked()) ? 1 : 0;
0251     params.clean_param = clean_param->text().toDouble();
0252     params.convFilterType = (SSolver::ConvFilterType) convFilter->currentIndex();
0253     params.fwhm = fwhm->value();
0254 
0255     //Star Filter Settings
0256     params.resort = resort->isChecked();
0257     params.maxSize = maxSize->text().toDouble();
0258     params.minSize = minSize->text().toDouble();
0259     params.maxEllipse = maxEllipse->text().toDouble();
0260     params.initialKeep = initialKeep->text().toInt();
0261     params.keepNum = keepNum->text().toInt();
0262     params.removeBrightest = brightestPercent->text().toDouble();
0263     params.removeDimmest = dimmestPercent->text().toDouble();
0264     params.saturationLimit = saturationLimit->text().toDouble();
0265 
0266     //Settings that usually get set by the config file
0267 
0268     params.maxwidth = maxWidth->text().toDouble();
0269     params.minwidth = minWidth->text().toDouble();
0270     params.inParallel = inParallel->isChecked();
0271     params.multiAlgorithm = (SSolver::MultiAlgo)multiAlgo->currentIndex();
0272     params.solverTimeLimit = solverTimeLimit->text().toInt();
0273 
0274     params.resort = resort->isChecked();
0275     params.autoDownsample = autoDownsample->isChecked();
0276     params.downsample = downsample->value();
0277     params.search_radius = radius->text().toDouble();
0278 
0279     return params;
0280 }
0281 
0282 void StellarSolverProfileEditor::sendSettingsToUI(SSolver::Parameters a)
0283 {
0284 
0285     description->setText(a.description);
0286     //Sextractor Settings
0287 
0288     apertureShape->setCurrentIndex(a.apertureShape);
0289     kron_fact->setText(QString::number(a.kron_fact));
0290     subpix->setText(QString::number(a.subpix));
0291     r_min->setText(QString::number(a.r_min));
0292 
0293     magzero->setText(QString::number(a.magzero));
0294     threshMultiple->setText(QString::number(a.threshold_bg_multiple));
0295     threshOffset->setText(QString::number(a.threshold_offset));
0296     minarea->setText(QString::number(a.minarea));
0297     deblend_thresh->setText(QString::number(a.deblend_thresh));
0298     deblend_contrast->setText(QString::number(a.deblend_contrast));
0299     cleanCheckBox->setChecked(a.clean == 1);
0300     clean_param->setText(QString::number(a.clean_param));
0301     convFilter->setCurrentIndex(a.convFilterType);
0302     fwhm->setValue(a.fwhm);
0303 
0304     //Star Filter Settings
0305 
0306     maxSize->setText(QString::number(a.maxSize));
0307     minSize->setText(QString::number(a.minSize));
0308     maxEllipse->setText(QString::number(a.maxEllipse));
0309     initialKeep->setText(QString::number(a.initialKeep));
0310     keepNum->setText(QString::number(a.keepNum));
0311     brightestPercent->setText(QString::number(a.removeBrightest));
0312     dimmestPercent->setText(QString::number(a.removeDimmest));
0313     saturationLimit->setText(QString::number(a.saturationLimit));
0314 
0315     //Astrometry Settings
0316 
0317     autoDownsample->setChecked(a.autoDownsample);
0318     downsample->setValue(a.downsample);
0319     inParallel->setChecked(a.inParallel);
0320     multiAlgo->setCurrentIndex(a.multiAlgorithm);
0321     solverTimeLimit->setText(QString::number(a.solverTimeLimit));
0322     minWidth->setText(QString::number(a.minwidth));
0323     maxWidth->setText(QString::number(a.maxwidth));
0324     radius->setText(QString::number(a.search_radius));
0325     resort->setChecked(a.resort);
0326 }
0327 
0328 void StellarSolverProfileEditor::copySingleProfile()
0329 {
0330 
0331 }
0332 
0333 void StellarSolverProfileEditor::openSingleProfile()
0334 {
0335     QString fileURL = QFileDialog::getOpenFileName(nullptr, "Load Options Profiles File", dirPath,
0336                       "INI files(*.ini)");
0337     if (fileURL.isEmpty())
0338         return;
0339     if(!QFileInfo::exists(fileURL))
0340     {
0341         KSMessageBox::warning(this, "Message", "The file doesn't exist");
0342         return;
0343     }
0344     QSettings settings(fileURL, QSettings::IniFormat);
0345     QStringList groups = settings.childGroups();
0346     for(QString group : groups)
0347     {
0348         settings.beginGroup(group);
0349         QStringList keys = settings.childKeys();
0350         QMap<QString, QVariant> map;
0351         for(QString key : keys)
0352             map.insert(key, settings.value(key));
0353         SSolver::Parameters newParams = SSolver::Parameters::convertFromMap(map);
0354         optionsList.append(newParams);
0355         optionsProfile->addItem(group);
0356     }
0357     if(optionsProfile->count() > 0)
0358         loadProfile(groups.first());
0359     m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(true);
0360 }
0361 
0362 void StellarSolverProfileEditor::loadProfiles()
0363 {
0364     if( !optionsAreSaved )
0365     {
0366         if(QMessageBox::question(this, "Abort?",
0367                                  "You made unsaved changes in the settings, do you really wish to overwrite them?") == QMessageBox::No)
0368         {
0369             return;
0370         }
0371         optionsAreSaved = true; //They just got overwritten
0372     }
0373     disconnectOptionsProfileComboBox();
0374     optionsProfile->clear();
0375     if(QFile(savedOptionsProfiles).exists())
0376         optionsList = StellarSolver::loadSavedOptionsProfiles(savedOptionsProfiles);
0377     else
0378         optionsList = getDefaultProfiles();
0379     for(SSolver::Parameters params : optionsList)
0380         optionsProfile->addItem(params.listName);
0381     if(optionsList.count() > 0)
0382     {
0383         sendSettingsToUI(optionsList.at(0));
0384         openOptionsProfileNum = 0;
0385     }
0386     connectOptionsProfileComboBox();
0387     m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(false);
0388 }
0389 
0390 void StellarSolverProfileEditor::saveSingleProfile()
0391 {
0392     QString fileURL = QFileDialog::getSaveFileName(nullptr, "Save Options Profiles", dirPath,
0393                       "INI files(*.ini)");
0394     if (fileURL.isEmpty())
0395         return;
0396     QSettings settings(fileURL, QSettings::IniFormat);
0397     SSolver::Parameters params = optionsList.at(optionsProfile->currentIndex());
0398     settings.beginGroup(params.listName);
0399     QMap<QString, QVariant> map = SSolver::Parameters::convertToMap(params);
0400     QMapIterator<QString, QVariant> it(map);
0401     while(it.hasNext())
0402     {
0403         it.next();
0404         settings.setValue(it.key(), it.value());
0405     }
0406     settings.endGroup();
0407 }
0408 
0409 void StellarSolverProfileEditor::saveProfiles()
0410 {
0411     QSettings settings(savedOptionsProfiles, QSettings::IniFormat);
0412     for(int i = 0 ; i < optionsList.count(); i++)
0413     {
0414         SSolver::Parameters params = optionsList.at(i);
0415         settings.beginGroup(params.listName);
0416         QMap<QString, QVariant> map = SSolver::Parameters::convertToMap(params);
0417         QMapIterator<QString, QVariant> it(map);
0418         while(it.hasNext())
0419         {
0420             it.next();
0421             settings.setValue(it.key(), it.value());
0422         }
0423         settings.endGroup();
0424     }
0425     QStringList groups = settings.childGroups();
0426     for(QString group : groups)
0427     {
0428         bool groupInList = false;
0429         for(SSolver::Parameters params : optionsList)
0430         {
0431             if(params.listName == group)
0432                 groupInList = true;
0433         }
0434         if( ! groupInList)
0435             settings.remove(group);
0436     }
0437 }
0438 
0439 QList<SSolver::Parameters> StellarSolverProfileEditor::getDefaultProfiles()
0440 {
0441     switch(selectedProfileGroup)
0442     {
0443         case FocusProfiles:
0444             return getDefaultFocusOptionsProfiles();
0445         case HFRProfiles:
0446             return getDefaultHFROptionsProfiles();
0447         case GuideProfiles:
0448             return getDefaultGuideOptionsProfiles();
0449         default:
0450         case AlignProfiles:
0451             return getDefaultAlignOptionsProfiles();
0452     }
0453 }
0454 
0455 void StellarSolverProfileEditor::loadDefaultProfiles()
0456 {
0457     if( !optionsAreSaved )
0458     {
0459         if(QMessageBox::question(this, "Abort?",
0460                                  "You made unsaved changes in the settings, do you really wish to overwrite them?") == QMessageBox::No)
0461         {
0462             return;
0463         }
0464         optionsAreSaved = true; //They just got overwritten
0465     }
0466     disconnectOptionsProfileComboBox();
0467     optionsList = getDefaultProfiles();
0468     optionsProfile->clear();
0469     for(SSolver::Parameters param : optionsList)
0470         optionsProfile->addItem(param.listName);
0471     connectOptionsProfileComboBox();
0472     if(optionsProfile->count() > 0)
0473         loadOptionsProfileIgnoreOldSettings(0);
0474     m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(true);
0475 }
0476 
0477 void StellarSolverProfileEditor::saveBackupProfiles()
0478 {
0479     QString fileURL = QFileDialog::getSaveFileName(nullptr, "Save Options Profiles", dirPath,
0480                       "INI files(*.ini)");
0481     if (fileURL.isEmpty())
0482         return;
0483     QSettings settings(fileURL, QSettings::IniFormat);
0484     for(int i = 0 ; i < optionsList.count(); i++)
0485     {
0486         SSolver::Parameters params = optionsList.at(i);
0487         settings.beginGroup(params.listName);
0488         QMap<QString, QVariant> map = SSolver::Parameters::convertToMap(params);
0489         QMapIterator<QString, QVariant> it(map);
0490         while(it.hasNext())
0491         {
0492             it.next();
0493             settings.setValue(it.key(), it.value());
0494         }
0495         settings.endGroup();
0496     }
0497 }
0498 
0499 void StellarSolverProfileEditor::openBackupProfiles()
0500 {
0501     QString fileURL = QFileDialog::getOpenFileName(nullptr, "Load Options Profiles File", dirPath,
0502                       "INI files(*.ini)");
0503     if (fileURL.isEmpty())
0504         return;
0505     if(!QFileInfo(fileURL).exists())
0506     {
0507         QMessageBox::warning(this, "Message", "The file doesn't exist");
0508         return;
0509     }
0510     disconnectOptionsProfileComboBox();
0511     optionsList.clear();
0512     QSettings settings(fileURL, QSettings::IniFormat);
0513     QStringList groups = settings.childGroups();
0514     for(QString group : groups)
0515     {
0516         settings.beginGroup(group);
0517         QStringList keys = settings.childKeys();
0518         QMap<QString, QVariant> map;
0519         for(QString key : keys)
0520             map.insert(key, settings.value(key));
0521         SSolver::Parameters newParams = SSolver::Parameters::convertFromMap(map);
0522         optionsList.append(newParams);
0523         optionsProfile->addItem(group);
0524     }
0525     if(optionsProfile->count() > 0)
0526         loadOptionsProfileIgnoreOldSettings(0);
0527     connectOptionsProfileComboBox();
0528     m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(true);
0529 }
0530 
0531 void StellarSolverProfileEditor::slotApply()
0532 {
0533     int index = optionsProfile->currentIndex();
0534     SSolver::Parameters currentParams = getSettingsFromUI();
0535     SSolver::Parameters savedParams = optionsList.at(index);
0536     if(!(currentParams == savedParams) || currentParams.description != savedParams.description)
0537     {
0538         currentParams.listName = savedParams.listName;
0539         optionsList.replace(index, currentParams);
0540     }
0541     optionsAreSaved = true;
0542 
0543     saveProfiles();
0544     emit optionsProfilesUpdated();
0545 }
0546 
0547 }