Kstars

fitstab.cpp
1 /*
2  SPDX-FileCopyrightText: 2012 Jasem Mutlaq <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "fitstab.h"
8 
9 #include "fitsdata.h"
10 #include "fitshistogrameditor.h"
11 #include "fitshistogramcommand.h"
12 #include "fitsview.h"
13 #include "fitsviewer.h"
14 #include "ksnotification.h"
15 #include "kstars.h"
16 #include "Options.h"
17 #include "ui_fitsheaderdialog.h"
18 #include "ui_statform.h"
19 
20 #include <KMessageBox>
21 #include <QtConcurrent>
22 #include <QIcon>
23 
24 #include <fits_debug.h>
25 
26 namespace
27 {
28 const char kAutoToolTip[] = "Automatically find stretch parameters";
29 const char kStretchOffToolTip[] = "Stretch the image";
30 const char kStretchOnToolTip[] = "Disable stretching of the image.";
31 } // namespace
32 
33 FITSTab::FITSTab(FITSViewer *parent) : QWidget(parent)
34 {
35  viewer = parent;
36  undoStack = new QUndoStack(this);
37  undoStack->setUndoLimit(10);
38  undoStack->clear();
39  connect(undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(modifyFITSState(bool)));
40 
41  statWidget = new QDialog(this);
42  fitsHeaderDialog = new QDialog(this);
43  m_HistogramEditor = new FITSHistogramEditor(this);
44  connect(m_HistogramEditor, &FITSHistogramEditor::newHistogramCommand, [this](FITSHistogramCommand * command)
45  {
46  undoStack->push(command);
47  });
48 }
49 
50 FITSTab::~FITSTab()
51 {
52  // Make sure it's done
53  //histogramFuture.waitForFinished();
54  //disconnect();
55 }
56 
57 void FITSTab::saveUnsaved()
58 {
59  if (undoStack->isClean() || m_View->getMode() != FITS_NORMAL)
60  return;
61 
62  QString caption = i18n("Save Changes to FITS?");
63  QString message = i18n("The current FITS file has unsaved changes. Would you like to save before closing it?");
64 
66  if (ans == KMessageBox::Yes)
67  saveFile();
68  if (ans == KMessageBox::No)
69  {
70  undoStack->clear();
71  modifyFITSState();
72  }
73 }
74 
75 void FITSTab::closeEvent(QCloseEvent *ev)
76 {
77  saveUnsaved();
78 
79  if (undoStack->isClean())
80  ev->accept();
81  else
82  ev->ignore();
83 }
84 QString FITSTab::getPreviewText() const
85 {
86  return previewText;
87 }
88 
89 void FITSTab::setPreviewText(const QString &value)
90 {
91  previewText = value;
92 }
93 
94 void FITSTab::selectRecentFITS(int i)
95 {
96  loadFile(QUrl::fromLocalFile(recentImages->item(i)->text()));
97 }
98 
99 void FITSTab::clearRecentFITS()
100 {
101  disconnect(recentImages, &QListWidget::currentRowChanged, this, &FITSTab::selectRecentFITS);
102  recentImages->clear();
103  connect(recentImages, &QListWidget::currentRowChanged, this, &FITSTab::selectRecentFITS);
104 }
105 
106 namespace
107 {
108 
109 // Sets the text value in the slider's value display, and if adjustSlider is true,
110 // moves the slider to the correct position.
111 void setSlider(QSlider *slider, QLabel *label, float value, float maxValue, bool adjustSlider)
112 {
113  if (adjustSlider)
114  slider->setValue(static_cast<int>(value * 10000 / maxValue));
115  QString valStr = QString("%1").arg(static_cast<double>(value), 5, 'f', 4);
116  label->setText(valStr);
117 }
118 
119 // Adds the following to a horizontal layout (left to right): a vertical line,
120 // a label with the slider's name, a slider, and a text field to display the slider's value.
121 void setupStretchSlider(QSlider *slider, QLabel *label, QLabel *val, int fontSize,
122  const QString &name, QHBoxLayout *layout)
123 {
124  QFrame* line = new QFrame();
127  layout->addWidget(line);
128  QFont font = label->font();
129  font.setPointSize(fontSize);
130 
131  label->setText(name);
133  label->setFont(font);
134  layout->addWidget(label);
135  slider->setMinimum(0);
136  slider->setMaximum(10000);
138  layout->addWidget(slider);
140  val->setFont(font);
141  layout->addWidget(val);
142 }
143 
144 // Adds a button with the icon and tooltip to the layout.
145 void setupStretchButton(QPushButton *button, const QString &iconName, const QString &tip, QHBoxLayout *layout)
146 {
147  button->setIcon(QIcon::fromTheme(iconName));
148  button->setIconSize(QSize(22, 22));
149  button->setToolTip(tip);
150  button->setCheckable(true);
151  button->setChecked(true);
153  layout->addWidget(button);
154 }
155 
156 } // namespace
157 
158 // Updates all the widgets in the stretch area to display the view's stretch parameters.
159 void FITSTab::setStretchUIValues(bool adjustSliders)
160 {
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);
165 
166 
167  bool stretchActive = m_View->isImageStretched();
168  if (stretchActive)
169  {
170  stretchButton->setChecked(true);
171  stretchButton->setToolTip(kStretchOnToolTip);
172  }
173  else
174  {
175  stretchButton->setChecked(false);
176  stretchButton->setToolTip(kStretchOffToolTip);
177  }
178 
179  // Only activate the auto button if stretching is on and auto-stretching is not set.
180  if (stretchActive && !m_View->getAutoStretch())
181  {
182  autoButton->setEnabled(true);
183  autoButton->setIcon(QIcon::fromTheme("tools-wizard"));
184  autoButton->setIconSize(QSize(22, 22));
185  autoButton->setToolTip(kAutoToolTip);
186  }
187  else
188  {
189  autoButton->setEnabled(false);
190  autoButton->setIcon(QIcon());
191  autoButton->setIconSize(QSize(22, 22));
192  autoButton->setToolTip("");
193  }
194  autoButton->setChecked(m_View->getAutoStretch());
195 
196  // Disable most of the UI if stretching is not active.
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);
206 }
207 
208 // Adjusts the maxShadows value so that we have room to adjust the slider.
209 void FITSTab::rescaleShadows()
210 {
211  if (!m_View) return;
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);
215 }
216 
217 // Adjusts the maxMidtones value so that we have room to adjust the slider.
218 void FITSTab::rescaleMidtones()
219 {
220  if (!m_View) return;
221  StretchParams1Channel params = m_View->getStretchParams().grey_red;
222  maxMidtones = std::max(.002f, std::min(1.0f, params.midtones * 2.0f));
223  setStretchUIValues(true);
224 }
225 
226 QHBoxLayout* FITSTab::setupStretchBar()
227 {
228  constexpr int fontSize = 12;
229 
230  QHBoxLayout *stretchBarLayout = new QHBoxLayout();
231 
232  stretchButton.reset(new QPushButton());
233  setupStretchButton(stretchButton.get(), "transform-move", kStretchOffToolTip, stretchBarLayout);
234 
235  // Shadows
236  shadowsLabel.reset(new QLabel());
237  shadowsVal.reset(new QLabel());
238  shadowsSlider.reset(new QSlider(Qt::Horizontal, this));
239  setupStretchSlider(shadowsSlider.get(), shadowsLabel.get(), shadowsVal.get(), fontSize, "Shadows", stretchBarLayout);
240 
241  // Midtones
242  midtonesLabel.reset(new QLabel());
243  midtonesVal.reset(new QLabel());
244  midtonesSlider.reset(new QSlider(Qt::Horizontal, this));
245  setupStretchSlider(midtonesSlider.get(), midtonesLabel.get(), midtonesVal.get(), fontSize, "Midtones", stretchBarLayout);
246 
247  // Highlights
248  highlightsLabel.reset(new QLabel());
249  highlightsVal.reset(new QLabel());
250  highlightsSlider.reset(new QSlider(Qt::Horizontal, this));
251  setupStretchSlider(highlightsSlider.get(), highlightsLabel.get(), highlightsVal.get(), fontSize, "Highlights",
252  stretchBarLayout);
253 
254  // Separator
255  QFrame* line4 = new QFrame();
258  stretchBarLayout->addWidget(line4);
259 
260  autoButton.reset(new QPushButton());
261  setupStretchButton(autoButton.get(), "tools-wizard", kAutoToolTip, stretchBarLayout);
262 
263  connect(stretchButton.get(), &QPushButton::clicked, [ = ]()
264  {
265  // This will toggle whether we're currently stretching.
266  m_View->setStretch(!m_View->isImageStretched());
267  });
268 
269  // Make rough displays for the slider movement.
270  connect(shadowsSlider.get(), &QSlider::sliderMoved, [ = ](int value)
271  {
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);
277  });
278  connect(midtonesSlider.get(), &QSlider::sliderMoved, [ = ](int value)
279  {
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);
285  });
286  connect(highlightsSlider.get(), &QSlider::sliderMoved, [ = ](int value)
287  {
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);
293  });
294 
295  // Make a final full-res display when the slider is released.
296  connect(shadowsSlider.get(), &QSlider::sliderReleased, [ = ]()
297  {
298  if (!m_View) return;
299  rescaleShadows();
300  StretchParams params = m_View->getStretchParams();
301  m_View->setStretchParams(params);
302  });
303  connect(midtonesSlider.get(), &QSlider::sliderReleased, [ = ]()
304  {
305  if (!m_View) return;
306  rescaleMidtones();
307  StretchParams params = m_View->getStretchParams();
308  m_View->setStretchParams(params);
309  });
310  connect(highlightsSlider.get(), &QSlider::sliderReleased, [ = ]()
311  {
312  if (!m_View) return;
313  StretchParams params = m_View->getStretchParams();
314  m_View->setStretchParams(params);
315  });
316 
317  connect(autoButton.get(), &QPushButton::clicked, [ = ]()
318  {
319  // If we're not currently using automatic stretch parameters, turn that on.
320  // If we're already using automatic parameters, don't do anything.
321  // User can just move the sliders to take manual control.
322  if (!m_View->getAutoStretch())
323  m_View->setAutoStretchParams();
324  else
325  KMessageBox::information(this, "You are already using automatic stretching. To manually stretch, drag a slider.");
326  setStretchUIValues(false);
327  });
328 
329  // This is mostly useful right at the start, when the image is displayed without any user interaction.
330  // Check for slider-in-use, as we don't wont to rescale while the user is active.
331  connect(m_View.get(), &FITSView::newStatus, [ = ](const QString & ignored)
332  {
333  Q_UNUSED(ignored)
334  bool slidersInUse = shadowsSlider->isSliderDown() || midtonesSlider->isSliderDown() ||
335  highlightsSlider->isSliderDown();
336  if (!slidersInUse)
337  {
338  rescaleShadows();
339  rescaleMidtones();
340  }
341  setStretchUIValues(!slidersInUse);
342  });
343 
344  return stretchBarLayout;
345 }
346 
347 bool FITSTab::setupView(FITSMode mode, FITSScale filter)
348 {
349  if (m_View.isNull())
350  {
351  m_View.reset(new FITSView(this, mode, filter));
352  m_View->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
353  QVBoxLayout *vlayout = new QVBoxLayout();
354 
355  fitsSplitter = new QSplitter(Qt::Horizontal, this);
356  fitsTools = new QToolBox();
357 
358  stat.setupUi(statWidget);
359 
360  for (int i = 0; i <= STAT_STDDEV; i++)
361  {
362  for (int j = 0; j < 3; j++)
363  {
364  stat.statsTable->setItem(i, j, new QTableWidgetItem());
365  stat.statsTable->item(i, j)->setTextAlignment(Qt::AlignHCenter);
366  }
367 
368  // Set col span for items up to HFR
369  if (i <= STAT_HFR)
370  stat.statsTable->setSpan(i, 0, 1, 3);
371  }
372 
373  fitsTools->addItem(statWidget, i18n("Statistics"));
374 
375  fitsTools->addItem(m_HistogramEditor, i18n("Histogram"));
376 
377  header.setupUi(fitsHeaderDialog);
378  fitsTools->addItem(fitsHeaderDialog, i18n("FITS Header"));
379 
380  QVBoxLayout *recentPanelLayout = new QVBoxLayout();
381  QWidget *recentPanel = new QWidget(fitsSplitter);
382  recentPanel->setLayout(recentPanelLayout);
383  fitsTools->addItem(recentPanel, i18n("Recent Images"));
384  recentImages = new QListWidget(recentPanel);
385  recentPanelLayout->addWidget(recentImages);
386  QPushButton *clearRecent = new QPushButton(i18n("Clear"));
387  recentPanelLayout->addWidget(clearRecent);
388  connect(clearRecent, &QPushButton::pressed, this, &FITSTab::clearRecentFITS);
389  connect(recentImages, &QListWidget::currentRowChanged, this, &FITSTab::selectRecentFITS);
390 
391  QScrollArea *scrollFitsPanel = new QScrollArea(fitsSplitter);
392  scrollFitsPanel->setWidgetResizable(true);
393  scrollFitsPanel->setWidget(fitsTools);
394 
395  fitsSplitter->addWidget(scrollFitsPanel);
396  fitsSplitter->addWidget(m_View.get());
397 
398 
399  //This code allows the fitsTools to start in a closed state
400  fitsSplitter->setSizes(QList<int>() << 0 << m_View->width() );
401 
402  vlayout->addWidget(fitsSplitter);
403  vlayout->addLayout(setupStretchBar());
404 
405  connect(fitsSplitter, &QSplitter::splitterMoved, m_HistogramEditor, &FITSHistogramEditor::resizePlot);
406 
407  setLayout(vlayout);
408  connect(m_View.get(), &FITSView::newStatus, this, &FITSTab::newStatus);
409  connect(m_View.get(), &FITSView::debayerToggled, this, &FITSTab::debayerToggled);
410 
411  // On Failure to load
412  connect(m_View.get(), &FITSView::failed, this, &FITSTab::failed);
413 
414  return true;
415  }
416 
417  // returns false if no setup needed.
418  return false;
419 }
420 
421 void FITSTab::loadFile(const QUrl &imageURL, FITSMode mode, FITSScale filter)
422 {
423  // check if the address points to an appropriate address
424  if (imageURL.isEmpty() || !imageURL.isValid() || !QFileInfo(imageURL.toLocalFile()).exists())
425  return;
426 
427  if (setupView(mode, filter))
428  {
429 
430  // On Success loading image
431  connect(m_View.get(), &FITSView::loaded, [&]()
432  {
433  processData();
434  emit loaded();
435  });
436  }
437  else
438  // update tab text
439  modifyFITSState(true, imageURL);
440 
441  currentURL = imageURL;
442 
443  m_View->setFilter(filter);
444 
445  m_View->loadFile(imageURL.toLocalFile());
446 }
447 
448 bool FITSTab::shouldComputeHFR() const
449 {
450  if (viewer->isStarsMarked())
451  return true;
452  if (!Options::autoHFR())
453  return false;
454  return ((!m_View.isNull()) && (m_View->getMode() == FITS_NORMAL));
455 }
456 
457 void FITSTab::processData()
458 {
459  const QSharedPointer<FITSData> &imageData = m_View->imageData();
460 
461  m_HistogramEditor->setImageData(imageData);
462 
463  // Only construct histogram if it is actually visible
464  // Otherwise wait until histogram is needed before creating it.
465  // if (fitsSplitter->sizes().at(0) != 0 && !imageData->isHistogramConstructed() &&
466  // !Options::nonLinearHistogram())
467  // {
468  // imageData->constructHistogram();
469  // }
470 
471  if (shouldComputeHFR())
472  {
473  m_View->searchStars();
474  qCDebug(KSTARS_FITS) << "FITS HFR:" << imageData->getHFR();
475  }
476 
477  evaluateStats();
478 
479  loadFITSHeader();
480 
481  // Don't add it to the list if it is already there
482  if (recentImages->findItems(currentURL.toLocalFile(), Qt::MatchExactly).count() == 0)
483  {
484  //Don't add it to the list if it is a preview
485  if(!imageData->filename().startsWith(QDir::tempPath()))
486  {
487  disconnect(recentImages, &QListWidget::currentRowChanged, this,
488  &FITSTab::selectRecentFITS);
489  recentImages->addItem(imageData->filename());
490  recentImages->setCurrentRow(recentImages->count() - 1);
491  connect(recentImages, &QListWidget::currentRowChanged, this,
492  &FITSTab::selectRecentFITS);
493  }
494  }
495 
496  // This could both compute the HFRs and setup the graphics, however,
497  // if shouldComputeHFR() above is true, then that will compute the HFRs
498  // and this would notice that and just setup graphics. They are separated
499  // for the case where the graphics is not desired.
500  if (viewer->isStarsMarked())
501  {
502  m_View->toggleStars(true);
503  m_View->updateFrame();
504  }
505 
506  // if (Options::nonLinearHistogram())
507  // m_HistogramEditor->createNonLinearHistogram();
508 }
509 
510 bool FITSTab::loadData(const QSharedPointer<FITSData> &data, FITSMode mode, FITSScale filter)
511 {
512  setupView(mode, filter);
513 
514  // Empty URL
515  currentURL = QUrl();
516 
517  if (viewer->isStarsMarked())
518  {
519  m_View->toggleStars(true);
520  //view->updateFrame();
521  }
522 
523  m_View->setFilter(filter);
524 
525  if (!m_View->loadData(data))
526  {
527  // On Failure to load
528  // connect(view.get(), &FITSView::failed, this, &FITSTab::failed);
529  return false;
530  }
531 
532  processData();
533  return true;
534 }
535 
536 void FITSTab::modifyFITSState(bool clean, const QUrl &imageURL)
537 {
538  if (clean)
539  {
540  if (undoStack->isClean() == false)
541  undoStack->setClean();
542 
543  mDirty = false;
544  }
545  else
546  mDirty = true;
547 
548  emit changeStatus(clean, imageURL);
549 }
550 
551 bool FITSTab::saveImage(const QString &filename)
552 {
553  return m_View->saveImage(filename);
554 }
555 
556 void FITSTab::copyFITS()
557 {
558  QApplication::clipboard()->setImage(m_View->getDisplayImage());
559 }
560 
561 void FITSTab::histoFITS()
562 {
563  // if (Options::nonLinearHistogram())
564  // {
565  // m_HistogramEditor->createNonLinearHistogram();
566  // evaluateStats();
567  // }
568  // if (!m_View->imageData()->isHistogramConstructed())
569  // {
570  // m_View->imageData()->constructHistogram();
571  // evaluateStats();
572  // }
573 
574  fitsTools->setCurrentIndex(1);
575  if(m_View->width() > 200)
576  fitsSplitter->setSizes(QList<int>() << 200 << m_View->width() - 200);
577  else
578  fitsSplitter->setSizes(QList<int>() << 50 << 50);
579 }
580 
581 void FITSTab::evaluateStats()
582 {
583  const QSharedPointer<FITSData> &imageData = m_View->imageData();
584 
585  stat.statsTable->item(STAT_WIDTH, 0)->setText(QString::number(imageData->width()));
586  stat.statsTable->item(STAT_HEIGHT, 0)->setText(QString::number(imageData->height()));
587  stat.statsTable->item(STAT_BITPIX, 0)->setText(QString::number(imageData->bpp()));
588  stat.statsTable->item(STAT_HFR, 0)->setText(QString::number(imageData->getHFR(), 'f', 3));
589 
590  if (imageData->channels() == 1)
591  {
592  for (int i = STAT_MIN; i <= STAT_STDDEV; i++)
593  {
594  if (stat.statsTable->columnSpan(i, 0) != 3)
595  stat.statsTable->setSpan(i, 0, 1, 3);
596  }
597 
598  stat.statsTable->horizontalHeaderItem(0)->setText(i18n("Value"));
599  stat.statsTable->hideColumn(1);
600  stat.statsTable->hideColumn(2);
601  }
602  else
603  {
604  for (int i = STAT_MIN; i <= STAT_STDDEV; i++)
605  {
606  if (stat.statsTable->columnSpan(i, 0) != 1)
607  stat.statsTable->setSpan(i, 0, 1, 1);
608  }
609 
610  stat.statsTable->horizontalHeaderItem(0)->setText(i18nc("Red", "R"));
611  stat.statsTable->showColumn(1);
612  stat.statsTable->showColumn(2);
613  }
614 
615  if (!Options::nonLinearHistogram() && !imageData->isHistogramConstructed())
616  imageData->constructHistogram();
617 
618  for (int i = 0; i < imageData->channels(); i++)
619  {
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));
625  }
626 }
627 
628 void FITSTab::statFITS()
629 {
630  fitsTools->setCurrentIndex(0);
631  if(m_View->width() > 200)
632  fitsSplitter->setSizes(QList<int>() << 200 << m_View->width() - 200);
633  else
634  fitsSplitter->setSizes(QList<int>() << 50 << 50);
635  evaluateStats();
636 }
637 
638 void FITSTab::loadFITSHeader()
639 {
640  const QSharedPointer<FITSData> &imageData = m_View->imageData();
641 
642  int nkeys = imageData->getRecords().size();
643  int counter = 0;
644  header.tableWidget->setRowCount(nkeys);
645  for (const auto &oneRecord : imageData->getRecords())
646  {
647  QTableWidgetItem *tempItem = new QTableWidgetItem(oneRecord.key);
649  header.tableWidget->setItem(counter, 0, tempItem);
650  tempItem = new QTableWidgetItem(oneRecord.value.toString());
652  header.tableWidget->setItem(counter, 1, tempItem);
653  tempItem = new QTableWidgetItem(oneRecord.comment);
655  header.tableWidget->setItem(counter, 2, tempItem);
656  counter++;
657  }
658 
659  header.tableWidget->setColumnWidth(0, 100);
660  header.tableWidget->setColumnWidth(1, 100);
661  header.tableWidget->setColumnWidth(2, 250);
662 }
663 
664 void FITSTab::headerFITS()
665 {
666  fitsTools->setCurrentIndex(2);
667  if(m_View->width() > 200)
668  fitsSplitter->setSizes(QList<int>() << 200 << m_View->width() - 200);
669  else
670  fitsSplitter->setSizes(QList<int>() << 50 << 50);
671 }
672 
673 bool FITSTab::saveFile()
674 {
675  QUrl backupCurrent = currentURL;
676  QUrl currentDir(Options::fitsDir());
677  currentDir.setScheme("file");
678 
679  if (currentURL.toLocalFile().startsWith(QLatin1String("/tmp/")) || currentURL.toLocalFile().contains("/Temp"))
680  currentURL.clear();
681 
682  // If no changes made, return.
683  if (mDirty == false && !currentURL.isEmpty())
684  return false;
685 
686  if (currentURL.isEmpty())
687  {
688  QString selectedFilter;
689 #ifdef Q_OS_OSX //For some reason, the other code caused KStars to crash on MacOS
690  currentURL =
691  QFileDialog::getSaveFileUrl(KStars::Instance(), i18nc("@title:window", "Save FITS"), currentDir,
692  "Images (*.fits *.fits.gz *.fit *.xisf *.jpg *.jpeg *.png)");
693 #else
694  currentURL =
695  QFileDialog::getSaveFileUrl(KStars::Instance(), i18nc("@title:window", "Save FITS"), currentDir,
696  "FITS (*.fits *.fits.gz *.fit);;XISF (*.xisf);;JPEG (*.jpg *.jpeg);;PNG (*.png)", &selectedFilter);
697 #endif
698  // if user presses cancel
699  if (currentURL.isEmpty())
700  {
701  currentURL = backupCurrent;
702  return false;
703  }
704 
705  // If no extension is selected append one
706  if (currentURL.toLocalFile().contains('.') == 0)
707  {
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");
714  else
715  currentURL.setPath(currentURL.toLocalFile() + ".fits");
716  }
717  }
718 
719  if (currentURL.isValid())
720  {
721  QString localFile = currentURL.toLocalFile();
722  // if (localFile.contains(".fit"))
723  // localFile = "!" + localFile;
724 
725  if (!saveImage(localFile))
726  {
727  KSNotification::error(i18n("Image save error: %1", m_View->imageData()->getLastError()), i18n("Image Save"));
728  return false;
729  }
730 
731  emit newStatus(i18n("File saved to %1", currentURL.url()), FITS_MESSAGE);
732  modifyFITSState();
733  return true;
734  }
735  else
736  {
737  QString message = i18n("Invalid URL: %1", currentURL.url());
738  KSNotification::sorry(message, i18n("Invalid URL"));
739  return false;
740  }
741 }
742 
743 bool FITSTab::saveFileAs()
744 {
745  currentURL.clear();
746  return saveFile();
747 }
748 
749 void FITSTab::ZoomIn()
750 {
751  QPoint oldCenter = m_View->getImagePoint(m_View->viewport()->rect().center());
752  m_View->ZoomIn();
753  m_View->cleanUpZoom(oldCenter);
754 }
755 
756 void FITSTab::ZoomOut()
757 {
758  QPoint oldCenter = m_View->getImagePoint(m_View->viewport()->rect().center());
759  m_View->ZoomOut();
760  m_View->cleanUpZoom(oldCenter);
761 }
762 
763 void FITSTab::ZoomDefault()
764 {
765  QPoint oldCenter = m_View->getImagePoint(m_View->viewport()->rect().center());
766  m_View->ZoomDefault();
767  m_View->cleanUpZoom(oldCenter);
768 }
769 
770 void FITSTab::tabPositionUpdated()
771 {
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()),
775  FITS_RESOLUTION);
776 }
AlignHCenter
void setMinimum(int)
void setCheckable(bool)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QString number(int n, int base)
void setFrameShadow(QFrame::Shadow)
void splitterMoved(int pos, int index)
void setSizePolicy(QSizePolicy)
void setImage(const QImage &image, QClipboard::Mode mode)
void setPointSize(int pointSize)
void clicked(bool checked)
QIcon fromTheme(const QString &name)
void setFont(const QFont &)
void setChecked(bool)
void setFrameShape(QFrame::Shape)
QString caption()
bool exists() const const
KGuiItem save()
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setMaximum(int)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void currentRowChanged(int currentRow)
static KStars * Instance()
Definition: kstars.h:121
QString tempPath()
void setValue(int)
bool isValid() const const
QClipboard * clipboard()
ButtonCode warningYesNoCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
ItemIsSelectable
QString i18n(const char *text, const TYPE &arg...)
bool isEmpty() const const
void sliderMoved(int value)
Horizontal
Primary window to view monochrome and color FITS images. The FITSviewer can open multiple images each...
Definition: fitsviewer.h:48
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options, const QStringList &supportedSchemes)
QUrl fromLocalFile(const QString &localFile)
QString toLocalFile() const const
void sliderReleased()
void setIconSize(const QSize &size)
QString label(StandardShortcut id)
void setIcon(const QIcon &icon)
void setWidgetResizable(bool resizable)
void setFlags(Qt::ItemFlags flags)
void setToolTip(const QString &)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void setPath(const QString &path, QUrl::ParsingMode mode)
void ignore()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void addLayout(QLayout *layout, int stretch)
MatchExactly
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
void setLayout(QLayout *layout)
QString message
KGuiItem discard()
void setWidget(QWidget *widget)
void accept()
int stat(const QString &path, KDE_struct_stat *buf)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon May 8 2023 03:57:30 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.