Kstars

fovdialog.cpp
1/*
2 SPDX-FileCopyrightText: 2003 Jason Harris <kstars@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "fovdialog.h"
8
9#include <QFile>
10#include <QFrame>
11#include <QPainter>
12#include <QTextStream>
13#include <QPaintEvent>
14#include <QDebug>
15#include <QPushButton>
16#include <QComboBox>
17#include <QDoubleSpinBox>
18#include <QLineEdit>
19#include <QDBusReply>
20#include <QDBusInterface>
21#include <QPointer>
22
23#include <KActionCollection>
24#include <KLocalizedString>
25#include <kcolorbutton.h>
26#include <KMessageBox>
27
28#include "kstars.h"
29#include "kstarsdata.h"
30#include "widgets/fovwidget.h"
31#include "Options.h"
32
33// This is needed to make FOV work with QVariant
34Q_DECLARE_METATYPE(FOV *)
35
36int FOVDialog::fovID = -1;
37
38namespace
39{
40// Try to convert text in KLine edit to double
41inline double textToDouble(const QLineEdit *edit, bool *ok = nullptr)
42{
43 return edit->text().replace(QLocale().decimalPoint(), ".").toDouble(ok);
44}
45
46// Extract FOV from QListWidget. No checking is done
47FOV *getFOV(QListWidgetItem *item)
48{
49 return item->data(Qt::UserRole).value<FOV *>();
50}
51
52// Convert double to QString
53QString toString(double x, int precision = 2)
54{
55 return QString::number(x, 'f', precision).replace('.', QLocale().decimalPoint());
56}
57}
58
59FOVDialogUI::FOVDialogUI(QWidget *parent) : QFrame(parent)
60{
61 setupUi(this);
62}
63
64NewFOVUI::NewFOVUI(QWidget *parent) : QFrame(parent)
65{
66 setupUi(this);
67}
68
69//---------FOVDialog---------------//
70FOVDialog::FOVDialog(QWidget *p) : QDialog(p)
71{
72#ifdef Q_OS_OSX
73 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
74#endif
75 // Register FOV* data type
76 if (fovID == -1)
77 fovID = qRegisterMetaType<FOV *>("FOV*");
78 fov = new FOVDialogUI(this);
79
80 setWindowTitle(i18nc("@title:window", "Set FOV Indicator"));
81
82 QVBoxLayout *mainLayout = new QVBoxLayout;
83 mainLayout->addWidget(fov);
84 setLayout(mainLayout);
85
87 mainLayout->addWidget(buttonBox);
88 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
89 connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
90
91 connect(fov->FOVListBox, SIGNAL(currentRowChanged(int)), SLOT(slotSelect(int)));
92 connect(fov->NewButton, SIGNAL(clicked()), SLOT(slotNewFOV()));
93 connect(fov->EditButton, SIGNAL(clicked()), SLOT(slotEditFOV()));
94 connect(fov->RemoveButton, SIGNAL(clicked()), SLOT(slotRemoveFOV()));
95
96 // Read list of FOVs and for each FOV create listbox entry, which stores it.
97 foreach (FOV *f, FOVManager::getFOVs())
98 {
99 addListWidget(f);
100 }
101}
102
103FOVDialog::~FOVDialog()
104{
105 // Delete FOVs
106 //for(int i = 0; i < fov->FOVListBox->count(); i++) {
107 //delete getFOV( fov->FOVListBox->item(i) );
108 //}
109}
110
111QListWidgetItem *FOVDialog::addListWidget(FOV *f)
112{
113 QListWidgetItem *item = new QListWidgetItem(f->name(), fov->FOVListBox);
114 item->setData(Qt::UserRole, QVariant::fromValue<FOV *>(f));
115 return item;
116}
117
118void FOVDialog::slotSelect(int irow)
119{
120 bool enable = irow >= 0;
121 fov->RemoveButton->setEnabled(enable);
122 fov->EditButton->setEnabled(enable);
123 if (enable)
124 {
125 //paint dialog with selected FOV symbol
126 fov->ViewBox->setFOV(getFOV(fov->FOVListBox->currentItem()));
127 fov->ViewBox->update();
128 }
129}
130
131void FOVDialog::slotNewFOV()
132{
133 QPointer<NewFOV> newfdlg = new NewFOV(this);
134 if (newfdlg->exec() == QDialog::Accepted)
135 {
136 FOV *newfov = new FOV(newfdlg->getFOV());
137 FOVManager::addFOV(newfov);
138 addListWidget(newfov);
139 fov->FOVListBox->setCurrentRow(fov->FOVListBox->count() - 1);
140 }
141 delete newfdlg;
142}
143
144void FOVDialog::slotEditFOV()
145{
146 //Preload current values
147 QListWidgetItem *item = fov->FOVListBox->currentItem();
148 if (item == nullptr)
149 return;
150 FOV *f = item->data(Qt::UserRole).value<FOV *>();
151
152 // Create dialog
153 QPointer<NewFOV> newfdlg = new NewFOV(this, f);
154 if (newfdlg->exec() == QDialog::Accepted)
155 {
156 // Overwrite FOV
157 f->sync(newfdlg->getFOV());
158 fov->ViewBox->update();
159 }
160 delete newfdlg;
161}
162
163void FOVDialog::slotRemoveFOV()
164{
165 int i = fov->FOVListBox->currentRow();
166 if (i >= 0)
167 {
168 QListWidgetItem *item = fov->FOVListBox->takeItem(i);
169 FOVManager::removeFOV(getFOV(item));
170 delete item;
171 }
172}
173
174//-------------NewFOV------------------//
175
176NewFOV::NewFOV(QWidget *parent, const FOV *fov) : QDialog(parent), f()
177{
178 ui = new NewFOVUI(this);
179
180 setWindowTitle(i18nc("@title:window", "New FOV Indicator"));
181
182 QVBoxLayout *mainLayout = new QVBoxLayout;
183 mainLayout->addWidget(ui);
184 setLayout(mainLayout);
185
187 mainLayout->addWidget(buttonBox);
188 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
189 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
190
191 okB = buttonBox->button(QDialogButtonBox::Ok);
192
193 // Initialize FOV if required
194 if (fov != nullptr)
195 {
196 f.sync(*fov);
197 ui->FOVName->setText(f.name());
198 ui->FOVEditX->setText(toString(f.sizeX()));
199 ui->FOVEditY->setText(toString(f.sizeY()));
200 ui->FOVEditOffsetX->setText(toString(f.offsetX()));
201 ui->FOVEditOffsetY->setText(toString(f.offsetY()));
202 ui->FOVEditRotation->setText(toString(f.PA()));
203 ui->ColorButton->setColor(QColor(f.color()));
204 ui->ShapeBox->setCurrentIndex(f.shape());
205 ui->FOVLockCP->setChecked(f.lockCelestialPole());
206
207 ui->TLength2->setValue(Options::telescopeFocalLength());
208 ui->cameraWidth->setValue(Options::cameraWidth());
209 ui->cameraHeight->setValue(Options::cameraHeight());
210 ui->cameraPixelSizeW->setValue(Options::cameraPixelWidth());
211 ui->cameraPixelSizeH->setValue(Options::cameraPixelHeight());
212
213 ui->ViewBox->setFOV(&f);
214 ui->ViewBox->update();
215 }
216
217 connect(ui->FOVName, SIGNAL(textChanged(QString)), SLOT(slotUpdateFOV()));
218 connect(ui->FOVEditX, SIGNAL(textChanged(QString)), SLOT(slotUpdateFOV()));
219 connect(ui->FOVEditY, SIGNAL(textChanged(QString)), SLOT(slotUpdateFOV()));
220 connect(ui->FOVEditOffsetX, SIGNAL(textChanged(QString)), SLOT(slotUpdateFOV()));
221 connect(ui->FOVEditOffsetY, SIGNAL(textChanged(QString)), SLOT(slotUpdateFOV()));
222 connect(ui->FOVEditRotation, SIGNAL(textChanged(QString)), SLOT(slotUpdateFOV()));
223 connect(ui->FOVLockCP, SIGNAL(toggled(bool)), SLOT(slotUpdateFOV()));
224 connect(ui->ColorButton, SIGNAL(changed(QColor)), SLOT(slotUpdateFOV()));
225 connect(ui->ShapeBox, SIGNAL(activated(int)), SLOT(slotUpdateFOV()));
226 connect(ui->ComputeEyeFOV, SIGNAL(clicked()), SLOT(slotComputeFOV()));
227 connect(ui->ComputeCameraFOV, SIGNAL(clicked()), SLOT(slotComputeFOV()));
228 connect(ui->ComputeHPBW, SIGNAL(clicked()), SLOT(slotComputeFOV()));
229 connect(ui->ComputeBinocularFOV, SIGNAL(clicked()), SLOT(slotComputeFOV()));
230 connect(ui->ComputeTLengthFromFNum1, SIGNAL(clicked()), SLOT(slotComputeTelescopeFL()));
231 connect(ui->DetectFromINDI, SIGNAL(clicked()), SLOT(slotDetectFromINDI()));
232
233#ifndef HAVE_INDI
234 ui->DetectFromINDI->setEnabled(false);
235#endif
236
237 // Populate eyepiece AFOV options. The userData field contains the apparent FOV associated with that option
238 ui->EyepieceAFOV->insertItem(0, i18nc("Specify the apparent field of view (AFOV) manually", "Specify AFOV"), -1);
239 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Ramsden (Typical)"), 30);
240 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Orthoscopic (Typical)"), 45);
241 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Ploessl (Typical)"), 50);
242 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Erfle (Typical)"), 60);
243 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Tele Vue Radian"), 60);
244 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Baader Hyperion"), 68);
245 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Tele Vue Panoptic"), 68);
246 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Tele Vue Delos"), 72);
247 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Meade UWA"), 82);
248 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Tele Vue Nagler"), 82);
249 ui->EyepieceAFOV->addItem(i18nc("Eyepiece Design / Brand / Name; Optional", "Tele Vue Ethos (Typical)"), 100);
250
251 connect(ui->EyepieceAFOV, SIGNAL(currentIndexChanged(int)), SLOT(slotEyepieceAFOVChanged(int)));
252
253 ui->LinearFOVDistance->insertItem(0, i18n("1000 yards"));
254 ui->LinearFOVDistance->insertItem(1, i18n("1000 meters"));
255 connect(ui->LinearFOVDistance, SIGNAL(currentIndexChanged(int)), SLOT(slotBinocularFOVDistanceChanged(int)));
256
257 slotUpdateFOV();
258}
259
260void NewFOV::slotBinocularFOVDistanceChanged(int index)
261{
262 QString text = (index == 0 ? i18n("feet") : i18n("meters"));
263 ui->LabelUnits->setText(text);
264}
265
266void NewFOV::slotUpdateFOV()
267{
268 bool okX, okY;
269 f.setName(ui->FOVName->text());
270 float sizeX = textToDouble(ui->FOVEditX, &okX);
271 float sizeY = textToDouble(ui->FOVEditY, &okY);
272 if (okX && okY)
273 f.setSize(sizeX, sizeY);
274
275 float xoffset = textToDouble(ui->FOVEditOffsetX, &okX);
276 float yoffset = textToDouble(ui->FOVEditOffsetY, &okY);
277 if (okX && okY)
278 f.setOffset(xoffset, yoffset);
279
280 float rot = textToDouble(ui->FOVEditRotation, &okX);
281 if (okX)
282 f.setPA(rot);
283
284 f.setShape(static_cast<FOV::Shape>(ui->ShapeBox->currentIndex()));
285 f.setColor(ui->ColorButton->color().name());
286 f.setLockCelestialPole(ui->FOVLockCP->isChecked());
287
288 okB->setEnabled(!f.name().isEmpty() && okX && okY);
289
290 ui->ViewBox->setFOV(&f);
291 ui->ViewBox->update();
292}
293
294void NewFOV::slotComputeFOV()
295{
296 if (sender() == ui->ComputeEyeFOV && ui->TLength1->value() > 0.0)
297 {
298 ui->FOVEditX->setText(toString(60.0 * ui->EyeFOV->value() * ui->EyeLength->value() / ui->TLength1->value()));
299 ui->FOVEditY->setText(ui->FOVEditX->text());
300 }
301 else if (sender() == ui->ComputeCameraFOV && ui->TLength2->value() > 0.0)
302 {
303 /*double sx = (double)ui->ChipWidth->value() * 3438.0 / ui->TLength2->value();
304 double sy = (double)ui->ChipHeight->value() * 3438.0 / ui->TLength2->value();
305 //const double aspectratio = 3.0/2.0; // Use the default aspect ratio for DSLRs / Film (i.e. 3:2)*/
306
307 // FOV in arcmins
308 double fov_x = 206264.8062470963552 * ui->cameraWidth->value() * ui->cameraPixelSizeW->value() / 60000.0 / ui->TLength2->value();
309 double fov_y = 206264.8062470963552 * ui->cameraHeight->value() * ui->cameraPixelSizeH->value() / 60000.0 / ui->TLength2->value();
310
311 ui->FOVEditX->setText(toString(fov_x));
312 ui->FOVEditY->setText(toString(fov_y));
313 }
314 else if (sender() == ui->ComputeHPBW && ui->RTDiameter->value() > 0.0 && ui->WaveLength->value() > 0.0)
315 {
316 ui->FOVEditX->setText(toString(34.34 * 1.2 * ui->WaveLength->value() / ui->RTDiameter->value()));
317 // Beam width for an antenna is usually a circle on the sky.
318 ui->ShapeBox->setCurrentIndex(4);
319 ui->FOVEditY->setText(ui->FOVEditX->text());
320 slotUpdateFOV();
321 }
322 else if (sender() == ui->ComputeBinocularFOV && ui->LinearFOV->value() > 0.0 &&
323 ui->LinearFOVDistance->currentIndex() >= 0)
324 {
325 double sx =
326 atan((double)ui->LinearFOV->value() / ((ui->LinearFOVDistance->currentIndex() == 0) ? 3000.0 : 1000.0)) *
327 180.0 * 60.0 / dms::PI;
328 ui->FOVEditX->setText(toString(sx));
329 ui->FOVEditY->setText(ui->FOVEditX->text());
330 }
331}
332
333void NewFOV::slotEyepieceAFOVChanged(int index)
334{
335 if (index == 0)
336 {
337 ui->EyeFOV->setEnabled(true);
338 }
339 else
340 {
341 bool ok;
342 ui->EyeFOV->setEnabled(false);
343 ui->EyeFOV->setValue(ui->EyepieceAFOV->itemData(index).toFloat(&ok));
344 Q_ASSERT(ok);
345 }
346}
347
348void NewFOV::slotComputeTelescopeFL()
349{
352 {
353 ui->TLength1->setValue(telescopeFLDialog->computeFL());
354 }
355 delete telescopeFLDialog;
356}
357
358void NewFOV::slotDetectFromINDI()
359{
360 QDBusInterface alignInterface("org.kde.kstars",
361 "/KStars/Ekos/Align",
362 "org.kde.kstars.Ekos.Align",
364
365 QDBusReply<QList<double>> cameraReply = alignInterface.call("cameraInfo");
366 if (cameraReply.isValid())
367 {
368 QList<double> values = cameraReply.value();
369
370 ui->cameraWidth->setValue(values[0]);
371 ui->cameraHeight->setValue(values[1]);
372 ui->cameraPixelSizeW->setValue(values[2]);
373 ui->cameraPixelSizeH->setValue(values[3]);
374 }
375
376 QDBusReply<QList<double>> telescopeReply = alignInterface.call("telescopeInfo");
377 if (telescopeReply.isValid())
378 {
379 QList<double> values = telescopeReply.value();
380 ui->TLength2->setValue(values[0]);
381 }
382
383 QDBusReply<QList<double>> solutionReply = alignInterface.call("getSolutionResult");
384 if (solutionReply.isValid())
385 {
386 QList<double> values = solutionReply.value();
387 if (values[0] > -1e6)
388 ui->FOVEditRotation->setText(QString::number(values[0]));
389 }
390}
391
392
393//-------------TelescopeFL------------------//
394
395TelescopeFL::TelescopeFL(QWidget *parent) : QDialog(parent), aperture(nullptr), fNumber(nullptr), apertureUnit(nullptr)
396{
397 setWindowTitle(i18nc("@title:window", "Telescope Focal Length Calculator"));
398
399 //QWidget *mainWidget = new QWidget( this );
400 QGridLayout *mainLayout = new QGridLayout(this);
401 setLayout(mainLayout);
402
403 aperture = new QDoubleSpinBox();
404 aperture->setRange(0.0, 100000.0);
405 aperture->setDecimals(2);
406 aperture->setSingleStep(0.1);
407
408 fNumber = new QDoubleSpinBox();
409 fNumber->setRange(0.0, 99.9);
410 fNumber->setDecimals(2);
411 fNumber->setSingleStep(0.1);
412
413 apertureUnit = new QComboBox(this);
414 apertureUnit->insertItem(0, i18nc("millimeters", "mm"));
415 apertureUnit->insertItem(1, i18n("inch"));
416
417 mainLayout->addWidget(new QLabel(i18n("Aperture diameter: "), this), 0, 0);
418 mainLayout->addWidget(aperture, 0, 1);
419 mainLayout->addWidget(apertureUnit, 0, 2);
420 mainLayout->addWidget(new QLabel(i18nc("F-Number or F-Ratio of optical system", "F-Number: "), this), 1, 0);
421 mainLayout->addWidget(fNumber, 1, 1);
422
424 mainLayout->addWidget(buttonBox);
425 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
426 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
427
428 show();
429}
430
432{
433 const double inch_to_mm = 25.4; // 1 inch, by definition, is 25.4 mm
434 return (aperture->value() * fNumber->value() *
435 ((apertureUnit->currentIndex() == 1) ?
436 inch_to_mm :
437 1.0)); // Focal Length = Aperture * F-Number, by definition of F-Number
438}
439
440unsigned int FOVDialog::currentItem() const
441{
442 return fov->FOVListBox->currentRow();
443}
A simple class encapsulating a Field-of-View symbol.
Definition fov.h:28
Dialog for defining a new FOV symbol.
Definition fovdialog.h:64
NewFOV(QWidget *parent=nullptr, const FOV *fov=nullptr)
Create new dialog.
Dialog for calculating telescope focal length from f-number and diameter.
Definition fovdialog.h:101
double computeFL() const
Compute and return the focal length in mm.
TelescopeFL(QWidget *parent=nullptr)
Create a telescope focal length dialog.
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
QAction * close(const QObject *recvr, const char *slot, QObject *parent)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void insertItem(int index, const QIcon &icon, const QString &text, const QVariant &userData)
QDBusConnection sessionBus()
virtual void accept()
void accepted()
virtual void reject()
void rejected()
QPushButton * button(StandardButton which) const const
void setDecimals(int prec)
void setRange(double minimum, double maximum)
void setSingleStep(double val)
void addWidget(QWidget *widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, Qt::Alignment alignment)
virtual QVariant data(int role) const const
virtual void setData(int role, const QVariant &value)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * sender() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
UserRole
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setEnabled(bool)
void setLayout(QLayout *layout)
void show()
void update()
void setWindowTitle(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:48:25 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.