Changeset View
Standalone View
kstars/auxiliary/xplanetimageviewer.cpp
- This file was added.
1 | | ||||
---|---|---|---|---|---|
2 | /*************************************************************************** | ||||
3 | XPlanetImageviewer.cpp - Based on: KStars Image Viwer by Thomas Kabelmann | ||||
4 | ------------------- | ||||
5 | begin : Sun Aug 12, 2018 | ||||
6 | copyright : (C) 2018 by Robert Lancaster | ||||
7 | email : rlancaste@gmail.com | ||||
8 | ***************************************************************************/ | ||||
9 | | ||||
10 | /*************************************************************************** | ||||
11 | * * | ||||
12 | * This program is free software; you can redistribute it and/or modify * | ||||
13 | * it under the terms of the GNU General Public License as published by * | ||||
14 | * the Free Software Foundation; either version 2 of the License, or * | ||||
15 | * (at your option) any later version. * | ||||
16 | * * | ||||
17 | ***************************************************************************/ | ||||
18 | | ||||
19 | #include "xplanetimageviewer.h" | ||||
20 | #include "Options.h" | ||||
21 | #include "dialogs/timedialog.h" | ||||
22 | #include <QtConcurrent> | ||||
23 | | ||||
24 | #ifndef KSTARS_LITE | ||||
25 | #include "kstars.h" | ||||
26 | #endif | ||||
27 | | ||||
28 | #ifndef KSTARS_LITE | ||||
29 | #include <KMessageBox> | ||||
30 | #endif | ||||
31 | | ||||
32 | #include <QDesktopWidget> | ||||
33 | #include <QFileDialog> | ||||
34 | #include <QPainter> | ||||
35 | #include <QResizeEvent> | ||||
36 | #include <QStatusBar> | ||||
37 | #include <QTemporaryFile> | ||||
38 | #include <QVBoxLayout> | ||||
39 | #include <QPushButton> | ||||
40 | #include <QApplication> | ||||
41 | #include <QtWidgets/QSlider> | ||||
42 | #include "skymap.h" | ||||
43 | #include "kspaths.h" | ||||
44 | | ||||
45 | #include <QUuid> | ||||
46 | #include <sys/stat.h> | ||||
47 | | ||||
48 | XPlanetImageLabel::XPlanetImageLabel(QWidget *parent) : QFrame(parent) | ||||
49 | { | ||||
50 | #ifndef KSTARS_LITE | ||||
51 | grabGesture(Qt::PinchGesture); | ||||
pino: You are setting a non-empty frame, but then you are neither taking this into account when… | |||||
I didn't write this code, it was in ImageViewer. But I can look at it. lancaster: I didn't write this code, it was in ImageViewer. But I can look at it. | |||||
52 | setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); | ||||
53 | setFrameStyle(QFrame::StyledPanel | QFrame::Plain); | ||||
54 | setLineWidth(2); | ||||
55 | #endif | ||||
56 | } | ||||
57 | | ||||
58 | void XPlanetImageLabel::setImage(const QImage &img) | ||||
59 | { | ||||
60 | #ifndef KSTARS_LITE | ||||
61 | m_Image = img; | ||||
62 | pix = QPixmap::fromImage(m_Image); | ||||
63 | #endif | ||||
64 | } | ||||
65 | | ||||
66 | void XPlanetImageLabel::invertPixels() | ||||
67 | { | ||||
68 | #ifndef KSTARS_LITE | ||||
69 | m_Image.invertPixels(); | ||||
70 | pix = QPixmap::fromImage(m_Image.scaled(width(), height(), Qt::KeepAspectRatio)); | ||||
71 | #endif | ||||
You can use the paint even object to know which parts were "damaged", and thus limit the drawPixmap below to the interested region(s). This will speed up drawing a bit. pino: You can use the paint even object to know which parts were "damaged", and thus limit the… | |||||
I didn't write this code, it was in ImageViewer. But this is a very good idea. lancaster: I didn't write this code, it was in ImageViewer. But this is a very good idea. | |||||
72 | } | ||||
73 | | ||||
74 | void XPlanetImageLabel::paintEvent(QPaintEvent *) | ||||
75 | { | ||||
76 | #ifndef KSTARS_LITE | ||||
77 | QPainter p; | ||||
78 | p.begin(this); | ||||
Considering that resizeEvent resizes the pixmap to the window size, then this code is effectively dead... pino: Considering that `resizeEvent` resizes the pixmap to the window size, then this code is… | |||||
I didn't write this code, it was in ImageViewer. But I can look at it. You are probably right lancaster: I didn't write this code, it was in ImageViewer. But I can look at it. You are probably right | |||||
79 | int x = 0; | ||||
80 | if (pix.width() < width()) | ||||
81 | x = (width() - pix.width()) / 2; | ||||
82 | p.drawPixmap(x, 0, pix); | ||||
83 | p.end(); | ||||
84 | #endif | ||||
85 | } | ||||
86 | | ||||
87 | void XPlanetImageLabel::resizeEvent(QResizeEvent *event) | ||||
88 | { | ||||
89 | if (event->size() == pix.size()) | ||||
pino: Just compare the sizes, eg
lang=c++
if (event->size() == pix.size()) | |||||
I didn't write this code, it was in ImageViewer. But you are right about it, it is simpler lancaster: I didn't write this code, it was in ImageViewer. But you are right about it, it is simpler | |||||
90 | return; | ||||
91 | | ||||
92 | pix = QPixmap::fromImage(m_Image.scaled(event->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); | ||||
93 | } | ||||
94 | | ||||
95 | void XPlanetImageLabel::refreshImage() | ||||
96 | { | ||||
97 | pix = QPixmap::fromImage(m_Image.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation)); | ||||
98 | update(); | ||||
99 | } | ||||
100 | | ||||
101 | void XPlanetImageLabel::wheelEvent(QWheelEvent *e) | ||||
102 | { | ||||
103 | //This attempts to send the wheel event back to the Scroll Area if it was taken from a trackpad | ||||
104 | //It should still do the zoom if it is a mouse wheel | ||||
105 | if (e->source() == Qt::MouseEventSynthesizedBySystem) | ||||
106 | { | ||||
107 | QFrame::wheelEvent(e); | ||||
108 | } | ||||
109 | else | ||||
110 | { | ||||
111 | if (e->delta() > 0) | ||||
112 | emit zoomIn(); | ||||
113 | else if (e->delta() < 0) | ||||
114 | emit zoomOut(); | ||||
115 | e->accept(); | ||||
116 | } | ||||
117 | } | ||||
118 | | ||||
119 | bool XPlanetImageLabel::event(QEvent *event) | ||||
120 | { | ||||
121 | if (event->type() == QEvent::Gesture) | ||||
122 | return gestureEvent(dynamic_cast<QGestureEvent *>(event)); | ||||
123 | return QFrame::event(event); | ||||
124 | } | ||||
125 | | ||||
126 | bool XPlanetImageLabel::gestureEvent(QGestureEvent *event) | ||||
127 | { | ||||
128 | if (QGesture *pinch = event->gesture(Qt::PinchGesture)) | ||||
129 | pinchTriggered(dynamic_cast<QPinchGesture *>(pinch)); | ||||
130 | return true; | ||||
131 | } | ||||
132 | | ||||
133 | | ||||
No way to get all the objects from some other kstars API, instead of hardcoding them all? pino: No way to get all the objects from some other kstars API, instead of hardcoding them all? | |||||
Most of them do not exist in KStars since they are moons and KStars doesn't currently handle them. The planets, Sun, and Moon could come from KStars' planet list, but then they wouldn't be in the right order with the moons. lancaster: Most of them do not exist in KStars since they are moons and KStars doesn't currently handle… | |||||
134 | void XPlanetImageLabel::pinchTriggered(QPinchGesture *gesture) | ||||
135 | { | ||||
136 | if (gesture->totalScaleFactor() > 1) | ||||
137 | emit zoomIn(); | ||||
138 | else | ||||
139 | emit zoomOut(); | ||||
140 | } | ||||
This is not exactly an ideal way to indent, and it will not work with some languages (beside that, translators can think the extra spaces are typos, and ingore them). pino: This is not exactly an ideal way to indent, and it will not work with some languages (beside… | |||||
lancaster: good idea | |||||
141 | | ||||
142 | | ||||
143 | void XPlanetImageLabel::mousePressEvent(QMouseEvent *e) | ||||
144 | { | ||||
145 | mouseButtonDown = true; | ||||
146 | lastMousePoint = e->globalPos(); | ||||
147 | e->accept(); | ||||
148 | } | ||||
149 | | ||||
150 | void XPlanetImageLabel::mouseReleaseEvent(QMouseEvent *e) | ||||
151 | { | ||||
152 | mouseButtonDown = false; | ||||
153 | e->accept(); | ||||
154 | } | ||||
155 | | ||||
156 | void XPlanetImageLabel::mouseMoveEvent(QMouseEvent *e) | ||||
157 | { | ||||
158 | if(mouseButtonDown) | ||||
159 | { | ||||
160 | QPoint newPoint = e->globalPos(); | ||||
161 | int dx = newPoint.x() - lastMousePoint.x(); | ||||
162 | int dy = newPoint.y() - lastMousePoint.y(); | ||||
163 | emit changePosition(QPoint(dx, dy)); | ||||
164 | lastMousePoint = newPoint; | ||||
165 | } | ||||
166 | e->accept(); | ||||
167 | } | ||||
168 | | ||||
169 | XPlanetImageViewer::XPlanetImageViewer(const QString &obj, QWidget *parent): QDialog(parent) | ||||
170 | { | ||||
171 | #ifndef KSTARS_LITE | ||||
172 | lastURL = QUrl::fromLocalFile(QDir::homePath()); | ||||
173 | | ||||
174 | object=obj; | ||||
175 | #ifdef Q_OS_OSX | ||||
176 | setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); | ||||
177 | #endif | ||||
178 | setAttribute(Qt::WA_DeleteOnClose, true); | ||||
179 | setModal(false); | ||||
180 | setWindowTitle(i18n("XPlanet Solar System Simulator: %1", object)); | ||||
181 | | ||||
182 | setXPlanetDate(KStarsData::Instance()->ut()); | ||||
183 | if (Options::xplanetFOV()) | ||||
184 | FOV = KStars::Instance()->map()->fov(); | ||||
185 | else | ||||
186 | FOV = 0; | ||||
187 | | ||||
188 | // Create widget | ||||
189 | QFrame *page = new QFrame(this); | ||||
190 | | ||||
191 | //setMainWidget( page ); | ||||
192 | QVBoxLayout *mainLayout = new QVBoxLayout(this); | ||||
193 | mainLayout->addWidget(page); | ||||
194 | setLayout(mainLayout); | ||||
195 | | ||||
196 | QWidget *selectorsWidget= new QWidget(this); | ||||
197 | QHBoxLayout *selectorsLayout = new QHBoxLayout(selectorsWidget); | ||||
198 | selectorsLayout->setMargin(0); | ||||
199 | mainLayout->addWidget(selectorsWidget); | ||||
200 | | ||||
201 | QStringList objects; | ||||
202 | objects << i18n("Sun") << i18n("Mercury") << i18n("Venus"); | ||||
203 | objects << i18n("Earth") << i18n("Moon"); | ||||
204 | objects << i18n("Mars") << i18n("Phobos") << i18n("Deimos"); | ||||
205 | objects << i18n("Jupiter") << i18n("Ganymede") << i18n("Io") << i18n("Callisto") << i18n("Europa"); | ||||
206 | objects << i18n("Saturn") << i18n("Titan") << i18n("Mimas") << i18n("Enceladus") << i18n("Tethys") << i18n("Dione") << i18n("Rhea") << i18n("Hyperion") << i18n("Iapetus") << i18n("Phoebe"); | ||||
207 | objects << i18n("Uranus") << i18n("Umbriel") << i18n("Ariel") << i18n("Miranda") << i18n("Titania") << i18n("Oberon"); | ||||
208 | objects << i18n("Neptune") << i18n("Triton"); | ||||
209 | | ||||
210 | QComboBox *objectSelector = new QComboBox(this); | ||||
211 | objectSelector->addItems(objects); | ||||
212 | objectSelector->setToolTip(i18n("This allows you to select a new object/target for XPlanet to view")); | ||||
213 | selectorsLayout->addWidget(objectSelector); | ||||
214 | objectSelector->setCurrentIndex(objectSelector->findText(object)); | ||||
215 | connect(objectSelector, SIGNAL(currentTextChanged(QString)), this, SLOT(updateXPlanetObject(QString))); | ||||
216 | | ||||
217 | origin = i18n("Earth"); | ||||
218 | | ||||
219 | selectorsLayout->addWidget(new QLabel(i18n("from"),this)); | ||||
220 | originSelector = new QComboBox(this); | ||||
221 | originSelector->addItems(objects); | ||||
222 | originSelector->setToolTip(i18n("This allows you to select a viewing location")); | ||||
223 | selectorsLayout->addWidget(originSelector); | ||||
224 | originSelector->setCurrentIndex(originSelector->findText(origin)); | ||||
225 | connect(originSelector, SIGNAL(currentTextChanged(QString)), this, SLOT(updateXPlanetOrigin(QString))); | ||||
226 | | ||||
If this is supposed to be an integer value, then use the proper widget for it: QSpinBox. pino: If this is supposed to be an integer value, then use the proper widget for it: QSpinBox. | |||||
lancaster: VERY good idea | |||||
227 | lat = Options::xplanetLatitude().toDouble(); | ||||
228 | lon = Options::xplanetLongitude().toDouble(); | ||||
229 | radius = 45; | ||||
230 | | ||||
231 | selectorsLayout->addWidget(new QLabel(i18n("Location:"), this)); | ||||
232 | | ||||
233 | latDisplay = new QLabel(this); | ||||
234 | latDisplay->setToolTip(i18n("XPlanet Latitude, only valid when viewing the object from the same object")); | ||||
235 | latDisplay->setText(QString::number(lat)); | ||||
236 | latDisplay->setDisabled(true); | ||||
237 | selectorsLayout->addWidget(latDisplay); | ||||
238 | | ||||
239 | selectorsLayout->addWidget(new QLabel(",", this)); | ||||
240 | lonDisplay = new QLabel(this); | ||||
Why not just use an enum for the time scale, adding each item to the combobox with its enum value? pino: Why not just use an enum for the time scale, adding each item to the combobox with its enum… | |||||
lancaster: I will try it | |||||
241 | lonDisplay->setToolTip(i18n("XPlanet Longitude, only valid when viewing the object from the same object")); | ||||
242 | lonDisplay->setText(QString::number(lon)); | ||||
243 | lonDisplay->setDisabled(true); | ||||
244 | selectorsLayout->addWidget(lonDisplay); | ||||
245 | | ||||
246 | selectorsLayout->addWidget(new QLabel(",", this)); | ||||
247 | radDisplay = new QLabel(this); | ||||
248 | radDisplay->setToolTip(i18n("XPlanet object radius in %, only valid when viewing the object from the same object")); | ||||
249 | radDisplay->setText(QString::number(radius)); | ||||
250 | radDisplay->setDisabled(true); | ||||
251 | selectorsLayout->addWidget(radDisplay); | ||||
252 | | ||||
253 | freeRotate = new QPushButton(this); | ||||
254 | freeRotate->setIcon(QIcon::fromTheme("object-rotate-left")); | ||||
255 | freeRotate->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
256 | freeRotate->setMaximumSize(QSize(32,32)); | ||||
257 | freeRotate->setMinimumSize(QSize(32,32)); | ||||
258 | freeRotate->setCheckable(true); | ||||
259 | freeRotate->setToolTip(i18n("Hover over target and freely rotate view with mouse in XPlanet Viewer")); | ||||
260 | selectorsLayout->addWidget(freeRotate); | ||||
pino: QSpinBox... | |||||
lancaster: yep | |||||
261 | connect(freeRotate, SIGNAL(clicked()), this, SLOT(slotFreeRotate())); | ||||
262 | | ||||
263 | QPushButton *saveB = new QPushButton(this); | ||||
264 | saveB->setIcon(QIcon::fromTheme("document-save")); | ||||
265 | saveB->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
266 | saveB->setMaximumSize(QSize(32,32)); | ||||
267 | saveB->setMinimumSize(QSize(32,32)); | ||||
268 | saveB->setToolTip(i18n("Save the image to disk")); | ||||
269 | selectorsLayout->addWidget(saveB); | ||||
270 | connect(saveB, SIGNAL(clicked()), this, SLOT(saveFileToDisk())); | ||||
271 | | ||||
272 | QWidget *viewControlsWidget= new QWidget(this); | ||||
273 | QHBoxLayout *viewControlsLayout = new QHBoxLayout(viewControlsWidget); | ||||
274 | viewControlsLayout->setMargin(0); | ||||
275 | mainLayout->addWidget(viewControlsWidget); | ||||
276 | | ||||
277 | viewControlsLayout->addWidget(new QLabel(i18n("FOV:"), this)); | ||||
278 | | ||||
279 | FOVEdit = new NonLinearDoubleSpinBox(); | ||||
280 | FOVEdit->setDecimals(4); | ||||
281 | QList<double> possibleValues; | ||||
282 | possibleValues << 0; | ||||
283 | for(double i=.001;i<100;i*=1.5) | ||||
284 | possibleValues << i; | ||||
285 | FOVEdit->setRecommendedValues(possibleValues); | ||||
286 | FOVEdit->setValue(0); | ||||
287 | FOVEdit->setToolTip(i18n("Sets the FOV to the Specified value. Note: has no effect if hovering over object.")); | ||||
288 | viewControlsLayout->addWidget(FOVEdit); | ||||
289 | connect(FOVEdit, SIGNAL(valueChanged(double)), this, SLOT(updateXPlanetFOVEdit())); | ||||
290 | | ||||
291 | kstarsFOV = new QPushButton(this); | ||||
292 | kstarsFOV->setIcon(QIcon::fromTheme("zoom-fit-width")); | ||||
293 | kstarsFOV->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
294 | kstarsFOV->setMaximumSize(QSize(32,32)); | ||||
295 | kstarsFOV->setMinimumSize(QSize(32,32)); | ||||
296 | kstarsFOV->setToolTip(i18n("Zoom to the current KStars FOV. Note: has no effect if hovering over object.")); | ||||
297 | viewControlsLayout->addWidget(kstarsFOV); | ||||
298 | connect(kstarsFOV, SIGNAL(clicked()), this, SLOT(setKStarsXPlanetFOV())); | ||||
299 | | ||||
300 | noFOV = new QPushButton(this); | ||||
Playing with fonts is not a good idea, since it could make things unreadable. pino: Playing with fonts is not a good idea, since it could make things unreadable.
You might want to… | |||||
I didn't write this code, it was in Skymap before I moved it to XPlanetViewer. But I can look at it. lancaster: I didn't write this code, it was in Skymap before I moved it to XPlanetViewer. But I can look… | |||||
301 | noFOV->setIcon(QIcon::fromTheme("system-reboot")); | ||||
302 | noFOV->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
303 | noFOV->setMaximumSize(QSize(32,32)); | ||||
304 | noFOV->setMinimumSize(QSize(32,32)); | ||||
305 | noFOV->setToolTip(i18n("Optimum FOV for the target, FOV parameter not specified. Note: has no effect if hovering over object.")); | ||||
306 | viewControlsLayout->addWidget(noFOV); | ||||
307 | connect(noFOV, SIGNAL(clicked()), this, SLOT(clearXPlanetFOV())); | ||||
308 | | ||||
309 | rotation = 0; | ||||
310 | | ||||
311 | viewControlsLayout->addWidget(new QLabel(i18n("Rotation:"), this)); | ||||
312 | | ||||
313 | rotateEdit = new QSpinBox(); | ||||
314 | | ||||
315 | rotateEdit->setRange(-180, 180); | ||||
316 | rotateEdit->setValue(0); | ||||
317 | rotateEdit->setSingleStep(10); | ||||
318 | rotateEdit->setToolTip(i18n("Set the view rotation to the desired angle")); | ||||
319 | viewControlsLayout->addWidget(rotateEdit); | ||||
320 | connect(rotateEdit, SIGNAL(valueChanged(int)), this, SLOT(updateXPlanetRotationEdit())); | ||||
321 | | ||||
322 | QPushButton *invertRotation = new QPushButton(this); | ||||
323 | invertRotation->setIcon(QIcon::fromTheme("object-flip-vertical")); | ||||
324 | invertRotation->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
325 | invertRotation->setMaximumSize(QSize(32,32)); | ||||
326 | invertRotation->setMinimumSize(QSize(32,32)); | ||||
327 | invertRotation->setToolTip(i18n("Rotate the view 180 degrees")); | ||||
328 | viewControlsLayout->addWidget(invertRotation); | ||||
329 | connect(invertRotation, SIGNAL(clicked()), this, SLOT(invertXPlanetRotation())); | ||||
330 | | ||||
331 | QPushButton *resetRotation = new QPushButton(this); | ||||
332 | resetRotation->setIcon(QIcon::fromTheme("system-reboot")); | ||||
333 | resetRotation->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
334 | resetRotation->setMaximumSize(QSize(32,32)); | ||||
335 | resetRotation->setMinimumSize(QSize(32,32)); | ||||
336 | resetRotation->setToolTip(i18n("Reset view rotation to 0")); | ||||
337 | viewControlsLayout->addWidget(resetRotation); | ||||
338 | connect(resetRotation, SIGNAL(clicked()), this, SLOT(resetXPlanetRotation())); | ||||
339 | | ||||
340 | QPushButton *optionsB = new QPushButton(this); | ||||
341 | optionsB->setIcon(QIcon::fromTheme("configure")); | ||||
pino: This is leaked at every invocation. | |||||
I didn't write this code, it was in Skymap before I moved it to XPlanetViewer. But you are probably right, that is easily fixed lancaster: I didn't write this code, it was in Skymap before I moved it to XPlanetViewer. But you are… | |||||
342 | optionsB->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
343 | optionsB->setMaximumSize(QSize(32,32)); | ||||
344 | optionsB->setMinimumSize(QSize(32,32)); | ||||
345 | optionsB->setToolTip(i18n("Bring up XPlanet Options")); | ||||
346 | viewControlsLayout->addWidget(optionsB); | ||||
347 | connect(optionsB, SIGNAL(clicked()), KStars::Instance(), SLOT(slotViewOps())); | ||||
348 | | ||||
349 | QPushButton *invertB = new QPushButton(this); | ||||
350 | invertB->setIcon(QIcon::fromTheme("edit-select-invert")); | ||||
351 | invertB->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
352 | invertB->setMaximumSize(QSize(32,32)); | ||||
353 | invertB->setMinimumSize(QSize(32,32)); | ||||
354 | invertB->setToolTip(i18n("Reverse colors of the image. This is useful to enhance contrast at times. This affects " | ||||
355 | "only the display and not the saving.")); | ||||
356 | viewControlsLayout->addWidget(invertB); | ||||
357 | connect(invertB, SIGNAL(clicked()), this, SLOT(invertColors())); | ||||
358 | | ||||
359 | QWidget *timeWidget= new QWidget(this); | ||||
360 | QHBoxLayout *timeLayout = new QHBoxLayout(timeWidget); | ||||
361 | mainLayout->addWidget(timeWidget); | ||||
362 | timeLayout->setMargin(0); | ||||
363 | | ||||
364 | xplanetTime = KStarsData::Instance()->lt(); | ||||
365 | | ||||
366 | QPushButton *setTime = new QPushButton(this); | ||||
367 | setTime->setIcon(QIcon::fromTheme("clock")); | ||||
368 | setTime->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
369 | setTime->setMaximumSize(QSize(32,32)); | ||||
370 | setTime->setMinimumSize(QSize(32,32)); | ||||
371 | setTime->setToolTip(i18n("Allows you to set the XPlanet time to a different date/time from KStars")); | ||||
372 | timeLayout->addWidget(setTime); | ||||
373 | connect(setTime, SIGNAL(clicked()), this, SLOT(setXPlanetTime())); | ||||
374 | | ||||
375 | QPushButton *kstarsTime = new QPushButton(this); | ||||
376 | kstarsTime->setIcon(QIcon::fromTheme("system-reboot")); | ||||
377 | kstarsTime->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
378 | kstarsTime->setMaximumSize(QSize(32,32)); | ||||
379 | kstarsTime->setMinimumSize(QSize(32,32)); | ||||
380 | kstarsTime->setToolTip(i18n("Sets the XPlanet time to the current KStars time")); | ||||
381 | timeLayout->addWidget(kstarsTime); | ||||
382 | connect(kstarsTime, SIGNAL(clicked()), this, SLOT(setXPlanetTimetoKStarsTime())); | ||||
383 | | ||||
384 | XPlanetTimeDisplay = new QLabel(this); | ||||
385 | XPlanetTimeDisplay->setToolTip(i18n("Current XPlanet Time")); | ||||
386 | timeLayout->addWidget(XPlanetTimeDisplay); | ||||
387 | | ||||
388 | XPlanetTimeDisplay->setText(xplanetTime.date().toString() + ", " + xplanetTime.time().toString()); | ||||
389 | | ||||
390 | timeSlider = new QSlider(Qt::Horizontal, this); | ||||
391 | timeLayout->addWidget(timeSlider); | ||||
392 | timeSlider->setRange(-100, 100); | ||||
393 | timeSlider->setToolTip(i18n("This sets the time step from the current XPlanet time, good for viewing events")); | ||||
394 | connect(timeSlider, SIGNAL(valueChanged(int)), this, SLOT(updateXPlanetTime(int))); | ||||
395 | | ||||
396 | timeEdit = new QSpinBox(this); | ||||
397 | timeEdit->setRange(-100, 100); | ||||
398 | timeEdit->setMaximumWidth(50); | ||||
399 | timeEdit->setToolTip(i18n("This sets the time step from the current XPlanet time")); | ||||
400 | timeLayout->addWidget(timeEdit); | ||||
401 | connect(timeEdit, SIGNAL(valueChanged(int)), this, SLOT(updateXPlanetTimeEdit())); | ||||
402 | | ||||
403 | timeUnit = MINS; | ||||
404 | timeUnitsSelect = new QComboBox(this); | ||||
405 | timeLayout->addWidget(timeUnitsSelect); | ||||
406 | timeUnitsSelect->addItem(i18n("years")); | ||||
407 | timeUnitsSelect->addItem(i18n("months")); | ||||
408 | timeUnitsSelect->addItem(i18n("days")); | ||||
409 | timeUnitsSelect->addItem(i18n("hours")); | ||||
410 | timeUnitsSelect->addItem(i18n("mins")); | ||||
411 | timeUnitsSelect->addItem(i18n("secs")); | ||||
412 | timeUnitsSelect->setCurrentIndex(MINS); | ||||
413 | timeUnitsSelect->setToolTip(i18n("Lets you change the units for the timestep in the animation")); | ||||
414 | connect(timeUnitsSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(updateXPlanetTimeUnits(int))); | ||||
415 | | ||||
416 | XPlanetTimer = new QTimer(this); | ||||
417 | XPlanetTimer->setInterval(Options::xplanetAnimationDelay().toInt()); | ||||
418 | connect(XPlanetTimer, SIGNAL(timeout()), this, SLOT(incrementXPlanetTime())); | ||||
419 | | ||||
420 | runTime = new QPushButton(this); | ||||
421 | runTime->setIcon(QIcon::fromTheme("media-playback-start")); | ||||
422 | runTime->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
423 | runTime->setCheckable(true); | ||||
424 | runTime->setMaximumSize(QSize(32,32)); | ||||
425 | runTime->setMinimumSize(QSize(32,32)); | ||||
426 | runTime->setToolTip(i18n("Lets you run the animation")); | ||||
427 | timeLayout->addWidget(runTime); | ||||
428 | connect(runTime, SIGNAL(clicked()), this, SLOT(toggleXPlanetRun())); | ||||
429 | | ||||
430 | QPushButton *resetTime = new QPushButton(this); | ||||
431 | resetTime->setIcon(QIcon::fromTheme("system-reboot")); | ||||
432 | resetTime->setAttribute(Qt::WA_LayoutUsesWidgetRect); | ||||
433 | resetTime->setMaximumSize(QSize(32,32)); | ||||
434 | resetTime->setMinimumSize(QSize(32,32)); | ||||
435 | resetTime->setToolTip(i18n("Resets the animation to 0 timesteps from the current XPlanet Time")); | ||||
436 | timeLayout->addWidget(resetTime); | ||||
437 | connect(resetTime, SIGNAL(clicked()), this, SLOT(resetXPlanetTime())); | ||||
438 | | ||||
439 | m_View = new XPlanetImageLabel(page); | ||||
440 | m_View->setAutoFillBackground(false); | ||||
441 | m_Caption = new QLabel(page); | ||||
442 | m_Caption->setAutoFillBackground(true); | ||||
443 | m_Caption->setFrameShape(QFrame::StyledPanel); | ||||
444 | m_Caption->setText(object); | ||||
445 | // Add layout | ||||
446 | QVBoxLayout *vlay = new QVBoxLayout(page); | ||||
447 | vlay->setSpacing(0); | ||||
448 | vlay->setMargin(0); | ||||
449 | vlay->addWidget(m_View); | ||||
450 | vlay->addWidget(m_Caption); | ||||
451 | | ||||
452 | | ||||
453 | connect(m_View, SIGNAL(zoomIn()), this, SLOT(zoomInXPlanetFOV())); | ||||
454 | connect(m_View, SIGNAL(zoomOut()), this, SLOT(zoomOutXPlanetFOV())); | ||||
455 | connect(m_View, SIGNAL(changePosition(QPoint)), this, SLOT(changeXPlanetPosition(QPoint))); | ||||
456 | | ||||
457 | //Reverse colors | ||||
458 | QPalette p = palette(); | ||||
459 | p.setColor(QPalette::Window, palette().color(QPalette::WindowText)); | ||||
460 | p.setColor(QPalette::WindowText, palette().color(QPalette::Window)); | ||||
461 | m_Caption->setPalette(p); | ||||
462 | m_View->setPalette(p); | ||||
463 | | ||||
464 | #ifdef Q_OS_OSX | ||||
465 | QList<QPushButton *> qButtons = findChildren<QPushButton *>(); | ||||
466 | for (auto &button : qButtons) | ||||
467 | button->setAutoDefault(false); | ||||
468 | #endif | ||||
469 | updateXPlanetTime(0); | ||||
470 | startXplanet(); | ||||
471 | #endif | ||||
472 | } | ||||
473 | | ||||
474 | XPlanetImageViewer::~XPlanetImageViewer() | ||||
475 | { | ||||
476 | QApplication::restoreOverrideCursor(); | ||||
477 | } | ||||
478 | | ||||
479 | void XPlanetImageViewer::startXplanet() | ||||
480 | { | ||||
481 | if(xplanetRunning) | ||||
482 | return; | ||||
483 | | ||||
484 | //This means something failed in the file output | ||||
485 | if(!setupOutputFile()) | ||||
486 | return; | ||||
487 | | ||||
488 | QString xPlanetLocation = Options::xplanetPath(); | ||||
489 | #ifdef Q_OS_OSX | ||||
490 | if (Options::xplanetIsInternal()) | ||||
491 | xPlanetLocation = QCoreApplication::applicationDirPath() + "/xplanet/bin/xplanet"; | ||||
492 | #endif | ||||
493 | | ||||
494 | // If Options::xplanetPath() is empty, return | ||||
495 | if (xPlanetLocation.isEmpty()) | ||||
496 | { | ||||
497 | KMessageBox::error(nullptr, i18n("Xplanet binary path is empty in config panel.")); | ||||
pino: This is always true. | |||||
True, because the new process was created a few lines before. I believe this was already in Skymap before I moved it here. lancaster: True, because the new process was created a few lines before. I believe this was already in… | |||||
498 | return; | ||||
499 | } | ||||
500 | | ||||
pino: `isEmpty()` | |||||
lancaster: ok | |||||
501 | // If Options::xplanetPath() does not exist, return | ||||
502 | const QFileInfo xPlanetLocationInfo(xPlanetLocation); | ||||
503 | if (!xPlanetLocationInfo.exists() || !xPlanetLocationInfo.isExecutable()) | ||||
504 | { | ||||
505 | KMessageBox::error(nullptr, i18n("The configured Xplanet binary does not exist or is not executable.")); | ||||
506 | return; | ||||
507 | } | ||||
508 | | ||||
509 | // Create xplanet process | ||||
510 | QProcess *xplanetProc = new QProcess(this); | ||||
511 | | ||||
512 | // Add some options | ||||
513 | QStringList args; | ||||
514 | | ||||
515 | //This specifies the object to be viewed | ||||
516 | args << "-body" << object.toLower(); | ||||
517 | //This is the date and time requested | ||||
518 | args << "-date" << date; | ||||
519 | //This is the glare from the sun | ||||
520 | args << "-glare" << Options::xplanetGlare(); | ||||
521 | args << "-base_magnitude" << Options::xplanetMagnitude(); | ||||
522 | //This is the correction for light's travel time. | ||||
523 | args << "-light_time"; | ||||
524 | | ||||
525 | args << "-geometry" << Options::xplanetWidth() + 'x' + Options::xplanetHeight(); | ||||
526 | | ||||
527 | if(FOV != 0) | ||||
528 | args << "-fov" << QString::number(FOV); | ||||
529 | //Need to convert to locale for places that don't use decimals?? | ||||
530 | //args << "-fov" << fov.setNum(fov());//.replace('.', ','); | ||||
531 | | ||||
532 | //This rotates the view for different object angles | ||||
533 | args << "-rotate" << QString::number(rotation); | ||||
534 | | ||||
535 | if (Options::xplanetConfigFile()) | ||||
536 | args << "-config" << Options::xplanetConfigFilePath(); | ||||
537 | if (Options::xplanetStarmap()) | ||||
538 | args << "-starmap" << Options::xplanetStarmapPath(); | ||||
539 | if (Options::xplanetArcFile()) | ||||
540 | args << "-arc_file" << Options::xplanetArcFilePath(); | ||||
541 | if (!file.fileName().isEmpty()) | ||||
542 | args << "-output" << file.fileName() << "-quality" << Options::xplanetQuality(); | ||||
543 | | ||||
544 | // Labels | ||||
545 | if (Options::xplanetLabel()) | ||||
546 | { | ||||
547 | args << "-fontsize" << Options::xplanetFontSize() << "-color" | ||||
548 | << "0x" + Options::xplanetColor().mid(1) << "-date_format" << Options::xplanetDateFormat(); | ||||
549 | | ||||
550 | if (Options::xplanetLabelGMT()) | ||||
551 | args << "-gmtlabel"; | ||||
552 | else | ||||
553 | args << "-label"; | ||||
554 | if (!Options::xplanetLabelString().isEmpty()) | ||||
555 | args << "-label_string" | ||||
556 | << "\"" + Options::xplanetLabelString() + "\""; | ||||
557 | if (Options::xplanetLabelTL()) | ||||
558 | args << "-labelpos" | ||||
559 | << "+15+15"; | ||||
560 | else if (Options::xplanetLabelTR()) | ||||
561 | args << "-labelpos" | ||||
562 | << "-15+15"; | ||||
563 | else if (Options::xplanetLabelBR()) | ||||
564 | args << "-labelpos" | ||||
565 | << "-15-15"; | ||||
566 | else if (Options::xplanetLabelBL()) | ||||
567 | args << "-labelpos" | ||||
568 | << "+15-15"; | ||||
569 | } | ||||
570 | | ||||
571 | // Markers | ||||
I see this was copied from skymap.cpp, but it is ugly and inefficient anyway. Also, date is used only as argument for xplanet in startXplanet(), so just create & use it there directly, without storing it as class variable. pino: I see this was copied from skymap.cpp, but it is ugly and inefficient anyway.
Since… | |||||
True, it is ugly, but that is why I had put it in this separate method, to clean up the code and get that mess out of there and keep it all together. If there is a simple way to get the date all correct in the right format for xplanet, we should explore that. lancaster: True, it is ugly, but that is why I had put it in this separate method, to clean up the code… | |||||
572 | if (Options::xplanetMarkerFile()) | ||||
573 | args << "-marker_file" << Options::xplanetMarkerFilePath(); | ||||
574 | if (Options::xplanetMarkerBounds()) | ||||
575 | args << "-markerbounds" << Options::xplanetMarkerBoundsPath(); | ||||
576 | | ||||
577 | // Position | ||||
578 | // This sets the position from which the planet is viewed. | ||||
579 | // Note that setting Latitude and Longitude means that position above the SAME object | ||||
580 | | ||||
581 | if(object == origin) | ||||
582 | { | ||||
583 | if (Options::xplanetRandom()) | ||||
584 | args << "-random"; | ||||
585 | else | ||||
586 | args << "-latitude" << QString::number(lat) << "-longitude" << QString::number(lon) << "-radius" << QString::number(radius); | ||||
587 | } | ||||
588 | else | ||||
589 | args << "-origin" << origin; | ||||
590 | | ||||
591 | // Projection | ||||
592 | if (Options::xplanetProjection()) | ||||
593 | { | ||||
594 | switch (Options::xplanetProjection()) | ||||
595 | { | ||||
596 | case 1: | ||||
597 | args << "-projection" | ||||
598 | << "ancient"; | ||||
599 | break; | ||||
600 | case 2: | ||||
601 | args << "-projection" | ||||
602 | << "azimuthal"; | ||||
603 | break; | ||||
604 | case 3: | ||||
605 | args << "-projection" | ||||
606 | << "bonne"; | ||||
607 | break; | ||||
608 | case 4: | ||||
609 | args << "-projection" | ||||
610 | << "gnomonic"; | ||||
611 | break; | ||||
612 | case 5: | ||||
613 | args << "-projection" | ||||
614 | << "hemisphere"; | ||||
615 | break; | ||||
616 | case 6: | ||||
617 | args << "-projection" | ||||
618 | << "lambert"; | ||||
619 | break; | ||||
620 | case 7: | ||||
621 | args << "-projection" | ||||
622 | << "mercator"; | ||||
623 | break; | ||||
624 | case 8: | ||||
625 | args << "-projection" | ||||
626 | << "mollweide"; | ||||
627 | break; | ||||
628 | case 9: | ||||
629 | args << "-projection" | ||||
630 | << "orthographic"; | ||||
631 | break; | ||||
632 | case 10: | ||||
633 | args << "-projection" | ||||
634 | << "peters"; | ||||
635 | break; | ||||
636 | case 11: | ||||
637 | args << "-projection" | ||||
638 | << "polyconic"; | ||||
639 | break; | ||||
640 | case 12: | ||||
641 | args << "-projection" | ||||
642 | << "rectangular"; | ||||
643 | break; | ||||
644 | case 13: | ||||
645 | args << "-projection" | ||||
646 | << "tsc"; | ||||
647 | break; | ||||
648 | default: | ||||
649 | break; | ||||
650 | } | ||||
651 | if (Options::xplanetBackground()) | ||||
652 | { | ||||
653 | if (Options::xplanetBackgroundImage()) | ||||
654 | args << "-background" << Options::xplanetBackgroundImagePath(); | ||||
655 | else | ||||
656 | args << "-background" | ||||
657 | << "0x" + Options::xplanetBackgroundColorValue().mid(1); | ||||
658 | } | ||||
659 | } | ||||
660 | | ||||
661 | #ifdef Q_OS_OSX | ||||
662 | if (Options::xplanetIsInternal()) | ||||
663 | { | ||||
664 | QString searchDir = QCoreApplication::applicationDirPath() + "/xplanet/share/xplanet/"; | ||||
665 | args << "-searchdir" << searchDir; | ||||
666 | } | ||||
667 | #endif | ||||
668 | | ||||
669 | //This prevents it from running forever. | ||||
670 | args << "-num_times" << "1"; | ||||
671 | // Run xplanet | ||||
672 | | ||||
673 | if(Options::xplanetUseFIFO()) | ||||
674 | QtConcurrent::run(this, &XPlanetImageViewer::loadImage); | ||||
675 | xplanetProc->start(xPlanetLocation, args); | ||||
676 | | ||||
A bit more verbose text for the error mesage box would be useful, otherwise users just get e.g. "Input/output error" and that's it. pino: A bit more verbose text for the error mesage box would be useful, otherwise users just get e.g. | |||||
I didn't write this code, it was in ImageViewer. But I can look at it. lancaster: I didn't write this code, it was in ImageViewer. But I can look at it. | |||||
677 | // qDebug() << "Run:" << xplanetProc->program() << args.join(" "); | ||||
678 | xplanetRunning = true; | ||||
679 | bool succeeded = xplanetProc->waitForFinished(Options::xplanetTimeout().toInt()); | ||||
680 | xplanetRunning = false; | ||||
681 | xplanetProc->deleteLater(); | ||||
682 | if(succeeded) | ||||
683 | { | ||||
684 | if(FOV == 0) | ||||
685 | m_Caption->setText(i18n("XPlanet View: ") + object + i18n(" from ") + origin + ", " + dateText); | ||||
Word puzzle. See https://techbase.kde.org/Development/Tutorials/Localization/i18n_Mistakes yurchor: Word puzzle.
Should be something like
m_Caption->setText(i18n("XPlanet View: %1 from %2, %3"… | |||||
686 | else | ||||
687 | m_Caption->setText(i18n("XPlanet View: ") + object + i18n(" from ") + origin + ", " + dateText + i18n(", FOV: ") + QString::number(FOV)); | ||||
yurchor: Word puzzle. See above. | |||||
688 | if(!Options::xplanetUseFIFO()) | ||||
689 | loadImage(); | ||||
690 | showImage(); | ||||
691 | } | ||||
692 | } | ||||
693 | | ||||
694 | bool XPlanetImageViewer::setupOutputFile() | ||||
695 | { | ||||
696 | if(Options::xplanetUseFIFO()) | ||||
697 | { | ||||
698 | if(file.fileName().contains("/tmp/xplanetfifo") && file.exists()) | ||||
699 | return true; | ||||
700 | file.setFileName(QString("/tmp/xplanetfifo%1.png").arg(QUuid::createUuid().toString().mid(1, 8))); | ||||
701 | int fd =0; | ||||
702 | if ((fd = mkfifo(file.fileName().toLatin1(), S_IRUSR | S_IWUSR) < 0)) | ||||
703 | { | ||||
704 | KMessageBox::error(nullptr, i18n("Error making FIFO file %1: %2.", file.fileName(), strerror(errno))); | ||||
705 | return false; | ||||
706 | } | ||||
707 | } | ||||
708 | else | ||||
709 | { | ||||
710 | QDir writableDir; | ||||
711 | QString xPlanetDirPath = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "xplanet"; | ||||
712 | writableDir.mkpath(xPlanetDirPath); | ||||
713 | file.setFileName(xPlanetDirPath + QDir::separator() + object + ".png"); | ||||
714 | } | ||||
715 | | ||||
716 | return true; | ||||
717 | } | ||||
718 | | ||||
719 | void XPlanetImageViewer::zoomInXPlanetFOV() | ||||
720 | { | ||||
721 | if(origin == object) | ||||
722 | { | ||||
723 | radius += 5; | ||||
724 | radDisplay->setText(QString::number(radius)); | ||||
725 | startXplanet(); | ||||
726 | } | ||||
727 | else | ||||
728 | { | ||||
729 | FOVEdit->stepDown(); | ||||
730 | startXplanet(); | ||||
731 | } | ||||
732 | | ||||
733 | } | ||||
734 | | ||||
735 | void XPlanetImageViewer::zoomOutXPlanetFOV() | ||||
pino: lang=c++
const bool initialLoad = !isVisible();
much easier... | |||||
lancaster: true | |||||
736 | { | ||||
737 | if(origin == object) | ||||
738 | { | ||||
739 | if(radius > 0) | ||||
740 | { | ||||
741 | radius -= 5; | ||||
742 | radDisplay->setText(QString::number(radius)); | ||||
743 | startXplanet(); | ||||
744 | } | ||||
745 | } | ||||
746 | else | ||||
747 | { | ||||
748 | FOVEdit->stepUp(); | ||||
749 | startXplanet(); | ||||
750 | } | ||||
pino: Why this sequence of resize()? | |||||
An interesting question. I found that I was having trouble getting the view (imagelabel) to display and update without using the resize method. repaint, paint, update, did not work. But this did work. I can try again. lancaster: An interesting question. I found that I was having trouble getting the view (imagelabel) to… | |||||
Ok I went back and looked at the issue. It was because the resize event was required to update the picture in the image viewer class since the picture never changed like it does in xplanet viewer. I fixed this just now by providing a different method. lancaster: Ok I went back and looked at the issue. It was because the resize event was required to update… | |||||
751 | | ||||
752 | } | ||||
753 | | ||||
754 | void XPlanetImageViewer::updateXPlanetTime(int timeShift){ | ||||
755 | | ||||
756 | KStarsDateTime shiftedXPlanetTime; | ||||
757 | switch(timeUnit) | ||||
758 | { | ||||
759 | case YEARS: | ||||
760 | shiftedXPlanetTime = xplanetTime.addDays(timeShift * 365); | ||||
761 | break; | ||||
762 | | ||||
763 | case MONTHS: | ||||
764 | shiftedXPlanetTime = xplanetTime.addDays(timeShift * 30); | ||||
765 | break; | ||||
766 | | ||||
767 | case DAYS: | ||||
Considering non-local URLs as save destination are not supported, then just use file paths (and QString) instead of urls (and QUrl). This will be less confusing, and simplify things a bit. pino: Considering non-local URLs as save destination are not supported, then just use file paths (and… | |||||
I didn't write this code, it was in ImageViewer. But I can look at it. lancaster: I didn't write this code, it was in ImageViewer. But I can look at it. | |||||
768 | shiftedXPlanetTime = xplanetTime.addDays(timeShift); | ||||
getSaveFileUrl() is static, so call it as such, instead of constructing a QFileDialog on the stack (that won't be used, anyway). pino: `getSaveFileUrl()` is static, so call it as such, instead of constructing a QFileDialog on the… | |||||
I didn't write this code, it was in ImageViewer. But I can look at it. lancaster: I didn't write this code, it was in ImageViewer. But I can look at it. | |||||
769 | break; | ||||
770 | | ||||
771 | case HOURS: | ||||
772 | shiftedXPlanetTime = xplanetTime.addSecs(timeShift * 3600); | ||||
773 | break; | ||||
774 | | ||||
775 | case MINS: | ||||
pino: Why the static_cast? Just use `parentWidget()` instead. | |||||
I didn't write this code, it was in ImageViewer. But I can look at it. lancaster: I didn't write this code, it was in ImageViewer. But I can look at it. | |||||
776 | shiftedXPlanetTime = xplanetTime.addSecs(timeShift * 60); | ||||
777 | break; | ||||
778 | | ||||
779 | case SECS: | ||||
780 | shiftedXPlanetTime = xplanetTime.addSecs(timeShift); | ||||
781 | break; | ||||
782 | } | ||||
783 | | ||||
784 | setXPlanetDate(shiftedXPlanetTime); | ||||
785 | dateText = shiftedXPlanetTime.date().toString() + ", " + shiftedXPlanetTime.time().toString(); | ||||
786 | timeEdit->setValue(timeShift); | ||||
787 | startXplanet(); | ||||
788 | } | ||||
789 | | ||||
790 | void XPlanetImageViewer::updateXPlanetObject(const QString &obj){ | ||||
791 | object = obj; | ||||
792 | | ||||
793 | setWindowTitle(i18n("XPlanet Solar System Simulator: %1", object)); | ||||
794 | if(freeRotate->isChecked()) | ||||
795 | originSelector->setCurrentIndex(originSelector->findText(object)); | ||||
796 | | ||||
797 | startXplanet(); | ||||
798 | } | ||||
799 | | ||||
800 | void XPlanetImageViewer::updateXPlanetOrigin(const QString &obj) | ||||
801 | { | ||||
802 | origin = obj; | ||||
803 | if(object == origin) | ||||
804 | freeRotate->setChecked(true); | ||||
805 | else | ||||
806 | freeRotate->setChecked(false); | ||||
807 | updateStates();//This will update the disabled/enabled states | ||||
808 | startXplanet(); | ||||
809 | } | ||||
810 | | ||||
811 | void XPlanetImageViewer::changeXPlanetPosition(QPoint delta) | ||||
812 | { | ||||
813 | if(origin == object) | ||||
814 | { | ||||
815 | double newLon = lon + delta.x(); | ||||
816 | double newLat = lat + delta.y(); | ||||
817 | | ||||
818 | if(newLat > 90) | ||||
819 | newLat = 90; | ||||
820 | if(newLat < -90) | ||||
821 | newLat = -90; | ||||
822 | | ||||
823 | lon = newLon; | ||||
824 | lat = newLat; | ||||
825 | | ||||
826 | latDisplay->setText(QString::number(lat)); | ||||
827 | lonDisplay->setText(QString::number(lon)); | ||||
828 | startXplanet(); | ||||
829 | } | ||||
830 | } | ||||
831 | | ||||
832 | void XPlanetImageViewer::slotFreeRotate() | ||||
833 | { | ||||
834 | if(freeRotate->isChecked()) | ||||
835 | originSelector->setCurrentIndex(originSelector->findText(object)); | ||||
836 | else | ||||
837 | originSelector->setCurrentIndex(originSelector->findText(i18n("Earth"))); | ||||
838 | } | ||||
839 | | ||||
840 | void XPlanetImageViewer::updateStates() | ||||
841 | { | ||||
842 | if(freeRotate->isChecked()) | ||||
843 | { | ||||
844 | FOVEdit->setDisabled(true); | ||||
845 | kstarsFOV->setDisabled(true); | ||||
846 | noFOV->setDisabled(true); | ||||
847 | | ||||
848 | latDisplay->setDisabled(false); | ||||
849 | lonDisplay->setDisabled(false); | ||||
850 | radDisplay->setDisabled(false); | ||||
851 | } | ||||
852 | else | ||||
853 | { | ||||
854 | FOVEdit->setDisabled(false); | ||||
855 | kstarsFOV->setDisabled(false); | ||||
856 | noFOV->setDisabled(false); | ||||
857 | | ||||
858 | latDisplay->setDisabled(true); | ||||
859 | lonDisplay->setDisabled(true); | ||||
860 | radDisplay->setDisabled(true); | ||||
861 | } | ||||
862 | } | ||||
863 | | ||||
864 | void XPlanetImageViewer::setXPlanetDate(KStarsDateTime time){ | ||||
865 | //Note Xplanet uses UT time for everything but we want the labels to all be LT | ||||
866 | KStarsDateTime utTime = KStarsData::Instance()->geo()->LTtoUT(time); | ||||
867 | QString year, month, day, hour, minute, second; | ||||
868 | | ||||
869 | if (year.setNum(utTime.date().year()).size() == 1) | ||||
870 | year.push_front('0'); | ||||
871 | if (month.setNum(utTime.date().month()).size() == 1) | ||||
872 | month.push_front('0'); | ||||
873 | if (day.setNum(utTime.date().day()).size() == 1) | ||||
874 | day.push_front('0'); | ||||
875 | if (hour.setNum(utTime.time().hour()).size() == 1) | ||||
876 | hour.push_front('0'); | ||||
877 | if (minute.setNum(utTime.time().minute()).size() == 1) | ||||
878 | minute.push_front('0'); | ||||
879 | if (second.setNum(utTime.time().second()).size() == 1) | ||||
880 | second.push_front('0'); | ||||
881 | | ||||
882 | date = year + month + day + '.' + hour + minute + second; | ||||
883 | | ||||
884 | } | ||||
885 | | ||||
886 | void XPlanetImageViewer::updateXPlanetTimeUnits(int units) | ||||
887 | { | ||||
888 | timeUnit = units; | ||||
889 | updateXPlanetTimeEdit(); | ||||
890 | } | ||||
891 | | ||||
892 | void XPlanetImageViewer::updateXPlanetTimeEdit() | ||||
893 | { | ||||
894 | int timeShift = timeEdit->text().toInt(); | ||||
895 | if(timeSlider->value() != timeShift) | ||||
896 | timeSlider->setValue(timeShift); | ||||
897 | else | ||||
898 | updateXPlanetTime(timeShift); | ||||
899 | } | ||||
900 | | ||||
901 | void XPlanetImageViewer::incrementXPlanetTime() | ||||
902 | { | ||||
903 | if(!xplanetRunning) | ||||
904 | { | ||||
905 | int timeShift = timeEdit->text().toInt(); | ||||
906 | if(timeSlider->maximum() == timeShift) | ||||
907 | timeSlider->setMaximum(timeSlider->maximum() + 1); | ||||
908 | if(timeEdit->maximum() == timeShift) | ||||
909 | timeEdit->setMaximum(timeSlider->maximum() + 1); | ||||
910 | timeSlider->setValue(timeShift + 1); | ||||
911 | } | ||||
912 | } | ||||
913 | | ||||
914 | void XPlanetImageViewer::setXPlanetTime() | ||||
915 | { | ||||
916 | QPointer<TimeDialog> timedialog = new TimeDialog(xplanetTime, KStarsData::Instance()->geo(), this); | ||||
917 | if (timedialog->exec() == QDialog::Accepted) | ||||
918 | { | ||||
919 | xplanetTime = timedialog->selectedDateTime(); | ||||
920 | XPlanetTimeDisplay->setText(xplanetTime.date().toString() + ", " + xplanetTime.time().toString()); | ||||
921 | int timeShift = 0; | ||||
922 | timeSlider->setMaximum(100); | ||||
923 | if(timeSlider->value() != timeShift) | ||||
924 | timeSlider->setValue(timeShift); | ||||
925 | else | ||||
926 | updateXPlanetTime(timeShift); | ||||
927 | startXplanet(); | ||||
928 | } | ||||
929 | } | ||||
930 | | ||||
931 | void XPlanetImageViewer::setXPlanetTimetoKStarsTime() | ||||
932 | { | ||||
933 | xplanetTime = KStarsData::Instance()->lt(); | ||||
934 | XPlanetTimeDisplay->setText(xplanetTime.date().toString() + ", " + xplanetTime.time().toString()); | ||||
935 | int timeShift = 0; | ||||
936 | timeSlider->setMaximum(100); | ||||
937 | if(timeSlider->value() != timeShift) | ||||
938 | timeSlider->setValue(timeShift); | ||||
939 | else | ||||
940 | updateXPlanetTime(timeShift); | ||||
941 | startXplanet(); | ||||
942 | } | ||||
943 | | ||||
944 | void XPlanetImageViewer::resetXPlanetTime() | ||||
945 | { | ||||
946 | int timeShift = 0; | ||||
947 | timeSlider->setMaximum(100); | ||||
948 | timeEdit->setMaximum(100); | ||||
949 | if(timeSlider->value() != timeShift) | ||||
950 | timeSlider->setValue(timeShift); | ||||
951 | else | ||||
952 | updateXPlanetTime(timeShift); | ||||
953 | } | ||||
954 | | ||||
955 | void XPlanetImageViewer::toggleXPlanetRun() | ||||
956 | { | ||||
957 | if(XPlanetTimer->isActive()) | ||||
958 | { | ||||
959 | XPlanetTimer->stop(); | ||||
960 | } | ||||
961 | else | ||||
962 | { | ||||
963 | XPlanetTimer->setInterval(Options::xplanetAnimationDelay().toInt()); | ||||
964 | XPlanetTimer->start(); | ||||
965 | } | ||||
966 | } | ||||
967 | | ||||
968 | void XPlanetImageViewer::updateXPlanetFOVEdit() | ||||
969 | { | ||||
970 | FOV = FOVEdit->value(); | ||||
971 | startXplanet(); | ||||
972 | } | ||||
973 | | ||||
974 | void XPlanetImageViewer::clearXPlanetFOV() | ||||
975 | { | ||||
976 | FOV = 0; | ||||
977 | FOVEdit->setValue(0); | ||||
978 | startXplanet(); | ||||
979 | } | ||||
980 | | ||||
981 | void XPlanetImageViewer::setKStarsXPlanetFOV() | ||||
982 | { | ||||
983 | FOV = KStars::Instance()->map()->fov(); | ||||
984 | FOVEdit->setValue(FOV); | ||||
985 | startXplanet(); | ||||
986 | } | ||||
987 | | ||||
988 | void XPlanetImageViewer::updateXPlanetRotationEdit() | ||||
989 | { | ||||
990 | rotation = rotateEdit->value(); | ||||
991 | startXplanet(); | ||||
992 | } | ||||
993 | | ||||
994 | void XPlanetImageViewer::resetXPlanetRotation(){ | ||||
995 | rotateEdit->setValue(0); | ||||
996 | } | ||||
997 | | ||||
998 | void XPlanetImageViewer::invertXPlanetRotation(){ | ||||
999 | rotateEdit->setValue(180); | ||||
1000 | } | ||||
1001 | | ||||
1002 | bool XPlanetImageViewer::loadImage() | ||||
1003 | { | ||||
1004 | #ifndef KSTARS_LITE | ||||
1005 | if (!image.load(file.fileName())) | ||||
1006 | { | ||||
1007 | QString text = i18n("Loading of the image of object %1 failed.", object); | ||||
1008 | KMessageBox::error(this, text); | ||||
1009 | return false; | ||||
1010 | } | ||||
1011 | return true; | ||||
1012 | #else | ||||
1013 | return false; | ||||
1014 | #endif | ||||
1015 | } | ||||
1016 | | ||||
1017 | bool XPlanetImageViewer::showImage() | ||||
1018 | { | ||||
1019 | #ifndef KSTARS_LITE | ||||
1020 | | ||||
1021 | //If the image is larger than screen width and/or screen height, | ||||
1022 | //shrink it to fit the screen | ||||
1023 | QRect deskRect = QApplication::desktop()->availableGeometry(); | ||||
1024 | int w = deskRect.width(); // screen width | ||||
1025 | int h = deskRect.height(); // screen height | ||||
1026 | | ||||
1027 | if (image.width() <= w && image.height() > h) //Window is taller than desktop | ||||
1028 | image = image.scaled(int(image.width() * h / image.height()), h); | ||||
1029 | else if (image.height() <= h && image.width() > w) //window is wider than desktop | ||||
1030 | image = image.scaled(w, int(image.height() * w / image.width())); | ||||
1031 | else if (image.width() > w && image.height() > h) //window is too tall and too wide | ||||
1032 | { | ||||
1033 | //which needs to be shrunk least, width or height? | ||||
1034 | float fx = float(w) / float(image.width()); | ||||
1035 | float fy = float(h) / float(image.height()); | ||||
1036 | if (fx > fy) //width needs to be shrunk less, so shrink to fit in height | ||||
1037 | image = image.scaled(int(image.width() * fy), h); | ||||
1038 | else //vice versa | ||||
1039 | image = image.scaled(w, int(image.height() * fx)); | ||||
1040 | } | ||||
1041 | const bool initialLoad = !isVisible(); | ||||
1042 | | ||||
1043 | show(); // hide is default | ||||
1044 | | ||||
1045 | m_View->setImage(image); | ||||
1046 | w = image.width(); | ||||
1047 | | ||||
1048 | //If the caption is wider than the image, set the window size | ||||
1049 | //to fit the caption | ||||
1050 | if (m_Caption->width() > w) | ||||
1051 | w = m_Caption->width(); | ||||
1052 | if(initialLoad) | ||||
1053 | resize(w, image.height()); | ||||
1054 | else | ||||
1055 | { | ||||
1056 | m_View->refreshImage(); | ||||
1057 | } | ||||
1058 | | ||||
1059 | update(); | ||||
1060 | show(); | ||||
1061 | | ||||
1062 | return true; | ||||
1063 | #else | ||||
1064 | return false; | ||||
1065 | #endif | ||||
1066 | } | ||||
1067 | | ||||
1068 | void XPlanetImageViewer::saveFileToDisk() | ||||
1069 | { | ||||
1070 | #ifndef KSTARS_LITE | ||||
1071 | | ||||
1072 | QUrl newURL = QFileDialog::getSaveFileUrl(KStars::Instance(), i18n("Save Image"), lastURL); // save-dialog with default filename | ||||
1073 | if (!newURL.isEmpty()) | ||||
1074 | { | ||||
1075 | if (newURL.toLocalFile().endsWith(QLatin1String(".png")) == false) | ||||
1076 | newURL.setPath(newURL.toLocalFile() + ".png"); | ||||
1077 | | ||||
1078 | QFile f(newURL.toLocalFile()); | ||||
1079 | if (f.exists()) | ||||
1080 | { | ||||
1081 | int r = KMessageBox::warningContinueCancel(parentWidget(), | ||||
1082 | i18n("A file named \"%1\" already exists. " | ||||
1083 | "Overwrite it?", | ||||
1084 | newURL.fileName()), | ||||
1085 | i18n("Overwrite File?"), KStandardGuiItem::overwrite()); | ||||
1086 | if (r == KMessageBox::Cancel) | ||||
1087 | return; | ||||
1088 | | ||||
1089 | f.remove(); | ||||
1090 | } | ||||
1091 | | ||||
1092 | lastURL = QUrl(newURL.toString(QUrl::RemoveFilename)); | ||||
1093 | | ||||
1094 | saveFile(newURL); | ||||
1095 | } | ||||
1096 | #endif | ||||
1097 | } | ||||
1098 | | ||||
1099 | void XPlanetImageViewer::saveFile(QUrl &url) | ||||
1100 | { | ||||
1101 | #ifndef KSTARS_LITE | ||||
1102 | if (file.copy(url.toLocalFile()) == false) | ||||
1103 | { | ||||
1104 | QString text = i18n("Saving of the image %1 failed.", url.toString()); | ||||
1105 | KMessageBox::error(this, text); | ||||
1106 | } | ||||
1107 | else | ||||
1108 | KStars::Instance()->statusBar()->showMessage(i18n("Saved image to %1", url.toString())); | ||||
1109 | #endif | ||||
1110 | } | ||||
1111 | | ||||
1112 | void XPlanetImageViewer::invertColors() | ||||
1113 | { | ||||
1114 | #ifndef KSTARS_LITE | ||||
1115 | // Invert colors | ||||
1116 | m_View->invertPixels(); | ||||
1117 | m_View->update(); | ||||
1118 | #endif | ||||
1119 | } | ||||
1120 | |
You are setting a non-empty frame, but then you are neither taking this into account when calculating the actual size in resizeEvent, nor avoid to painting on the frame.