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

KDE's Doxygen guidelines are available online.