File indexing completed on 2024-04-28 03:43:15

0001 /*
0002     SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003     SPDX-FileCopyrightText: 2021 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "capturepreviewwidget.h"
0009 #include "sequencejob.h"
0010 #include <ekos_capture_debug.h>
0011 #include "ksutils.h"
0012 #include "ksmessagebox.h"
0013 #include "ekos/mount/mount.h"
0014 #include "Options.h"
0015 #include "capture.h"
0016 #include "sequencejob.h"
0017 #include "fitsviewer/fitsdata.h"
0018 #include "fitsviewer/summaryfitsview.h"
0019 #include "ekos/scheduler/scheduler.h"
0020 
0021 using Ekos::SequenceJob;
0022 
0023 CapturePreviewWidget::CapturePreviewWidget(QWidget *parent) : QWidget(parent)
0024 {
0025     setupUi(this);
0026     overlay = new CaptureProcessOverlay();
0027     overlay->setVisible(false);
0028     // history navigation
0029     connect(overlay->historyBackwardButton, &QPushButton::clicked, this, &CapturePreviewWidget::showPreviousFrame);
0030     connect(overlay->historyForwardButton, &QPushButton::clicked, this, &CapturePreviewWidget::showNextFrame);
0031     // deleting of captured frames
0032     connect(overlay->deleteCurrentFrameButton, &QPushButton::clicked, this, &CapturePreviewWidget::deleteCurrentFrame);
0033 }
0034 
0035 void CapturePreviewWidget::shareCaptureModule(Ekos::Capture *module)
0036 {
0037     captureModule = module;
0038     captureCountsWidget->shareCaptureProcess(module);
0039 
0040     if (captureModule != nullptr)
0041     {
0042         connect(captureModule, &Ekos::Capture::newDownloadProgress, captureCountsWidget,
0043                 &CaptureCountsWidget::updateDownloadProgress);
0044         connect(captureModule, &Ekos::Capture::newExposureProgress, captureCountsWidget,
0045                 &CaptureCountsWidget::updateExposureProgress);
0046         connect(captureModule, &Ekos::Capture::captureTarget, this, &CapturePreviewWidget::setTargetName);
0047     }
0048 }
0049 
0050 void CapturePreviewWidget::shareSchedulerModule(Ekos::Scheduler *module)
0051 {
0052     schedulerModule = module;
0053     captureCountsWidget->shareSchedulerProcess(module);
0054 }
0055 
0056 void CapturePreviewWidget::shareMountModule(Ekos::Mount *module)
0057 {
0058     mountModule = module;
0059     connect(mountModule, &Ekos::Mount::newTargetName, this, &CapturePreviewWidget::setTargetName);
0060 }
0061 
0062 void CapturePreviewWidget::updateJobProgress(Ekos::SequenceJob *job, const QSharedPointer<FITSData> &data)
0063 {
0064     // forward first to the counting widget
0065     captureCountsWidget->updateJobProgress(job);
0066 
0067     // without FITS data, we do nothing
0068     if (data == nullptr)
0069         return;
0070 
0071     // cache frame meta data
0072     m_currentFrame.frameType = job->getFrameType();
0073     if (job->getFrameType() == FRAME_LIGHT)
0074     {
0075         if (schedulerModule != nullptr && schedulerModule->activeJob() != nullptr)
0076             m_currentFrame.target = schedulerModule->activeJob()->getName();
0077         else
0078             m_currentFrame.target = m_mountTarget;
0079     }
0080     else
0081         m_currentFrame.target = "";
0082 
0083     m_currentFrame.filterName  = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
0084     m_currentFrame.exptime     = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
0085     m_currentFrame.targetdrift = -1.0; // will be updated later
0086     m_currentFrame.binning     = job->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
0087     m_currentFrame.gain        = job->getCoreProperty(SequenceJob::SJ_Gain).toDouble();
0088     m_currentFrame.offset      = job->getCoreProperty(SequenceJob::SJ_Offset).toDouble();
0089     m_currentFrame.filename    = data->filename();
0090     m_currentFrame.width       = data->width();
0091     m_currentFrame.height      = data->height();
0092 
0093     const auto ISOIndex = job->getCoreProperty(SequenceJob::SJ_Offset).toInt();
0094     if (ISOIndex >= 0 && ISOIndex <= captureModule->captureISOS->count())
0095         m_currentFrame.iso = captureModule->captureISOS->itemText(ISOIndex);
0096     else
0097         m_currentFrame.iso = "";
0098 
0099     // add it to the overlay
0100     overlay->addFrameData(m_currentFrame);
0101     overlay->setVisible(true);
0102 
0103     // load frame
0104     if (m_fitsPreview != nullptr && Options::useSummaryPreview())
0105         m_fitsPreview->loadData(data);
0106 }
0107 
0108 void CapturePreviewWidget::showNextFrame()
0109 {
0110     overlay->setEnabled(false);
0111     if (overlay->showNextFrame())
0112         m_fitsPreview->loadFile(overlay->currentFrame().filename);
0113     // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
0114     else
0115         overlay->setEnabled(true);
0116 }
0117 
0118 void CapturePreviewWidget::showPreviousFrame()
0119 {
0120     overlay->setEnabled(false);
0121     if (overlay->showPreviousFrame())
0122         m_fitsPreview->loadFile(overlay->currentFrame().filename);
0123     // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
0124     else
0125         overlay->setEnabled(true);
0126 }
0127 
0128 void CapturePreviewWidget::deleteCurrentFrame()
0129 {
0130     overlay->setEnabled(false);
0131     if (overlay->hasFrames() == false)
0132         // nothing to delete
0133         return;
0134 
0135     // make sure that the history does not change inbetween
0136     int pos = overlay->currentPosition();
0137     CaptureProcessOverlay::FrameData current = overlay->getFrame(pos);
0138     QFile *file = new QFile(current.filename);
0139 
0140     // prepare a warning dialog
0141     // move to trash or delete permanently
0142     QCheckBox *permanentlyDeleteCB = new QCheckBox(i18n("Delete directly, do not move to trash."));
0143     permanentlyDeleteCB->setChecked(m_permanentlyDelete);
0144     KSMessageBox::Instance()->setCheckBox(permanentlyDeleteCB);
0145     connect(permanentlyDeleteCB, &QCheckBox::toggled, this, [this](bool checked)
0146     {
0147         this->m_permanentlyDelete = checked;
0148     });
0149     // Delete
0150     connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, pos, file]()
0151     {
0152         KSMessageBox::Instance()->disconnect(this);
0153         bool success = false;
0154         if (this->m_permanentlyDelete == false && (success = file->moveToTrash()))
0155         {
0156             qCInfo(KSTARS_EKOS_CAPTURE) << overlay->currentFrame().filename << "moved to Trash.";
0157         }
0158         else if (this->m_permanentlyDelete && (success = file->remove()))
0159         {
0160             qCInfo(KSTARS_EKOS_CAPTURE) << overlay->currentFrame().filename << "deleted.";
0161         }
0162 
0163         if (success)
0164         {
0165             // delete it from the history and update the FITS view
0166             if (overlay->deleteFrame(pos) && overlay->hasFrames())
0167             {
0168                 m_fitsPreview->loadFile(overlay->currentFrame().filename);
0169                 // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
0170             }
0171             else
0172             {
0173                 m_fitsPreview->clearData();
0174                 overlay->setEnabled(true);
0175             }
0176         }
0177         else
0178         {
0179             qCWarning(KSTARS_EKOS_CAPTURE) << "Deleting" << overlay->currentFrame().filename << "failed!";
0180             // give up
0181             overlay->setEnabled(true);
0182         }
0183         // clear the check box
0184         KSMessageBox::Instance()->setCheckBox(nullptr);
0185     });
0186 
0187     // Cancel
0188     connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [this]()
0189     {
0190         KSMessageBox::Instance()->disconnect(this);
0191         // clear the check box
0192         KSMessageBox::Instance()->setCheckBox(nullptr);
0193         //do nothing
0194         overlay->setEnabled(true);
0195     });
0196 
0197     // open the message box
0198     QFileInfo fileinfo(current.filename);
0199     KSMessageBox::Instance()->warningContinueCancel(i18n("Do you really want to delete %1 from the file system?",
0200             fileinfo.fileName()),
0201             i18n("Delete %1", fileinfo.fileName()), 0, false, i18n("Delete"));
0202 
0203 }
0204 
0205 void CapturePreviewWidget::setSummaryFITSView(SummaryFITSView *view)
0206 {
0207     m_fitsPreview = view;
0208     QVBoxLayout * vlayout = new QVBoxLayout();
0209     vlayout->setContentsMargins(0, 0, 0, 0);
0210     vlayout->addWidget(view);
0211     previewWidget->setLayout(vlayout);
0212     previewWidget->setContentsMargins(0, 0, 0, 0);
0213 
0214     // initialize the FITS data overlay
0215     // create vertically info box as overlay
0216     QVBoxLayout *layout = new QVBoxLayout(view->processInfoWidget);
0217     layout->addWidget(overlay, 0);
0218 
0219     view->processInfoWidget->setLayout(layout);
0220     // react upon signals
0221     connect(view, &FITSView::loaded, [&]()
0222     {
0223         overlay->setEnabled(true);
0224     });
0225     connect(view, &FITSView::failed, [&]()
0226     {
0227         overlay->setEnabled(true);
0228     });
0229 }
0230 
0231 void CapturePreviewWidget::setEnabled(bool enabled)
0232 {
0233     // forward to sub widget
0234     captureCountsWidget->setEnabled(enabled);
0235     QWidget::setEnabled(enabled);
0236 }
0237 
0238 void CapturePreviewWidget::reset()
0239 {
0240     overlay->setVisible(false);
0241     // forward to sub widget
0242     captureCountsWidget->reset();
0243 }
0244 
0245 void CapturePreviewWidget::updateCaptureStatus(Ekos::CaptureState status)
0246 {
0247     // forward to sub widgets
0248     captureStatusWidget->setCaptureState(status);
0249     captureCountsWidget->updateCaptureStatus(status);
0250 }
0251 
0252 void CapturePreviewWidget::updateTargetDistance(double targetDiff)
0253 {
0254     // forward it to the overlay
0255     overlay->updateTargetDistance(targetDiff);
0256 }
0257 
0258 void CapturePreviewWidget::updateCaptureCountDown(int delta)
0259 {
0260     // forward to sub widget
0261     captureCountsWidget->updateCaptureCountDown(delta);
0262 }
0263 
0264 void CapturePreviewWidget::setTargetName(QString name)
0265 {
0266     targetLabel->setVisible(!name.isEmpty());
0267     mountTarget->setVisible(!name.isEmpty());
0268     mountTarget->setText(name);
0269     m_mountTarget = name;
0270     m_currentFrame.target = name;
0271 }
0272