Kstars

capturepreviewwidget.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 SPDX-FileCopyrightText: 2021 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "capturepreviewwidget.h"
9#include "sequencejob.h"
10#include <ekos_capture_debug.h>
11#include "ksutils.h"
12#include "ksmessagebox.h"
13#include "ekos/mount/mount.h"
14#include "Options.h"
15#include "capture.h"
16#include "sequencejob.h"
17#include "fitsviewer/fitsdata.h"
18#include "fitsviewer/summaryfitsview.h"
19#include "ekos/scheduler/schedulerjob.h"
20#include "ekos/scheduler/schedulermodulestate.h"
21
22using Ekos::SequenceJob;
23
24CapturePreviewWidget::CapturePreviewWidget(QWidget *parent) : QWidget(parent)
25{
26 setupUi(this);
27 m_overlay = QSharedPointer<CaptureProcessOverlay>(new CaptureProcessOverlay);
28 m_overlay->setVisible(false);
29 // capture device selection
30 connect(trainSelectionCB, &QComboBox::currentTextChanged, this, &CapturePreviewWidget::selectedTrainChanged);
31 // history navigation
32 connect(m_overlay->historyBackwardButton, &QPushButton::clicked, this, &CapturePreviewWidget::showPreviousFrame);
33 connect(m_overlay->historyForwardButton, &QPushButton::clicked, this, &CapturePreviewWidget::showNextFrame);
34 // deleting of captured frames
35 connect(m_overlay->deleteCurrentFrameButton, &QPushButton::clicked, this, &CapturePreviewWidget::deleteCurrentFrame);
36
37 // make invisible until we have at least two cameras active
38 trainSelectionCB->setVisible(false);
39}
40
41void CapturePreviewWidget::shareCaptureModule(Ekos::Capture *module)
42{
43 m_captureModule = module;
44 captureCountsWidget->shareCaptureProcess(module);
45 m_trainNames.clear();
46 trainSelectionCB->clear();
47 // make invisible until we have at least two cameras active
48 trainSelectionCB->setVisible(false);
49 captureLabel->setVisible(true);
50
51 if (m_captureModule != nullptr)
52 {
53 connect(m_captureModule, &Ekos::Capture::newDownloadProgress, this, &CapturePreviewWidget::updateDownloadProgress);
54 connect(m_captureModule, &Ekos::Capture::newExposureProgress, this, &CapturePreviewWidget::updateExposureProgress);
55 connect(m_captureModule, &Ekos::Capture::captureTarget, this, &CapturePreviewWidget::setTargetName);
56 }
57}
58
59void CapturePreviewWidget::shareSchedulerModuleState(QSharedPointer<Ekos::SchedulerModuleState> state)
60{
61 m_schedulerModuleState = state;
62 captureCountsWidget->shareSchedulerState(state);
63}
64
65void CapturePreviewWidget::shareMountModule(Ekos::Mount *module)
66{
67 m_mountModule = module;
68 connect(m_mountModule, &Ekos::Mount::newTargetName, this, &CapturePreviewWidget::setTargetName);
69}
70
71void CapturePreviewWidget::updateJobProgress(Ekos::SequenceJob *job, const QSharedPointer<FITSData> &data,
72 const QString &trainname)
73{
74 // ensure that we have all camera device names in the selection
75 if (!m_trainNames.contains(trainname))
76 {
77 m_trainNames.append(trainname);
78 trainSelectionCB->addItem(trainname);
79
80 trainSelectionCB->setVisible(m_trainNames.count() >= 2);
81 captureLabel->setVisible(m_trainNames.count() < 2);
82 }
83
84 if (job != nullptr)
85 {
86 // cache frame meta data
87 m_currentFrame[trainname].frameType = job->getFrameType();
88 if (job->getFrameType() == FRAME_LIGHT)
89 {
90 if (m_schedulerModuleState != nullptr && m_schedulerModuleState->activeJob() != nullptr)
91 m_currentFrame[trainname].target = m_schedulerModuleState->activeJob()->getName();
92 else
93 m_currentFrame[trainname].target = m_mountTarget;
94 }
95 else
96 {
97 m_currentFrame[trainname].target = "";
98 }
99
100 m_currentFrame[trainname].filterName = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
101 m_currentFrame[trainname].exptime = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
102 m_currentFrame[trainname].targetdrift = -1.0; // will be updated later
103 m_currentFrame[trainname].binning = job->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
104 m_currentFrame[trainname].gain = job->getCoreProperty(SequenceJob::SJ_Gain).toDouble();
105 m_currentFrame[trainname].offset = job->getCoreProperty(SequenceJob::SJ_Offset).toDouble();
106 m_currentFrame[trainname].jobType = job->jobType();
107 m_currentFrame[trainname].frameType = job->getFrameType();
108 m_currentFrame[trainname].count = job->getCoreProperty(SequenceJob::SJ_Count).toInt();
109 m_currentFrame[trainname].completed = job->getCompleted();
110
111 if (data != nullptr)
112 {
113 m_currentFrame[trainname].filename = data->filename();
114 m_currentFrame[trainname].width = data->width();
115 m_currentFrame[trainname].height = data->height();
116 }
117
118 const auto ISOIndex = job->getCoreProperty(SequenceJob::SJ_Offset).toInt();
119 if (ISOIndex >= 0 && ISOIndex <= m_captureModule->mainCamera()->captureISOS->count())
120 m_currentFrame[trainname].iso = m_captureModule->mainCamera()->captureISOS->itemText(ISOIndex);
121 else
122 m_currentFrame[trainname].iso = "";
123 }
124
125 // forward first to the counting widget
126 captureCountsWidget->updateJobProgress(m_currentFrame[trainname], trainname);
127
128 // add it to the overlay if data is present
129 if (!data.isNull())
130 {
131 m_overlay->addFrameData(m_currentFrame[trainname], trainname);
132 m_overlay->setVisible(true);
133 }
134
135 // load frame
136 if (m_fitsPreview != nullptr && Options::useSummaryPreview() && trainSelectionCB->currentText() == trainname)
137 m_fitsPreview->loadData(data);
138}
139
140void CapturePreviewWidget::updateJobPreview(const QString &filePath)
141{
142 // without FITS filePath, we do nothing
143 if (filePath == "")
144 return;
145
146 // load frame
147 if (m_fitsPreview != nullptr && Options::useSummaryPreview())
148 m_fitsPreview->loadFile(filePath);
149}
150
151void CapturePreviewWidget::showNextFrame()
152{
153 m_overlay->setEnabled(false);
154 if (m_overlay->showNextFrame())
155 m_fitsPreview->loadFile(m_overlay->currentFrame().filename);
156 // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
157 else
158 m_overlay->setEnabled(true);
159}
160
161void CapturePreviewWidget::showPreviousFrame()
162{
163 m_overlay->setEnabled(false);
164 if (m_overlay->showPreviousFrame())
165 m_fitsPreview->loadFile(m_overlay->currentFrame().filename);
166 // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
167 else
168 m_overlay->setEnabled(true);
169}
170
171void CapturePreviewWidget::deleteCurrentFrame()
172{
173 m_overlay->setEnabled(false);
174 if (m_overlay->hasFrames() == false)
175 // nothing to delete
176 return;
177
178 // make sure that the history does not change inbetween
179 int pos = m_overlay->currentPosition();
180 CaptureProcessOverlay::FrameData current = m_overlay->getFrame(pos);
181 QFile *file = new QFile(current.filename);
182
183 // prepare a warning dialog
184 // move to trash or delete permanently
185 QCheckBox *permanentlyDeleteCB = new QCheckBox(i18n("Delete directly, do not move to trash."));
186 permanentlyDeleteCB->setChecked(m_permanentlyDelete);
187 KSMessageBox::Instance()->setCheckBox(permanentlyDeleteCB);
188 connect(permanentlyDeleteCB, &QCheckBox::toggled, this, [this](bool checked)
189 {
190 this->m_permanentlyDelete = checked;
191 });
192 // Delete
193 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, pos, file]()
194 {
195 KSMessageBox::Instance()->disconnect(this);
196 bool success = false;
197 if (this->m_permanentlyDelete == false && (success = file->moveToTrash()))
198 {
199 qCInfo(KSTARS_EKOS_CAPTURE) << m_overlay->currentFrame().filename << "moved to Trash.";
200 }
201 else if (this->m_permanentlyDelete && (success = file->remove()))
202 {
203 qCInfo(KSTARS_EKOS_CAPTURE) << m_overlay->currentFrame().filename << "deleted.";
204 }
205
206 if (success)
207 {
208 // delete it from the history and update the FITS view
209 if (m_overlay->deleteFrame(pos) && m_overlay->hasFrames())
210 {
211 m_fitsPreview->loadFile(m_overlay->currentFrame().filename);
212 // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
213 }
214 else
215 {
216 m_fitsPreview->clearData();
217 m_overlay->setEnabled(true);
218 }
219 }
220 else
221 {
222 qCWarning(KSTARS_EKOS_CAPTURE) << "Deleting" << m_overlay->currentFrame().filename <<
223 "failed!";
224 // give up
225 m_overlay->setEnabled(true);
226 }
227 // clear the check box
228 KSMessageBox::Instance()->setCheckBox(nullptr);
229 });
230
231 // Cancel
232 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [this]()
233 {
234 KSMessageBox::Instance()->disconnect(this);
235 // clear the check box
236 KSMessageBox::Instance()->setCheckBox(nullptr);
237 //do nothing
238 m_overlay->setEnabled(true);
239 });
240
241 // open the message box
242 QFileInfo fileinfo(current.filename);
243 KSMessageBox::Instance()->warningContinueCancel(i18n("Do you really want to delete %1 from the file system?",
244 fileinfo.fileName()),
245 i18n("Delete %1", fileinfo.fileName()), 0, false, i18n("Delete"));
246
247}
248
249void CapturePreviewWidget::setSummaryFITSView(const QSharedPointer<SummaryFITSView> &view)
250{
251 m_fitsPreview = view;
252 QVBoxLayout * vlayout = new QVBoxLayout();
253 vlayout->setContentsMargins(0, 0, 0, 0);
254 vlayout->addWidget(view.get());
255 previewWidget->setLayout(vlayout);
256 previewWidget->setContentsMargins(0, 0, 0, 0);
257
258 // initialize the FITS data overlay
259 // create vertically info box as overlay
260 QVBoxLayout *layout = new QVBoxLayout(view->processInfoWidget);
261 layout->addWidget(m_overlay.get(), 0);
262
263 view->processInfoWidget->setLayout(layout);
264 // react upon signals
265 connect(view.get(), &FITSView::loaded, [&]()
266 {
267 m_overlay->setEnabled(true);
268 });
269 connect(view.get(), &FITSView::failed, [&]()
270 {
271 m_overlay->setEnabled(true);
272 });
273}
274
275void CapturePreviewWidget::setEnabled(bool enabled)
276{
277 // forward to sub widget
278 captureCountsWidget->setEnabled(enabled);
280}
281
282void CapturePreviewWidget::reset()
283{
284 if (m_overlay->hasFrames())
285 m_overlay->captureHistory().reset();
286 m_overlay->setVisible(false);
287 // forward to sub widget
288 captureCountsWidget->reset();
289}
290
291void CapturePreviewWidget::updateCaptureStatus(Ekos::CaptureState status, bool isPreview, const QString &trainname)
292{
293 // forward to sub widgets
294 captureStatusWidget->setCaptureState(status);
295 captureCountsWidget->updateCaptureStatus(status, isPreview, trainname);
296}
297
298void CapturePreviewWidget::updateTargetDistance(double targetDiff)
299{
300 // forward it to the overlay
301 m_overlay->updateTargetDistance(targetDiff);
302}
303
304void CapturePreviewWidget::updateCaptureCountDown(int delta)
305{
306 // forward to sub widget
307 captureCountsWidget->updateCaptureCountDown(delta);
308}
309
310void CapturePreviewWidget::selectedTrainChanged(QString newName)
311{
312 m_overlay->setCurrentTrainName(newName);
313 captureCountsWidget->setCurrentTrainName(newName);
314
315 m_overlay->setEnabled(false);
316 if (m_overlay->hasFrames())
317 {
318 // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
319 m_fitsPreview->loadFile(m_overlay->currentFrame().filename);
320 }
321 else
322 {
323 m_fitsPreview->clearData();
324 m_overlay->setEnabled(true);
325 }
326}
327
328void CapturePreviewWidget::updateExposureProgress(Ekos::SequenceJob *job, const QString &trainname)
329{
330 if (trainname == trainSelectionCB->currentText())
331 captureCountsWidget->updateExposureProgress(job, trainname);
332}
333
334void CapturePreviewWidget::updateDownloadProgress(double downloadTimeLeft, const QString &trainname)
335{
336 if (trainname == trainSelectionCB->currentText())
337 captureCountsWidget->updateDownloadProgress(downloadTimeLeft, trainname);
338}
339
340void CapturePreviewWidget::setTargetName(QString name)
341{
342 targetLabel->setVisible(!name.isEmpty());
343 mountTarget->setVisible(!name.isEmpty());
344 mountTarget->setText(name);
345 m_mountTarget = name;
346 m_currentFrame[trainSelectionCB->currentText()].target = name;
347}
348
Captures single or sequence of images from a CCD.
Definition capture.h:94
Supports controlling INDI telescope devices including setting/retrieving mount properties,...
Definition mount.h:33
void newTargetName(const QString &name)
The mount has finished the slew to a new target.
QString i18n(const char *text, const TYPE &arg...)
CaptureState
Capture states.
Definition ekos.h:92
QString name(StandardAction id)
void setChecked(bool)
void clicked(bool checked)
void toggled(bool checked)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void currentTextChanged(const QString &text)
void accepted()
void rejected()
bool moveToTrash()
bool remove()
void addWidget(QWidget *w)
void setContentsMargins(const QMargins &margins)
void append(QList< T > &&value)
void clear()
bool contains(const AT &value) const const
qsizetype count() const const
size_type count() const const
void setCheckBox(QCheckBox *cb)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T * get() const const
bool isNull() const const
bool isEmpty() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
QPoint toPoint() const const
QString toString() const const
QLayout * layout() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:40 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.