10 #include "fitshistogrameditor.h"
11 #include "fitshistogramcommand.h"
13 #include "fitsviewer.h"
14 #include "ksnotification.h"
17 #include "ui_fitsheaderdialog.h"
18 #include "ui_statform.h"
20 #include <KMessageBox>
21 #include <QtConcurrent>
24 #include <fits_debug.h>
28 const char kAutoToolTip[] =
"Automatically find stretch parameters";
29 const char kStretchOffToolTip[] =
"Stretch the image";
30 const char kStretchOnToolTip[] =
"Disable stretching of the image.";
37 undoStack->setUndoLimit(10);
39 connect(undoStack, SIGNAL(cleanChanged(
bool)),
this, SLOT(modifyFITSState(
bool)));
42 fitsHeaderDialog =
new QDialog(
this);
43 m_HistogramEditor =
new FITSHistogramEditor(
this);
44 connect(m_HistogramEditor, &FITSHistogramEditor::newHistogramCommand, [
this](FITSHistogramCommand * command)
46 undoStack->push(command);
57 void FITSTab::saveUnsaved()
59 if (undoStack->isClean() || m_View->getMode() != FITS_NORMAL)
63 QString message =
i18n(
"The current FITS file has unsaved changes. Would you like to save before closing it?");
79 if (undoStack->isClean())
84 QString FITSTab::getPreviewText()
const
89 void FITSTab::setPreviewText(
const QString &value)
94 void FITSTab::selectRecentFITS(
int i)
99 void FITSTab::clearRecentFITS()
102 recentImages->clear();
111 void setSlider(
QSlider *slider,
QLabel *label,
float value,
float maxValue,
bool adjustSlider)
114 slider->
setValue(
static_cast<int>(value * 10000 / maxValue));
116 label->setText(valStr);
131 label->setText(name);
133 label->setFont(font);
159 void FITSTab::setStretchUIValues(
bool adjustSliders)
161 StretchParams1Channel params = m_View->getStretchParams().grey_red;
162 setSlider(shadowsSlider.get(), shadowsVal.get(), params.shadows, maxShadows, adjustSliders);
163 setSlider(midtonesSlider.get(), midtonesVal.get(), params.midtones, maxMidtones, adjustSliders);
164 setSlider(highlightsSlider.get(), highlightsVal.get(), params.highlights, maxHighlights, adjustSliders);
167 bool stretchActive = m_View->isImageStretched();
170 stretchButton->setChecked(
true);
171 stretchButton->setToolTip(kStretchOnToolTip);
175 stretchButton->setChecked(
false);
176 stretchButton->setToolTip(kStretchOffToolTip);
180 if (stretchActive && !m_View->getAutoStretch())
182 autoButton->setEnabled(
true);
184 autoButton->setIconSize(
QSize(22, 22));
185 autoButton->setToolTip(kAutoToolTip);
189 autoButton->setEnabled(
false);
190 autoButton->setIcon(
QIcon());
191 autoButton->setIconSize(
QSize(22, 22));
192 autoButton->setToolTip(
"");
194 autoButton->setChecked(m_View->getAutoStretch());
197 shadowsSlider->setEnabled(stretchActive);
198 shadowsVal->setEnabled(stretchActive);
199 shadowsLabel->setEnabled(stretchActive);
200 midtonesSlider->setEnabled(stretchActive);
201 midtonesVal->setEnabled(stretchActive);
202 midtonesLabel->setEnabled(stretchActive);
203 highlightsSlider->setEnabled(stretchActive);
204 highlightsVal->setEnabled(stretchActive);
205 highlightsLabel->setEnabled(stretchActive);
209 void FITSTab::rescaleShadows()
212 StretchParams1Channel params = m_View->getStretchParams().grey_red;
213 maxShadows = std::max(0.002f, std::min(1.0f, params.shadows * 2.0f));
214 setStretchUIValues(
true);
218 void FITSTab::rescaleMidtones()
221 StretchParams1Channel params = m_View->getStretchParams().grey_red;
222 maxMidtones = std::max(.002f, std::min(1.0f, params.midtones * 2.0f));
223 setStretchUIValues(
true);
228 constexpr
int fontSize = 12;
233 setupStretchButton(stretchButton.get(),
"transform-move", kStretchOffToolTip, stretchBarLayout);
236 shadowsLabel.reset(
new QLabel());
237 shadowsVal.reset(
new QLabel());
239 setupStretchSlider(shadowsSlider.get(), shadowsLabel.get(), shadowsVal.get(), fontSize,
"Shadows", stretchBarLayout);
242 midtonesLabel.reset(
new QLabel());
243 midtonesVal.reset(
new QLabel());
245 setupStretchSlider(midtonesSlider.get(), midtonesLabel.get(), midtonesVal.get(), fontSize,
"Midtones", stretchBarLayout);
248 highlightsLabel.reset(
new QLabel());
249 highlightsVal.reset(
new QLabel());
251 setupStretchSlider(highlightsSlider.get(), highlightsLabel.get(), highlightsVal.get(), fontSize,
"Highlights",
261 setupStretchButton(autoButton.get(),
"tools-wizard", kAutoToolTip, stretchBarLayout);
266 m_View->setStretch(!m_View->isImageStretched());
272 StretchParams params = m_View->getStretchParams();
273 params.grey_red.shadows = this->maxShadows * value / 10000.0f;
274 m_View->setPreviewSampling(Options::stretchPreviewSampling());
275 m_View->setStretchParams(params);
276 m_View->setPreviewSampling(0);
280 StretchParams params = m_View->getStretchParams();
281 params.grey_red.midtones = this->maxMidtones * value / 10000.0f;
282 m_View->setPreviewSampling(Options::stretchPreviewSampling());
283 m_View->setStretchParams(params);
284 m_View->setPreviewSampling(0);
288 StretchParams params = m_View->getStretchParams();
289 params.grey_red.highlights = this->maxHighlights * value / 10000.0f;
290 m_View->setPreviewSampling(Options::stretchPreviewSampling());
291 m_View->setStretchParams(params);
292 m_View->setPreviewSampling(0);
300 StretchParams params = m_View->getStretchParams();
301 m_View->setStretchParams(params);
307 StretchParams params = m_View->getStretchParams();
308 m_View->setStretchParams(params);
313 StretchParams params = m_View->getStretchParams();
314 m_View->setStretchParams(params);
322 if (!m_View->getAutoStretch())
323 m_View->setAutoStretchParams();
325 KMessageBox::information(this,
"You are already using automatic stretching. To manually stretch, drag a slider.");
326 setStretchUIValues(false);
331 connect(m_View.get(), &FITSView::newStatus, [ = ](
const QString & ignored)
334 bool slidersInUse = shadowsSlider->isSliderDown() || midtonesSlider->isSliderDown() ||
335 highlightsSlider->isSliderDown();
341 setStretchUIValues(!slidersInUse);
344 return stretchBarLayout;
347 bool FITSTab::setupView(FITSMode mode, FITSScale filter)
351 m_View.reset(
new FITSView(
this, mode, filter));
358 stat.setupUi(statWidget);
360 for (
int i = 0; i <= STAT_STDDEV; i++)
362 for (
int j = 0; j < 3; j++)
370 stat.statsTable->setSpan(i, 0, 1, 3);
373 fitsTools->addItem(statWidget,
i18n(
"Statistics"));
375 fitsTools->addItem(m_HistogramEditor,
i18n(
"Histogram"));
377 header.setupUi(fitsHeaderDialog);
378 fitsTools->addItem(fitsHeaderDialog,
i18n(
"FITS Header"));
382 recentPanel->
setLayout(recentPanelLayout);
383 fitsTools->addItem(recentPanel,
i18n(
"Recent Images"));
385 recentPanelLayout->
addWidget(recentImages);
387 recentPanelLayout->
addWidget(clearRecent);
395 fitsSplitter->addWidget(scrollFitsPanel);
396 fitsSplitter->addWidget(m_View.get());
400 fitsSplitter->setSizes(
QList<int>() << 0 << m_View->width() );
408 connect(m_View.get(), &FITSView::newStatus,
this, &FITSTab::newStatus);
409 connect(m_View.get(), &FITSView::debayerToggled,
this, &FITSTab::debayerToggled);
412 connect(m_View.get(), &FITSView::failed,
this, &FITSTab::failed);
421 void FITSTab::loadFile(
const QUrl &imageURL, FITSMode mode, FITSScale filter)
427 if (setupView(mode, filter))
431 connect(m_View.get(), &FITSView::loaded, [&]()
439 modifyFITSState(
true, imageURL);
441 currentURL = imageURL;
443 m_View->setFilter(filter);
448 bool FITSTab::shouldComputeHFR()
const
450 if (viewer->isStarsMarked())
452 if (!Options::autoHFR())
454 return ((!m_View.isNull()) && (m_View->getMode() == FITS_NORMAL));
457 void FITSTab::processData()
461 m_HistogramEditor->setImageData(imageData);
471 if (shouldComputeHFR())
473 m_View->searchStars();
474 qCDebug(KSTARS_FITS) <<
"FITS HFR:" << imageData->getHFR();
482 if (recentImages->findItems(currentURL.toLocalFile(),
Qt::MatchExactly).count() == 0)
488 &FITSTab::selectRecentFITS);
489 recentImages->addItem(imageData->filename());
490 recentImages->setCurrentRow(recentImages->count() - 1);
492 &FITSTab::selectRecentFITS);
500 if (viewer->isStarsMarked())
502 m_View->toggleStars(
true);
503 m_View->updateFrame();
512 setupView(mode, filter);
517 if (viewer->isStarsMarked())
519 m_View->toggleStars(
true);
523 m_View->setFilter(filter);
525 if (!m_View->loadData(data))
536 void FITSTab::modifyFITSState(
bool clean,
const QUrl &imageURL)
540 if (undoStack->isClean() ==
false)
541 undoStack->setClean();
548 emit changeStatus(clean, imageURL);
551 bool FITSTab::saveImage(
const QString &filename)
553 return m_View->saveImage(filename);
556 void FITSTab::copyFITS()
561 void FITSTab::histoFITS()
574 fitsTools->setCurrentIndex(1);
575 if(m_View->width() > 200)
576 fitsSplitter->setSizes(
QList<int>() << 200 << m_View->width() - 200);
578 fitsSplitter->setSizes(
QList<int>() << 50 << 50);
581 void FITSTab::evaluateStats()
590 if (imageData->channels() == 1)
592 for (
int i = STAT_MIN; i <= STAT_STDDEV; i++)
594 if (
stat.statsTable->columnSpan(i, 0) != 3)
595 stat.statsTable->setSpan(i, 0, 1, 3);
598 stat.statsTable->horizontalHeaderItem(0)->setText(
i18n(
"Value"));
599 stat.statsTable->hideColumn(1);
600 stat.statsTable->hideColumn(2);
604 for (
int i = STAT_MIN; i <= STAT_STDDEV; i++)
606 if (
stat.statsTable->columnSpan(i, 0) != 1)
607 stat.statsTable->setSpan(i, 0, 1, 1);
610 stat.statsTable->horizontalHeaderItem(0)->setText(
i18nc(
"Red",
"R"));
611 stat.statsTable->showColumn(1);
612 stat.statsTable->showColumn(2);
615 if (!Options::nonLinearHistogram() && !imageData->isHistogramConstructed())
616 imageData->constructHistogram();
618 for (
int i = 0; i < imageData->channels(); i++)
620 stat.statsTable->item(STAT_MIN, i)->setText(
QString::number(imageData->getMin(i),
'f', 3));
621 stat.statsTable->item(STAT_MAX, i)->setText(
QString::number(imageData->getMax(i),
'f', 3));
622 stat.statsTable->item(STAT_MEAN, i)->setText(
QString::number(imageData->getMean(i),
'f', 3));
623 stat.statsTable->item(STAT_MEDIAN, i)->setText(
QString::number(imageData->getMedian(i),
'f', 3));
624 stat.statsTable->item(STAT_STDDEV, i)->setText(
QString::number(imageData->getStdDev(i),
'f', 3));
628 void FITSTab::statFITS()
630 fitsTools->setCurrentIndex(0);
631 if(m_View->width() > 200)
632 fitsSplitter->setSizes(
QList<int>() << 200 << m_View->width() - 200);
634 fitsSplitter->setSizes(
QList<int>() << 50 << 50);
638 void FITSTab::loadFITSHeader()
642 int nkeys = imageData->getRecords().size();
644 header.tableWidget->setRowCount(nkeys);
645 for (
const auto &oneRecord : imageData->getRecords())
649 header.tableWidget->setItem(counter, 0, tempItem);
652 header.tableWidget->setItem(counter, 1, tempItem);
655 header.tableWidget->setItem(counter, 2, tempItem);
659 header.tableWidget->setColumnWidth(0, 100);
660 header.tableWidget->setColumnWidth(1, 100);
661 header.tableWidget->setColumnWidth(2, 250);
664 void FITSTab::headerFITS()
666 fitsTools->setCurrentIndex(2);
667 if(m_View->width() > 200)
668 fitsSplitter->setSizes(
QList<int>() << 200 << m_View->width() - 200);
670 fitsSplitter->setSizes(
QList<int>() << 50 << 50);
673 bool FITSTab::saveFile()
675 QUrl backupCurrent = currentURL;
676 QUrl currentDir(Options::fitsDir());
677 currentDir.setScheme(
"file");
679 if (currentURL.toLocalFile().startsWith(
QLatin1String(
"/tmp/")) || currentURL.toLocalFile().contains(
"/Temp"))
683 if (mDirty ==
false && !currentURL.isEmpty())
686 if (currentURL.isEmpty())
689 #ifdef Q_OS_OSX //For some reason, the other code caused KStars to crash on MacOS
692 "Images (*.fits *.fits.gz *.fit *.xisf *.jpg *.jpeg *.png)");
696 "FITS (*.fits *.fits.gz *.fit);;XISF (*.xisf);;JPEG (*.jpg *.jpeg);;PNG (*.png)", &selectedFilter);
699 if (currentURL.isEmpty())
701 currentURL = backupCurrent;
706 if (currentURL.toLocalFile().contains(
'.') == 0)
708 if (selectedFilter.
contains(
"XISF"))
709 currentURL.
setPath(currentURL.toLocalFile() +
".xisf");
710 else if (selectedFilter.
contains(
"JPEG"))
711 currentURL.setPath(currentURL.toLocalFile() +
".jpg");
712 else if (selectedFilter.
contains(
"PNG"))
713 currentURL.setPath(currentURL.toLocalFile() +
".png");
715 currentURL.setPath(currentURL.toLocalFile() +
".fits");
719 if (currentURL.isValid())
721 QString localFile = currentURL.toLocalFile();
725 if (!saveImage(localFile))
727 KSNotification::error(
i18n(
"Image save error: %1", m_View->imageData()->getLastError()),
i18n(
"Image Save"));
731 emit newStatus(
i18n(
"File saved to %1", currentURL.url()), FITS_MESSAGE);
738 KSNotification::sorry(
message,
i18n(
"Invalid URL"));
743 bool FITSTab::saveFileAs()
749 void FITSTab::ZoomIn()
751 QPoint oldCenter = m_View->getImagePoint(m_View->viewport()->rect().center());
753 m_View->cleanUpZoom(oldCenter);
756 void FITSTab::ZoomOut()
758 QPoint oldCenter = m_View->getImagePoint(m_View->viewport()->rect().center());
760 m_View->cleanUpZoom(oldCenter);
763 void FITSTab::ZoomDefault()
765 QPoint oldCenter = m_View->getImagePoint(m_View->viewport()->rect().center());
766 m_View->ZoomDefault();
767 m_View->cleanUpZoom(oldCenter);
770 void FITSTab::tabPositionUpdated()
772 undoStack->setActive(
true);
773 emit newStatus(
QString(
"%1%").arg(m_View->getCurrentZoom()), FITS_ZOOM);
774 emit newStatus(
QString(
"%1x%2").arg(m_View->imageData()->width()).
arg(m_View->imageData()->height()),