Kstars

skycalendar.cpp
1/*
2 SPDX-FileCopyrightText: 2008 Jason Harris <kstars@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "skycalendar.h"
8
9#include "geolocation.h"
10#include "ksplanetbase.h"
11#include "kstarsdata.h"
12#include "dialogs/locationdialog.h"
13#include "skycomponents/skymapcomposite.h"
14
15#include <KPlotObject>
16
17#include <QPainter>
18#include <QPixmap>
19#include <QPrintDialog>
20#include <QPrinter>
21#include <QPushButton>
22#include <QScreen>
23#include <QtConcurrent>
24
25SkyCalendarUI::SkyCalendarUI(QWidget *parent) : QFrame(parent)
26{
27 setupUi(this);
28}
29
30SkyCalendar::SkyCalendar(QWidget *parent) : QDialog(parent)
31{
32#ifdef Q_OS_MACOS
33 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
34#endif
35
36 scUI = new SkyCalendarUI(this);
37
38 QVBoxLayout *mainLayout = new QVBoxLayout;
39
40 mainLayout->addWidget(scUI);
41 setLayout(mainLayout);
42
43 geo = KStarsData::Instance()->geo();
44
45 setWindowTitle(i18nc("@title:window", "Sky Calendar"));
46
48 mainLayout->addWidget(buttonBox);
49 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
50
51 QPushButton *printB = new QPushButton(QIcon::fromTheme("document-print"), i18n("&Print..."));
52 printB->setToolTip(i18n("Print the Sky Calendar"));
53 buttonBox->addButton(printB, QDialogButtonBox::ActionRole);
54 connect(printB, SIGNAL(clicked()), this, SLOT(slotPrint()));
55
56 setModal(false);
57
58 //Adjust minimum size for small screens:
59 if (QGuiApplication::primaryScreen()->geometry().height() <= scUI->CalendarView->height())
60 {
61 scUI->CalendarView->setMinimumSize(400, 600);
62 }
63
64 scUI->CalendarView->setShowGrid(false);
65 scUI->Year->setValue(KStarsData::Instance()->lt().date().year());
66
67 scUI->LocationButton->setText(geo->fullName());
68
69 scUI->CalendarView->setHorizon();
70
71 plotButtonText = scUI->CreateButton->text();
72 connect(scUI->CreateButton, &QPushButton::clicked, this, [this]()
73 {
74 scUI->CreateButton->setText(i18n("Please Wait") + "...");
75 slotFillCalendar();
76 });
77
78 connect(scUI->LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation()));
79}
80
81int SkyCalendar::year()
82{
83 return scUI->Year->value();
84}
85
86void SkyCalendar::slotFillCalendar()
87{
88 scUI->CreateButton->setEnabled(false);
89
90 scUI->CalendarView->resetPlot();
91 scUI->CalendarView->setHorizon();
92
93#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
94 if (scUI->checkBox_Mercury->isChecked())
95 QtConcurrent::run(&SkyCalendar::addPlanetEvents, this, KSPlanetBase::MERCURY);
96 if (scUI->checkBox_Venus->isChecked())
97 QtConcurrent::run(&SkyCalendar::addPlanetEvents, this, KSPlanetBase::VENUS);
98 if (scUI->checkBox_Mars->isChecked())
99 QtConcurrent::run(&SkyCalendar::addPlanetEvents, this, KSPlanetBase::MARS);
100 if (scUI->checkBox_Jupiter->isChecked())
101 QtConcurrent::run(&SkyCalendar::addPlanetEvents, this, KSPlanetBase::JUPITER);
102 if (scUI->checkBox_Saturn->isChecked())
103 QtConcurrent::run(&SkyCalendar::addPlanetEvents, this, KSPlanetBase::SATURN);
104 if (scUI->checkBox_Uranus->isChecked())
105 QtConcurrent::run(&SkyCalendar::addPlanetEvents, this, KSPlanetBase::URANUS);
106 if (scUI->checkBox_Neptune->isChecked())
107 QtConcurrent::run(&SkyCalendar::addPlanetEvents, this, KSPlanetBase::NEPTUNE);
108#else
109 if (scUI->checkBox_Mercury->isChecked())
110 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::MERCURY);
111 if (scUI->checkBox_Venus->isChecked())
112 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::VENUS);
113 if (scUI->checkBox_Mars->isChecked())
114 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::MARS);
115 if (scUI->checkBox_Jupiter->isChecked())
116 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::JUPITER);
117 if (scUI->checkBox_Saturn->isChecked())
118 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::SATURN);
119 if (scUI->checkBox_Uranus->isChecked())
120 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::URANUS);
121 if (scUI->checkBox_Neptune->isChecked())
122 QtConcurrent::run(this, &SkyCalendar::addPlanetEvents, KSPlanetBase::NEPTUNE);
123#endif
124
125 scUI->CreateButton->setText(i18n("Plot Planetary Almanac"));
126 scUI->CreateButton->setEnabled(true);
127}
128
129#if 0
130void SkyCalendar::slotCalculating()
131{
132 while (calculating)
133 {
134 //QMutexLocker locker(&calculationMutex);
135
136 if (!calculating)
137 continue;
138
139 scUI->CreateButton->setText(i18n("Please Wait") + ". ");
140 scUI->CreateButton->repaint();
141 QThread::msleep(200);
142 scUI->CreateButton->setText(i18n("Please Wait") + ".. ");
143 scUI->CreateButton->repaint();
144 QThread::msleep(200);
145
146 scUI->CreateButton->repaint();
147 QThread::msleep(200);
148 }
149 scUI->CreateButton->setText(plotButtonText);
150}
151#endif
152
153// FIXME: For the time being, adjust with dirty, cluttering labels that don't align to the line
154/*
155void SkyCalendar::drawEventLabel( float x1, float y1, float x2, float y2, QString LabelText ) {
156 QFont origFont = p->font();
157 p->setFont( QFont( "Bitstream Vera", 10 ) );
158
159 int textFlags = Qt::AlignCenter; // TODO: See if Qt::SingleLine flag works better
160 QFontMetricsF fm( p->font(), p->device() );
161
162 QRectF LabelRect = fm.boundingRect( QRectF(0,0,1,1), textFlags, LabelText );
163 QPointF LabelPoint = scUI->CalendarView->mapToWidget( QPointF( x, y ) );
164
165 float LabelAngle = atan2( y2 - y1, x2 - x1 )/dms::DegToRad;
166
167 p->save();
168 p->translate( LabelPoint );
169 p->rotate( LabelAngle );
170 p->drawText( LabelRect, textFlags, LabelText );
171 p->restore();
172
173 p->setFont( origFont );
174}
175*/
176
177void SkyCalendar::addPlanetEvents(int nPlanet)
178{
179 KSPlanetBase *ksp = KStarsData::Instance()->skyComposite()->planet(nPlanet);
180 QColor pColor = ksp->color();
181 //QVector<QPointF> vRise, vSet, vTransit;
182 std::vector<QPointF> vRise, vSet, vTransit;
183
184 for (KStarsDateTime kdt(QDate(year(), 1, 1), QTime(12, 0, 0)); kdt.date().year() == year();
185 kdt = kdt.addDays(scUI->spinBox_Interval->value()))
186 {
187 float rTime, sTime, tTime;
188
189 //Compute rise/set/transit times. If they occur before noon,
190 //recompute for the following day
191 QTime tmp_rTime = ksp->riseSetTime(kdt, geo, true, true); //rise time, exact
192 QTime tmp_sTime = ksp->riseSetTime(kdt, geo, false, true); //set time, exact
193 QTime tmp_tTime = ksp->transitTime(kdt, geo);
194 QTime midday(12, 0, 0);
195
196 // NOTE: riseSetTime should be fix now, this test is no longer necessary
197 if (tmp_rTime == tmp_sTime)
198 {
199 tmp_rTime = QTime();
200 tmp_sTime = QTime();
201 }
202
203 if (tmp_rTime.isValid() && tmp_sTime.isValid())
204 {
205 rTime = tmp_rTime.secsTo(midday) * 24.0 / 86400.0;
206 sTime = tmp_sTime.secsTo(midday) * 24.0 / 86400.0;
207
208 if (tmp_rTime <= midday)
209 rTime = 12.0 - rTime;
210 else
211 rTime = -12.0 - rTime;
212
213 if (tmp_sTime <= midday)
214 sTime = 12.0 - sTime;
215 else
216 sTime = -12.0 - sTime;
217 }
218 else
219 {
220 if (ksp->transitAltitude(kdt, geo).degree() > 0)
221 {
222 rTime = -24.0;
223 sTime = 24.0;
224 }
225 else
226 {
227 rTime = 24.0;
228 sTime = -24.0;
229 }
230 }
231
232 tTime = tmp_tTime.secsTo(midday) * 24.0 / 86400.0;
233 if (tmp_tTime <= midday)
234 tTime = 12.0 - tTime;
235 else
236 tTime = -12.0 - tTime;
237
238 float dy = kdt.date().daysInYear() - kdt.date().dayOfYear();
239 vRise.push_back(QPointF(rTime, dy));
240 vSet.push_back(QPointF(sTime, dy));
241 vTransit.push_back(QPointF(tTime, dy));
242 }
243
244 //Now, find continuous segments in each QVector and add each segment
245 //as a separate KPlotObject
246
247 KPlotObject *oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
248 KPlotObject *oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
249 KPlotObject *oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
250
251 float defaultSetTime = -10.0;
252 float defaultRiseTime = 8.0;
254 bool initialRise = true;
255 bool initialSet = true;
256 bool initialTransit = true;
257 bool extraCheckRise = true;
258 bool extraCheckSet = true;
259 bool extraCheckTransit = true;
260 bool needRiseLabel = false;
261 bool needSetLabel = false;
262 bool needTransertLabel = false;
263
264 float maxRiseTime = 0.0;
265 for (auto &item : vRise)
266 {
267 if (item.x() >= maxRiseTime)
268 maxRiseTime = item.x();
269 }
270 maxRiseTime = qFloor(maxRiseTime) - 1.0;
271
272 for (uint32_t i = 0; i < vRise.size(); ++i)
273 {
274 if (initialRise && (vRise.at(i).x() > defaultSetTime && vRise.at(i).x() < defaultRiseTime))
275 {
276 needRiseLabel = true;
277 initialRise = false;
278 }
279 else if (vRise.at(i).x() < defaultSetTime || vRise.at(i).x() > defaultRiseTime)
280 {
281 initialRise = true;
282 needRiseLabel = false;
283 }
284 else
285 needRiseLabel = false;
286
287 if (extraCheckRise && vRise.at(i).x() > defaultRiseTime && vRise.at(i).x() < maxRiseTime)
288 {
289 needRiseLabel = true;
290 extraCheckRise = false;
291 }
292
293 if (initialSet && (vSet.at(i).x() > defaultSetTime && vSet.at(i).x() < defaultRiseTime))
294 {
295 needSetLabel = true;
296 initialSet = false;
297 }
298 else if (vSet.at(i).x() < defaultSetTime || vSet.at(i).x() > defaultRiseTime)
299 {
300 initialSet = true;
301 needSetLabel = false;
302 }
303 else
304 needSetLabel = false;
305
306 if (extraCheckSet && vSet.at(i).x() > defaultRiseTime && vSet.at(i).x() < maxRiseTime)
307 {
308 needSetLabel = true;
309 extraCheckSet = false;
310 }
311
312 if (initialTransit && (vTransit.at(i).x() > defaultSetTime && vTransit.at(i).x() < defaultRiseTime))
313 {
314 needTransertLabel = true;
315 initialTransit = false;
316 }
317 else if (vTransit.at(i).x() < defaultSetTime || vTransit.at(i).x() > defaultRiseTime)
318 {
319 initialTransit = true;
320 needTransertLabel = false;
321 ;
322 }
323 else
324 needTransertLabel = false;
325
326 if (extraCheckTransit && vTransit.at(i).x() > defaultRiseTime && vTransit.at(i).x() < maxRiseTime)
327 {
328 needTransertLabel = true;
329 extraCheckTransit = false;
330 }
331
332 if (vRise.at(i).x() > -23.0 && vRise.at(i).x() < 23.0)
333 {
334 if (i > 0 && fabs(vRise.at(i).x() - vRise.at(i - 1).x()) > 6.0)
335 {
336 scUI->CalendarView->addPlotObject(oRise);
337 oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
338 extraCheckRise = true;
339 }
340
341 if (needRiseLabel)
342 label = i18nc("A planet rises from the horizon", "%1 rises", ksp->name());
343 else
344 label = QString();
345 // Add the current point to KPlotObject
346 oRise->addPoint(vRise.at(i), label);
347 }
348 else
349 {
350 scUI->CalendarView->addPlotObject(oRise);
351 oRise = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
352 }
353
354 if (vSet.at(i).x() > -23.0 && vSet.at(i).x() < 23.0)
355 {
356 if (i > 0 && fabs(vSet.at(i).x() - vSet.at(i - 1).x()) > 6.0)
357 {
358 scUI->CalendarView->addPlotObject(oSet);
359 oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
360 extraCheckSet = true;
361 }
362
363 if (needSetLabel)
364 label = i18nc("A planet sets from the horizon", "%1 sets", ksp->name());
365 else
366 label = QString();
367
368 oSet->addPoint(vSet.at(i), label);
369 }
370 else
371 {
372 scUI->CalendarView->addPlotObject(oSet);
373 oSet = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
374 }
375
376 if (vTransit.at(i).x() > -23.0 && vTransit.at(i).x() < 23.0)
377 {
378 if (i > 0 && fabs(vTransit.at(i).x() - vTransit.at(i - 1).x()) > 6.0)
379 {
380 scUI->CalendarView->addPlotObject(oTransit);
381 oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
382 extraCheckTransit = true;
383 }
384
385 if (needTransertLabel)
386 label = i18nc("A planet transits across the meridian", "%1 transits", ksp->name());
387 else
388 label = QString();
389
390 oTransit->addPoint(vTransit.at(i), label);
391 }
392 else
393 {
394 scUI->CalendarView->addPlotObject(oTransit);
395 oTransit = new KPlotObject(pColor, KPlotObject::Lines, 2.0);
396 }
397 }
398
399 // Add the last points
400 scUI->CalendarView->addPlotObject(oRise);
401 scUI->CalendarView->addPlotObject(oSet);
402 scUI->CalendarView->addPlotObject(oTransit);
403}
404
405void SkyCalendar::slotPrint()
406{
407 QPainter p; // Our painter object
408 QPrinter printer; // Our printer object
409 QString str_legend; // Text legend
410 QString str_year; // Calendar's year
411 int text_height = 200; // Height of legend text zone in points
412 QSize calendar_size; // Initial calendar widget size
413 QFont calendar_font; // Initial calendar font
414 int calendar_font_size; // Initial calendar font size
415
416 // Set printer resolution to 300 dpi
417 printer.setResolution(300);
418
419 // Open print dialog
420 //NOTE Changed from pointer to statically allocated object, what effect will it have?
421 //QPointer<QPrintDialog> dialog( KdePrint::createPrintDialog( &printer, this ) );
422 QPrintDialog dialog(&printer, this);
423 dialog.setWindowTitle(i18nc("@title:window", "Print sky calendar"));
424 if (dialog.exec() == QDialog::Accepted)
425 {
426 // Change mouse cursor
428
429 // Save calendar widget font
430 calendar_font = scUI->CalendarView->font();
431 // Save calendar widget font size
432 calendar_font_size = calendar_font.pointSize();
433 // Save calendar widget size
434 calendar_size = scUI->CalendarView->size();
435
436 // Set text legend
437 str_year.setNum(year());
438 str_legend = i18n("Sky Calendar");
439 str_legend += '\n';
440 str_legend += geo->fullName();
441 str_legend += " - ";
442 str_legend += str_year;
443
444 // Create a rectangle for legend text zone
445 QRect text_rect(0, 0, printer.width(), text_height);
446
447 // Increase calendar widget font size so it looks good in 300 dpi
448 calendar_font.setPointSize(calendar_font_size * 3);
449 scUI->CalendarView->setFont(calendar_font);
450 // Increase calendar widget size to fit the entire page
451 scUI->CalendarView->resize(printer.width(), printer.height() - text_height);
452
453 // Create a pixmap and render calendar widget into it
454 QPixmap pixmap(scUI->CalendarView->size());
455 scUI->CalendarView->render(&pixmap);
456
457 // Begin painting on printer
458 p.begin(&printer);
459 // Draw legend
460 p.drawText(text_rect, Qt::AlignLeft, str_legend);
461 // Draw calendar
462 p.drawPixmap(0, text_height, pixmap);
463 // Ending painting
464 p.end();
465
466 // Restore calendar widget font size
467 calendar_font.setPointSize(calendar_font_size);
468 scUI->CalendarView->setFont(calendar_font);
469 // Restore calendar widget size
470 scUI->CalendarView->resize(calendar_size);
471
472 // Restore mouse cursor
474 }
475 //delete dialog;
476}
477
478void SkyCalendar::slotLocation()
479{
481 if (ld->exec() == QDialog::Accepted)
482 {
483 GeoLocation *newGeo = ld->selectedCity();
484 if (newGeo)
485 {
486 geo = newGeo;
487 scUI->LocationButton->setText(geo->fullName());
488 }
489 }
490 delete ld;
491
492 scUI->CalendarView->setHorizon();
493 slotFillCalendar();
494}
495
496GeoLocation *SkyCalendar::get_geo()
497{
498 return geo;
499}
Contains all relevant information for specifying a location on Earth: City Name, State/Province name,...
Definition geolocation.h:28
QString fullName() const
void addPoint(const QPointF &p, const QString &label=QString(), double barWidth=0.0)
A subclass of TrailObject that provides additional information needed for most solar system objects.
QColor & color()
GeoLocation * geo()
Definition kstarsdata.h:230
SkyMapComposite * skyComposite()
Definition kstarsdata.h:166
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
Dialog for changing the geographic location of the observer.
virtual QString name(void) const
Definition skyobject.h:145
dms transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const
QTime transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const
The same iteration technique described in riseSetTime() is used here.
QTime riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact=true) const
Determine the time at which the point will rise or set.
Definition skyobject.cpp:93
int degree() const
Definition dms.h:116
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
GeoCoordinates geo(const QVariant &location)
QString label(StandardShortcut id)
void clicked(bool checked)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QDate date() const const
QPushButton * addButton(StandardButton button)
int pointSize() const const
void setPointSize(int pointSize)
void restoreOverrideCursor()
void setOverrideCursor(const QCursor &cursor)
QIcon fromTheme(const QString &name)
int height() const const
int width() const const
bool begin(QPaintDevice *device)
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
void drawText(const QPoint &position, const QString &text)
bool end()
void setResolution(int dpi)
QString & setNum(double n, char format, int precision)
AlignLeft
WaitCursor
QFuture< T > run(Function function,...)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void msleep(unsigned long msecs)
bool isValid(int h, int m, int s, int ms)
int secsTo(QTime t) const const
void setEnabled(bool)
void render(QPaintDevice *target, const QPoint &targetOffset, const QRegion &sourceRegion, RenderFlags renderFlags)
void repaint()
void setToolTip(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 11 2024 12:15:13 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.