1 /*
2 SPDX-FileCopyrightText: 2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 SPDX-FileCopyrightText: 2017 Robert Lancaster <rlancaste@gmail.com>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "stellarsolverprofileeditor.h"
9
10 #include "kstars.h"
11 #include "Options.h"
12 #include "kspaths.h"
13 #include "ksmessagebox.h"
14 #include "stellarsolverprofile.h"
15
16 #include <KConfigDialog>
17 #include <QInputDialog>
18 #include <QFileDialog>
19 #include <QSettings>
20 #include <stellarsolver.h>
21
22 namespace Ekos
23 {
StellarSolverProfileEditor(QWidget * parent,ProfileGroup group,KConfigDialog * dialog)24 StellarSolverProfileEditor::StellarSolverProfileEditor(QWidget *parent, ProfileGroup group,
25 KConfigDialog *dialog) : QWidget(KStars::Instance())
26 {
27 Q_UNUSED(parent);
28 setupUi(this);
29
30 //Get a pointer to the KConfigDialog
31 m_ConfigDialog = dialog;
32
33 setProfileGroup(group);
34 optionsProfileGroup->setEnabled(false);
35
36 /* // we may want to do this eventually
37 connect(optionsProfileGroup, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index){
38 setProfileGroup((ProfileGroup)index);
39 });
40 */
41
42 openProfile->setIcon(
43 QIcon::fromTheme("document-open"));
44 openProfile->setAttribute(Qt::WA_LayoutUsesWidgetRect);
45 connect(openProfile, &QPushButton::clicked, this, &StellarSolverProfileEditor::openSingleProfile);
46
47 saveProfile->setIcon(
48 QIcon::fromTheme("document-save"));
49 saveProfile->setAttribute(Qt::WA_LayoutUsesWidgetRect);
50 connect(saveProfile, &QPushButton::clicked, this, &StellarSolverProfileEditor::saveSingleProfile);
51
52 /* This is not implemented yet.
53 copyProfile->setIcon(
54 QIcon::fromTheme("edit-copy"));
55 copyProfile->setAttribute(Qt::WA_LayoutUsesWidgetRect);
56 */
57 copyProfile->setVisible(false); //until we enable it.
58
59 saveBackups->setIcon(
60 QIcon::fromTheme("document-save"));
61 saveBackups->setAttribute(Qt::WA_LayoutUsesWidgetRect);
62 connect(saveBackups, &QPushButton::clicked, this, &StellarSolverProfileEditor::saveBackupProfiles);
63
64 loadBackups->setIcon(
65 QIcon::fromTheme("document-open"));
66 loadBackups->setAttribute(Qt::WA_LayoutUsesWidgetRect);
67 connect(loadBackups, &QPushButton::clicked, this, &StellarSolverProfileEditor::openBackupProfiles);
68
69 reloadProfiles->setIcon(
70 QIcon::fromTheme("system-reboot"));
71 reloadProfiles->setAttribute(Qt::WA_LayoutUsesWidgetRect);
72 connect(reloadProfiles, &QPushButton::clicked, this, &StellarSolverProfileEditor::loadProfiles);
73
74 loadDefaults->setIcon(
75 QIcon::fromTheme("go-down"));
76 loadDefaults->setAttribute(Qt::WA_LayoutUsesWidgetRect);
77 connect(loadDefaults, &QPushButton::clicked, this, &StellarSolverProfileEditor::loadDefaultProfiles);
78
79 connect(addOptionProfile, &QAbstractButton::clicked, this, [this]()
80 {
81 bool ok;
82 QString name = QInputDialog::getText(this, tr("New Options Profile"),
83 tr("What would you like your profile to be called?"), QLineEdit::Normal,
84 "", &ok);
85 if (ok && !name.isEmpty())
86 {
87 disconnectOptionsProfileComboBox();
88 SSolver::Parameters params = getSettingsFromUI();
89 params.listName = name;
90 optionsList.append(params);
91 optionsProfile->addItem(name);
92 optionsProfile->setCurrentText(name);
93 openOptionsProfileNum = optionsProfile->count() - 1;
94 settingJustChanged();
95 connectOptionsProfileComboBox();
96 }
97 });
98
99 connect(removeOptionProfile, &QAbstractButton::clicked, this, [this]()
100 {
101 if(optionsList.count() == 0)
102 return;
103 int item = optionsProfile->currentIndex();
104 disconnectOptionsProfileComboBox();
105 optionsProfile->removeItem(item);
106 optionsList.removeAt(item);
107 if(optionsProfile->count() > 0)
108 {
109 if(item > optionsList.count() - 1) //So that it doesn't try to set the new list to a nonexistant one
110 item--;
111 openOptionsProfileNum = item;
112 loadOptionsProfileIgnoreOldSettings(item); //Because the old one no longer exists
113 }
114 settingJustChanged();
115 connectOptionsProfileComboBox();
116 });
117
118 connect(description, &QTextEdit::textChanged, this, &StellarSolverProfileEditor::settingJustChanged);
119
120 QList<QLineEdit *> lines = this->findChildren<QLineEdit *>();
121 for(QLineEdit *line : lines)
122 connect(line, &QLineEdit::textEdited, this, &StellarSolverProfileEditor::settingJustChanged);
123
124 QList<QCheckBox *> checks = this->findChildren<QCheckBox *>();
125 for(QCheckBox *check : checks)
126 connect(check, &QCheckBox::stateChanged, this, &StellarSolverProfileEditor::settingJustChanged);
127
128 QList<QComboBox *> combos = this->findChildren<QComboBox *>();
129 for(QComboBox *combo : combos)
130 {
131 if(combo != optionsProfile)
132 connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &StellarSolverProfileEditor::settingJustChanged);
133 }
134
135 QList<QSpinBox *> spins = this->findChildren<QSpinBox *>();
136 for(QSpinBox *spin : spins)
137 connect(spin, QOverload<int>::of(&QSpinBox::valueChanged), this, &StellarSolverProfileEditor::settingJustChanged);
138
139 if(selectedProfileGroup != AlignProfiles)
140 astrometryOptions->setVisible(false);
141 connect(m_ConfigDialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply()));
142 connect(m_ConfigDialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply()));
143 }
144
setProfileGroup(ProfileGroup group)145 void StellarSolverProfileEditor::setProfileGroup(ProfileGroup group)
146 {
147 selectedProfileGroup = group;
148 optionsProfileGroup->setCurrentIndex(static_cast<int>(group));
149 QString profileGroupFileName;
150 switch(selectedProfileGroup)
151 {
152 case AlignProfiles:
153 profileGroupFileName = "SavedAlignProfiles.ini";
154 break;
155 case FocusProfiles:
156 profileGroupFileName = "SavedFocusProfiles.ini";
157 break;
158 case GuideProfiles:
159 profileGroupFileName = "SavedGuideProfiles.ini";
160 break;
161 case HFRProfiles:
162 profileGroupFileName = "SavedHFRProfiles.ini";
163 break;
164 }
165
166 savedOptionsProfiles = QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath(profileGroupFileName);
167 loadProfiles();
168 }
169
connectOptionsProfileComboBox()170 void StellarSolverProfileEditor::connectOptionsProfileComboBox()
171 {
172 connect(optionsProfile, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
173 &StellarSolverProfileEditor::loadOptionsProfile);
174 }
175
disconnectOptionsProfileComboBox()176 void StellarSolverProfileEditor::disconnectOptionsProfileComboBox()
177 {
178 disconnect(optionsProfile, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
179 &StellarSolverProfileEditor::loadOptionsProfile);
180 }
181
settingJustChanged()182 void StellarSolverProfileEditor::settingJustChanged()
183 {
184 optionsAreSaved = false;
185 m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(true);
186 }
187
loadProfile(int profile)188 void StellarSolverProfileEditor::loadProfile(int profile)
189 {
190 optionsProfile->setCurrentIndex(profile);
191 }
192
loadOptionsProfile()193 void StellarSolverProfileEditor::loadOptionsProfile()
194 {
195 if(optionsProfile->count() == 0)
196 return;
197 if(openOptionsProfileNum < optionsList.count() && openOptionsProfileNum > 0)
198 {
199 SSolver::Parameters editorOptions = getSettingsFromUI();
200 SSolver::Parameters currentProfile = optionsList.at(openOptionsProfileNum);
201 if(!(editorOptions == currentProfile) || editorOptions.description != currentProfile.description)
202 {
203 editorOptions.listName = currentProfile.listName;
204 optionsList.replace(openOptionsProfileNum, editorOptions);
205 }
206 }
207 SSolver::Parameters newProfile = optionsList.at(optionsProfile->currentIndex());
208 QList<QWidget *> controls = this->findChildren<QWidget *>();
209 for(QWidget *control : controls)
210 control->blockSignals(true);
211 sendSettingsToUI(newProfile);
212 for(QWidget *control : controls)
213 control->blockSignals(false);
214 openOptionsProfileNum = optionsProfile->currentIndex();
215 }
216
loadOptionsProfileIgnoreOldSettings(int index)217 void StellarSolverProfileEditor::loadOptionsProfileIgnoreOldSettings(int index)
218 {
219 if(index >= optionsProfile->count())
220 return;
221 SSolver::Parameters newProfile = optionsList.at(index);
222 QList<QWidget *> controls = this->findChildren<QWidget *>();
223 for(QWidget *control : controls)
224 control->blockSignals(true);
225 sendSettingsToUI(newProfile);
226 optionsProfile->setCurrentIndex(index);
227 openOptionsProfileNum = optionsProfile->currentIndex();
228 for(QWidget *control : controls)
229 control->blockSignals(false);
230 }
231
232 //This sets all the settings for either the internal or external sextractor
233 //based on the requested settings in the mainwindow interface.
234 //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.
getSettingsFromUI()235 SSolver::Parameters StellarSolverProfileEditor::getSettingsFromUI()
236 {
237 SSolver::Parameters params;
238 params.description = description->toPlainText();
239 //These are to pass the parameters to the internal sextractor
240 params.apertureShape = (SSolver::Shape) apertureShape->currentIndex();
241 params.kron_fact = kron_fact->text().toDouble();
242 params.subpix = subpix->text().toInt() ;
243 params.r_min = r_min->text().toFloat();
244 //params.inflags
245 params.magzero = magzero->text().toFloat();
246 params.minarea = minarea->text().toFloat();
247 params.deblend_thresh = deblend_thresh->text().toInt();
248 params.deblend_contrast = deblend_contrast->text().toFloat();
249 params.clean = (cleanCheckBox->isChecked()) ? 1 : 0;
250 params.clean_param = clean_param->text().toDouble();
251 StellarSolver::createConvFilterFromFWHM(¶ms, fwhm->value());
252
253 //Star Filter Settings
254 params.resort = resort->isChecked();
255 params.maxSize = maxSize->text().toDouble();
256 params.minSize = minSize->text().toDouble();
257 params.maxEllipse = maxEllipse->text().toDouble();
258 params.initialKeep = initialKeep->text().toInt();
259 params.keepNum = keepNum->text().toInt();
260 params.removeBrightest = brightestPercent->text().toDouble();
261 params.removeDimmest = dimmestPercent->text().toDouble();
262 params.saturationLimit = saturationLimit->text().toDouble();
263
264 //Settings that usually get set by the config file
265
266 params.maxwidth = maxWidth->text().toDouble();
267 params.minwidth = minWidth->text().toDouble();
268 params.inParallel = inParallel->isChecked();
269 params.multiAlgorithm = (SSolver::MultiAlgo)multiAlgo->currentIndex();
270 params.solverTimeLimit = solverTimeLimit->text().toInt();
271
272 params.resort = resort->isChecked();
273 params.autoDownsample = autoDownsample->isChecked();
274 params.downsample = downsample->value();
275 params.search_radius = radius->text().toDouble();
276
277 return params;
278 }
279
sendSettingsToUI(SSolver::Parameters a)280 void StellarSolverProfileEditor::sendSettingsToUI(SSolver::Parameters a)
281 {
282
283 description->setText(a.description);
284 //Sextractor Settings
285
286 apertureShape->setCurrentIndex(a.apertureShape);
287 kron_fact->setText(QString::number(a.kron_fact));
288 subpix->setText(QString::number(a.subpix));
289 r_min->setText(QString::number(a.r_min));
290
291 magzero->setText(QString::number(a.magzero));
292 minarea->setText(QString::number(a.minarea));
293 deblend_thresh->setText(QString::number(a.deblend_thresh));
294 deblend_contrast->setText(QString::number(a.deblend_contrast));
295 cleanCheckBox->setChecked(a.clean == 1);
296 clean_param->setText(QString::number(a.clean_param));
297 fwhm->setValue(a.fwhm);
298
299 //Star Filter Settings
300
301 maxSize->setText(QString::number(a.maxSize));
302 minSize->setText(QString::number(a.minSize));
303 maxEllipse->setText(QString::number(a.maxEllipse));
304 initialKeep->setText(QString::number(a.initialKeep));
305 keepNum->setText(QString::number(a.keepNum));
306 brightestPercent->setText(QString::number(a.removeBrightest));
307 dimmestPercent->setText(QString::number(a.removeDimmest));
308 saturationLimit->setText(QString::number(a.saturationLimit));
309
310 //Astrometry Settings
311
312 autoDownsample->setChecked(a.autoDownsample);
313 downsample->setValue(a.downsample);
314 inParallel->setChecked(a.inParallel);
315 multiAlgo->setCurrentIndex(a.multiAlgorithm);
316 solverTimeLimit->setText(QString::number(a.solverTimeLimit));
317 minWidth->setText(QString::number(a.minwidth));
318 maxWidth->setText(QString::number(a.maxwidth));
319 radius->setText(QString::number(a.search_radius));
320 resort->setChecked(a.resort);
321 }
322
copySingleProfile()323 void StellarSolverProfileEditor::copySingleProfile()
324 {
325
326 }
327
openSingleProfile()328 void StellarSolverProfileEditor::openSingleProfile()
329 {
330 QString fileURL = QFileDialog::getOpenFileName(nullptr, "Load Options Profiles File", dirPath,
331 "INI files(*.ini)");
332 if (fileURL.isEmpty())
333 return;
334 if(!QFileInfo(fileURL).exists())
335 {
336 KSMessageBox::warning(this, "Message", "The file doesn't exist");
337 return;
338 }
339 QSettings settings(fileURL, QSettings::IniFormat);
340 QStringList groups = settings.childGroups();
341 for(QString group : groups)
342 {
343 settings.beginGroup(group);
344 QStringList keys = settings.childKeys();
345 QMap<QString, QVariant> map;
346 for(QString key : keys)
347 map.insert(key, settings.value(key));
348 SSolver::Parameters newParams = SSolver::Parameters::convertFromMap(map);
349 optionsList.append(newParams);
350 optionsProfile->addItem(group);
351 }
352 if(optionsProfile->count() > 0)
353 loadProfile(optionsProfile->count() - 1);
354 m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(true);
355 }
356
loadProfiles()357 void StellarSolverProfileEditor::loadProfiles()
358 {
359 if( !optionsAreSaved )
360 {
361 if(QMessageBox::question(this, "Abort?",
362 "You made unsaved changes in the settings, do you really wish to overwrite them?") == QMessageBox::No)
363 {
364 return;
365 }
366 optionsAreSaved = true; //They just got overwritten
367 }
368 disconnectOptionsProfileComboBox();
369 optionsProfile->clear();
370 if(QFile(savedOptionsProfiles).exists())
371 optionsList = StellarSolver::loadSavedOptionsProfiles(savedOptionsProfiles);
372 else
373 optionsList = getDefaultProfiles();
374 for(SSolver::Parameters params : optionsList)
375 optionsProfile->addItem(params.listName);
376 if(optionsList.count() > 0)
377 {
378 sendSettingsToUI(optionsList.at(0));
379 openOptionsProfileNum = 0;
380 }
381 connectOptionsProfileComboBox();
382 m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(false);
383 }
384
saveSingleProfile()385 void StellarSolverProfileEditor::saveSingleProfile()
386 {
387 QString fileURL = QFileDialog::getSaveFileName(nullptr, "Save Options Profiles", dirPath,
388 "INI files(*.ini)");
389 if (fileURL.isEmpty())
390 return;
391 QSettings settings(fileURL, QSettings::IniFormat);
392 SSolver::Parameters params = optionsList.at(optionsProfile->currentIndex());
393 settings.beginGroup(params.listName);
394 QMap<QString, QVariant> map = SSolver::Parameters::convertToMap(params);
395 QMapIterator<QString, QVariant> it(map);
396 while(it.hasNext())
397 {
398 it.next();
399 settings.setValue(it.key(), it.value());
400 }
401 settings.endGroup();
402 }
403
saveProfiles()404 void StellarSolverProfileEditor::saveProfiles()
405 {
406 QSettings settings(savedOptionsProfiles, QSettings::IniFormat);
407 for(int i = 0 ; i < optionsList.count(); i++)
408 {
409 SSolver::Parameters params = optionsList.at(i);
410 settings.beginGroup(params.listName);
411 QMap<QString, QVariant> map = SSolver::Parameters::convertToMap(params);
412 QMapIterator<QString, QVariant> it(map);
413 while(it.hasNext())
414 {
415 it.next();
416 settings.setValue(it.key(), it.value());
417 }
418 settings.endGroup();
419 }
420 QStringList groups = settings.childGroups();
421 for(QString group : groups)
422 {
423 bool groupInList = false;
424 for(SSolver::Parameters params : optionsList)
425 {
426 if(params.listName == group)
427 groupInList = true;
428 }
429 if( ! groupInList)
430 settings.remove(group);
431 }
432 }
433
getDefaultProfiles()434 QList<SSolver::Parameters> StellarSolverProfileEditor::getDefaultProfiles()
435 {
436 switch(selectedProfileGroup)
437 {
438 case FocusProfiles:
439 return getDefaultFocusOptionsProfiles();
440 case HFRProfiles:
441 return getDefaultHFROptionsProfiles();
442 case GuideProfiles:
443 return getDefaultGuideOptionsProfiles();
444 default:
445 case AlignProfiles:
446 return getDefaultAlignOptionsProfiles();
447 }
448 }
449
loadDefaultProfiles()450 void StellarSolverProfileEditor::loadDefaultProfiles()
451 {
452 if( !optionsAreSaved )
453 {
454 if(QMessageBox::question(this, "Abort?",
455 "You made unsaved changes in the settings, do you really wish to overwrite them?") == QMessageBox::No)
456 {
457 return;
458 }
459 optionsAreSaved = true; //They just got overwritten
460 }
461 disconnectOptionsProfileComboBox();
462 optionsList = getDefaultProfiles();
463 optionsProfile->clear();
464 for(SSolver::Parameters param : optionsList)
465 optionsProfile->addItem(param.listName);
466 connectOptionsProfileComboBox();
467 if(optionsProfile->count() > 0)
468 loadOptionsProfileIgnoreOldSettings(0);
469 m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(true);
470 }
471
saveBackupProfiles()472 void StellarSolverProfileEditor::saveBackupProfiles()
473 {
474 QString fileURL = QFileDialog::getSaveFileName(nullptr, "Save Options Profiles", dirPath,
475 "INI files(*.ini)");
476 if (fileURL.isEmpty())
477 return;
478 QSettings settings(fileURL, QSettings::IniFormat);
479 for(int i = 0 ; i < optionsList.count(); i++)
480 {
481 SSolver::Parameters params = optionsList.at(i);
482 settings.beginGroup(params.listName);
483 QMap<QString, QVariant> map = SSolver::Parameters::convertToMap(params);
484 QMapIterator<QString, QVariant> it(map);
485 while(it.hasNext())
486 {
487 it.next();
488 settings.setValue(it.key(), it.value());
489 }
490 settings.endGroup();
491 }
492 }
493
openBackupProfiles()494 void StellarSolverProfileEditor::openBackupProfiles()
495 {
496 QString fileURL = QFileDialog::getOpenFileName(nullptr, "Load Options Profiles File", dirPath,
497 "INI files(*.ini)");
498 if (fileURL.isEmpty())
499 return;
500 if(!QFileInfo(fileURL).exists())
501 {
502 QMessageBox::warning(this, "Message", "The file doesn't exist");
503 return;
504 }
505 disconnectOptionsProfileComboBox();
506 optionsList.clear();
507 QSettings settings(fileURL, QSettings::IniFormat);
508 QStringList groups = settings.childGroups();
509 for(QString group : groups)
510 {
511 settings.beginGroup(group);
512 QStringList keys = settings.childKeys();
513 QMap<QString, QVariant> map;
514 for(QString key : keys)
515 map.insert(key, settings.value(key));
516 SSolver::Parameters newParams = SSolver::Parameters::convertFromMap(map);
517 optionsList.append(newParams);
518 optionsProfile->addItem(group);
519 }
520 if(optionsProfile->count() > 0)
521 loadOptionsProfileIgnoreOldSettings(0);
522 connectOptionsProfileComboBox();
523 m_ConfigDialog->button(QDialogButtonBox::Apply)->setEnabled(true);
524 }
525
slotApply()526 void StellarSolverProfileEditor::slotApply()
527 {
528 int index = optionsProfile->currentIndex();
529 SSolver::Parameters currentParams = getSettingsFromUI();
530 SSolver::Parameters savedParams = optionsList.at(index);
531 if(!(currentParams == savedParams) || currentParams.description != savedParams.description)
532 {
533 currentParams.listName = savedParams.listName;
534 optionsList.replace(index, currentParams);
535 }
536 optionsAreSaved = true;
537
538 saveProfiles();
539 emit optionsProfilesUpdated();
540 }
541
542 }
543