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 = new CaptureProcessOverlay();
28 m_overlay->setVisible(false);
29 // capture device selection
30 connect(cameraSelectionCB, &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 cameraSelectionCB->setVisible(false);
39}
40
41void CapturePreviewWidget::shareCaptureModule(Ekos::Capture *module)
42{
43 m_captureModule = module;
44 captureCountsWidget->shareCaptureProcess(module);
45 m_trainNames.clear();
46
47 if (m_captureModule != nullptr)
48 {
49 connect(m_captureModule, &Ekos::Capture::newDownloadProgress, this, &CapturePreviewWidget::updateDownloadProgress);
50 connect(m_captureModule, &Ekos::Capture::newExposureProgress, this, &CapturePreviewWidget::updateExposureProgress);
51 connect(m_captureModule, &Ekos::Capture::captureTarget, this, &CapturePreviewWidget::setTargetName);
52 }
53}
54
55void CapturePreviewWidget::shareSchedulerModuleState(QSharedPointer<Ekos::SchedulerModuleState> state)
56{
57 m_schedulerModuleState = state;
58 captureCountsWidget->shareSchedulerState(state);
59}
60
61void CapturePreviewWidget::shareMountModule(Ekos::Mount *module)
62{
63 m_mountModule = module;
64 connect(m_mountModule, &Ekos::Mount::newTargetName, this, &CapturePreviewWidget::setTargetName);
65}
66
67void CapturePreviewWidget::updateJobProgress(Ekos::SequenceJob *job, const QSharedPointer<FITSData> &data,
68 const QString &trainname)
69{
70 // ensure that we have all camera device names in the selection
71 if (!m_trainNames.contains(trainname))
72 {
73 m_trainNames.append(trainname);
74 cameraSelectionCB->addItem(trainname);
75
76 cameraSelectionCB->setVisible(m_trainNames.count() >= 2);
77 captureLabel->setVisible(m_trainNames.count() < 2);
78 }
79
80 if (job != nullptr)
81 {
82 // cache frame meta data
83 m_currentFrame[trainname].frameType = job->getFrameType();
84 if (job->getFrameType() == FRAME_LIGHT)
85 {
86 if (m_schedulerModuleState != nullptr && m_schedulerModuleState->activeJob() != nullptr)
87 m_currentFrame[trainname].target = m_schedulerModuleState->activeJob()->getName();
88 else
89 m_currentFrame[trainname].target = m_mountTarget;
90 }
91 else
92 {
93 m_currentFrame[trainname].target = "";
94 }
95
96 m_currentFrame[trainname].filterName = job->getCoreProperty(SequenceJob::SJ_Filter).toString();
97 m_currentFrame[trainname].exptime = job->getCoreProperty(SequenceJob::SJ_Exposure).toDouble();
98 m_currentFrame[trainname].targetdrift = -1.0; // will be updated later
99 m_currentFrame[trainname].binning = job->getCoreProperty(SequenceJob::SJ_Binning).toPoint();
100 m_currentFrame[trainname].gain = job->getCoreProperty(SequenceJob::SJ_Gain).toDouble();
101 m_currentFrame[trainname].offset = job->getCoreProperty(SequenceJob::SJ_Offset).toDouble();
102 m_currentFrame[trainname].jobType = job->jobType();
103 m_currentFrame[trainname].frameType = job->getFrameType();
104 m_currentFrame[trainname].count = job->getCoreProperty(SequenceJob::SJ_Count).toInt();
105 m_currentFrame[trainname].completed = job->getCompleted();
106
107 if (data != nullptr)
108 {
109 m_currentFrame[trainname].filename = data->filename();
110 m_currentFrame[trainname].width = data->width();
111 m_currentFrame[trainname].height = data->height();
112 }
113
114 const auto ISOIndex = job->getCoreProperty(SequenceJob::SJ_Offset).toInt();
115 if (ISOIndex >= 0 && ISOIndex <= m_captureModule->mainCamera()->captureISOS->count())
116 m_currentFrame[trainname].iso = m_captureModule->mainCamera()->captureISOS->itemText(ISOIndex);
117 else
118 m_currentFrame[trainname].iso = "";
119 }
120
121 // forward first to the counting widget
122 captureCountsWidget->updateJobProgress(m_currentFrame[trainname], trainname);
123
124 // add it to the overlay if data is present
125 if (!data.isNull())
126 {
127 m_overlay->addFrameData(m_currentFrame[trainname], trainname);
128 m_overlay->setVisible(true);
129 }
130
131 // load frame
132 if (m_fitsPreview != nullptr && Options::useSummaryPreview() && cameraSelectionCB->currentText() == trainname)
133 m_fitsPreview->loadData(data);
134}
135
136void CapturePreviewWidget::updateJobPreview(const QString &filePath)
137{
138 // without FITS filePath, we do nothing
139 if (filePath == "")
140 return;
141
142 // load frame
143 if (m_fitsPreview != nullptr && Options::useSummaryPreview())
144 m_fitsPreview->loadFile(filePath);
145}
146
147void CapturePreviewWidget::showNextFrame()
148{
149 m_overlay->setEnabled(false);
150 if (m_overlay->showNextFrame())
151 m_fitsPreview->loadFile(m_overlay->currentFrame().filename);
152 // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
153 else
154 m_overlay->setEnabled(true);
155}
156
157void CapturePreviewWidget::showPreviousFrame()
158{
159 m_overlay->setEnabled(false);
160 if (m_overlay->showPreviousFrame())
161 m_fitsPreview->loadFile(m_overlay->currentFrame().filename);
162 // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
163 else
164 m_overlay->setEnabled(true);
165}
166
167void CapturePreviewWidget::deleteCurrentFrame()
168{
169 m_overlay->setEnabled(false);
170 if (m_overlay->hasFrames() == false)
171 // nothing to delete
172 return;
173
174 // make sure that the history does not change inbetween
175 int pos = m_overlay->currentPosition();
176 CaptureProcessOverlay::FrameData current = m_overlay->getFrame(pos);
177 QFile *file = new QFile(current.filename);
178
179 // prepare a warning dialog
180 // move to trash or delete permanently
181 QCheckBox *permanentlyDeleteCB = new QCheckBox(i18n("Delete directly, do not move to trash."));
182 permanentlyDeleteCB->setChecked(m_permanentlyDelete);
183 KSMessageBox::Instance()->setCheckBox(permanentlyDeleteCB);
184 connect(permanentlyDeleteCB, &QCheckBox::toggled, this, [this](bool checked)
185 {
186 this->m_permanentlyDelete = checked;
187 });
188 // Delete
189 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [this, pos, file]()
190 {
191 KSMessageBox::Instance()->disconnect(this);
192 bool success = false;
193 if (this->m_permanentlyDelete == false && (success = file->moveToTrash()))
194 {
195 qCInfo(KSTARS_EKOS_CAPTURE) << m_overlay->currentFrame().filename << "moved to Trash.";
196 }
197 else if (this->m_permanentlyDelete && (success = file->remove()))
198 {
199 qCInfo(KSTARS_EKOS_CAPTURE) << m_overlay->currentFrame().filename << "deleted.";
200 }
201
202 if (success)
203 {
204 // delete it from the history and update the FITS view
205 if (m_overlay->deleteFrame(pos) && m_overlay->hasFrames())
206 {
207 m_fitsPreview->loadFile(m_overlay->currentFrame().filename);
208 // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
209 }
210 else
211 {
212 m_fitsPreview->clearData();
213 m_overlay->setEnabled(true);
214 }
215 }
216 else
217 {
218 qCWarning(KSTARS_EKOS_CAPTURE) << "Deleting" << m_overlay->currentFrame().filename <<
219 "failed!";
220 // give up
221 m_overlay->setEnabled(true);
222 }
223 // clear the check box
224 KSMessageBox::Instance()->setCheckBox(nullptr);
225 });
226
227 // Cancel
228 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [this]()
229 {
230 KSMessageBox::Instance()->disconnect(this);
231 // clear the check box
232 KSMessageBox::Instance()->setCheckBox(nullptr);
233 //do nothing
234 m_overlay->setEnabled(true);
235 });
236
237 // open the message box
238 QFileInfo fileinfo(current.filename);
239 KSMessageBox::Instance()->warningContinueCancel(i18n("Do you really want to delete %1 from the file system?",
240 fileinfo.fileName()),
241 i18n("Delete %1", fileinfo.fileName()), 0, false, i18n("Delete"));
242
243}
244
245void CapturePreviewWidget::setSummaryFITSView(SummaryFITSView *view)
246{
247 m_fitsPreview = view;
248 QVBoxLayout * vlayout = new QVBoxLayout();
249 vlayout->setContentsMargins(0, 0, 0, 0);
250 vlayout->addWidget(view);
251 previewWidget->setLayout(vlayout);
252 previewWidget->setContentsMargins(0, 0, 0, 0);
253
254 // initialize the FITS data overlay
255 // create vertically info box as overlay
256 QVBoxLayout *layout = new QVBoxLayout(view->processInfoWidget);
257 layout->addWidget(m_overlay, 0);
258
259 view->processInfoWidget->setLayout(layout);
260 // react upon signals
261 connect(view, &FITSView::loaded, [&]()
262 {
263 m_overlay->setEnabled(true);
264 });
265 connect(view, &FITSView::failed, [&]()
266 {
267 m_overlay->setEnabled(true);
268 });
269}
270
271void CapturePreviewWidget::setEnabled(bool enabled)
272{
273 // forward to sub widget
274 captureCountsWidget->setEnabled(enabled);
276}
277
278void CapturePreviewWidget::reset()
279{
280 m_overlay->setVisible(false);
281 // forward to sub widget
282 captureCountsWidget->reset();
283}
284
285void CapturePreviewWidget::updateCaptureStatus(Ekos::CaptureState status, bool isPreview, const QString &trainname)
286{
287 // forward to sub widgets
288 captureStatusWidget->setCaptureState(status);
289 captureCountsWidget->updateCaptureStatus(status, isPreview, trainname);
290}
291
292void CapturePreviewWidget::updateTargetDistance(double targetDiff)
293{
294 // forward it to the overlay
295 m_overlay->updateTargetDistance(targetDiff);
296}
297
298void CapturePreviewWidget::updateCaptureCountDown(int delta)
299{
300 // forward to sub widget
301 captureCountsWidget->updateCaptureCountDown(delta);
302}
303
304void CapturePreviewWidget::selectedTrainChanged(QString newName)
305{
306 m_overlay->setCurrentTrainName(newName);
307 captureCountsWidget->setCurrentTrainName(newName);
308
309 m_overlay->setEnabled(false);
310 if (m_overlay->hasFrames())
311 {
312 // Hint: since the FITSView loads in the background, we have to wait for FITSView::load() to enable the layer
313 m_fitsPreview->loadFile(m_overlay->currentFrame().filename);
314 }
315 else
316 {
317 m_fitsPreview->clearData();
318 m_overlay->setEnabled(true);
319 }
320}
321
322void CapturePreviewWidget::updateExposureProgress(Ekos::SequenceJob *job, const QString &trainname)
323{
324 if (trainname == cameraSelectionCB->currentText())
325 captureCountsWidget->updateExposureProgress(job, trainname);
326}
327
328void CapturePreviewWidget::updateDownloadProgress(double downloadTimeLeft, const QString &trainname)
329{
330 if (trainname == cameraSelectionCB->currentText())
331 captureCountsWidget->updateDownloadProgress(downloadTimeLeft, trainname);
332}
333
334void CapturePreviewWidget::setTargetName(QString name)
335{
336 targetLabel->setVisible(!name.isEmpty());
337 mountTarget->setVisible(!name.isEmpty());
338 mountTarget->setText(name);
339 m_mountTarget = name;
340 m_currentFrame[cameraSelectionCB->currentText()].target = name;
341}
342
Captures single or sequence of images from a CCD.
Definition capture.h:91
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)
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
void setEnabled(bool)
QLayout * layout() const const
void setLayout(QLayout *layout)
virtual void setVisible(bool visible)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Aug 30 2024 11:50:34 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.