Kstars

fitslabel.cpp
1/*
2 SPDX-FileCopyrightText: 2003-2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 SPDX-FileCopyrightText: 2016-2017 Robert Lancaster <rlancaste@gmail.com>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8#include "fitslabel.h"
9
10#include "config-kstars.h"
11
12#include "fitsdata.h"
13#include "fitsview.h"
14#include "kspopupmenu.h"
15#include "kstars.h"
16#include "kstarsdata.h"
17#include "Options.h"
18#include "skymap.h"
19#include "ksnotification.h"
20#include <QGuiApplication>
21
22#ifdef HAVE_INDI
23#include "basedevice.h"
24#include "indi/indilistener.h"
25#include "indi/indiconcretedevice.h"
26#include "indi/indimount.h"
27#endif
28
29#include <QScrollBar>
30#include <QToolTip>
31
32#define BASE_OFFSET 50
33#define ZOOM_DEFAULT 100.0
34#define ZOOM_MIN 10
35#define ZOOM_MAX 400
36#define ZOOM_LOW_INCR 10
37#define ZOOM_HIGH_INCR 50
38
39FITSLabel::FITSLabel(FITSView *View, QWidget *parent) : QLabel(parent)
40{
41 this->view = View;
42 prevscale = 0.0;
43 //Rubber Band options
44 roiRB = new QRubberBand( QRubberBand::Rectangle, this);
45 roiRB->setAttribute(Qt::WA_TransparentForMouseEvents, 1);
46 roiRB->setGeometry(QRect(QPoint(1, 1), QPoint(100, 100)));
47
50 red70.setAlphaF(0.7);
51
53 roiRB->setPalette(pal);
54 QToolTip::showText(QPoint(1, 1), "Move Once to show selection stats", this);
55}
56
57
58void FITSLabel::setSize(double w, double h)
59{
60 m_Width = w;
61 m_Height = h;
62 m_Size = w * h;
63}
64
65bool FITSLabel::getMouseButtonDown()
66{
67 return mouseButtonDown;
68}
69
70/**
71This method was added to make the panning function work.
72If the mouse button is released, it resets mouseButtonDown variable and the mouse cursor.
73 */
74void FITSLabel::mouseReleaseEvent(QMouseEvent *e)
75{
76 float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
77
78 double x = round(e->x() / scale);
79 double y = round(e->y() / scale);
80
81 m_p2.setX(x);//record second point for selection Rectangle
82 m_p2.setY(y);
83
84 if (view->getCursorMode() == FITSView::dragCursor)
85 {
86 mouseButtonDown = false;
87 view->updateMouseCursor();
88 if( isRoiSelected && view->isSelectionRectShown())
89 {
90 QRect roiRaw = roiRB->geometry();
91 emit rectangleSelected(roiRaw.topLeft() / prevscale, roiRaw.bottomRight() / prevscale, true);
92 updateROIToolTip(e->globalPos());
93 }
94 if( e->modifiers () == Qt::ShiftModifier && view->isSelectionRectShown())
95 {
96 QRect roiRaw = roiRB->geometry();
97 emit rectangleSelected(roiRaw.topLeft() / prevscale, roiRaw.bottomRight() / prevscale, true);
98 updateROIToolTip(e->globalPos());
99 }
100 isRoiSelected = false;
101 }
102
103}
104
105void FITSLabel::leaveEvent(QEvent *e)
106{
107 Q_UNUSED(e)
108 view->updateMagnifyingGlass(-1, -1);
109 emit mouseOverPixel(-1, -1);
110}
111
112/**
113I added some things to the top of this method to allow panning and Scope slewing to function.
114If you are in the dragMouse mode and the mousebutton is pressed, The method checks the difference
115between the location of the last point stored and the current event point to see how the mouse has moved.
116Then it moves the scrollbars and thus the view to the right location.
117Then it stores the current point so next time it can do it again.
118 */
119void FITSLabel::mouseMoveEvent(QMouseEvent *e)
120{
121 const QSharedPointer<FITSData> &imageData = view->imageData();
122 if (imageData.isNull())
123 return;
124
125 float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
126
127 double x = round(e->x() / scale);
128 double y = round(e->y() / scale);
129
130 //Panning
131 if (e->modifiers() != Qt::ShiftModifier && view->getCursorMode() == FITSView::dragCursor && mouseButtonDown )
132 {
134 int dx = newPoint.x() - lastMousePoint.x();
135 int dy = newPoint.y() - lastMousePoint.y();
136 view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() - dx);
137 view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() - dy);
138
139 lastMousePoint = newPoint;
140 }
141 if( e->buttons() & Qt::LeftButton && view->getCursorMode() == FITSView::dragCursor )
142 {
143 //Translation of ROI
144 if(isRoiSelected && !mouseButtonDown)
145 {
146
147 int xdiff = x - prevPoint.x();
148 int ydiff = y - prevPoint.y();
149 roiRB->setGeometry(roiRB->geometry().translated(round(xdiff * scale), round(ydiff * scale)));
150 prevPoint = QPoint(x, y);
151
152 QRect roiRaw = roiRB->geometry();
153 // Opting to update stats on the go is extremely laggy for large images, only update if small image
154 if(!view->isLargeImage())
155 {
156 emit rectangleSelected(roiRaw.topLeft() / prevscale, roiRaw.bottomRight() / prevscale, true);
157 updateROIToolTip(e->globalPos());
158 }
159 }
160 //Stretching of ROI
161 if(e->modifiers() == Qt::ShiftModifier && !isRoiSelected && view->isSelectionRectShown())
162 {
163 roiRB->setGeometry(QRect(m_p1 * scale, QPoint(x, y)*scale).normalized());
164
165 QRect roiRaw = roiRB->geometry();
166 // Opting to update stats on the go is extremely laggy for large images, only update if small image
167 if(!view->isLargeImage())
168 {
169 emit rectangleSelected(roiRaw.topLeft() / prevscale, roiRaw.bottomRight() / prevscale, true);
170 updateROIToolTip(e->globalPos());
171 }
172 }
173 }
174
175 uint8_t const *buffer = imageData->getImageBuffer();
176
177 if (buffer == nullptr)
178 return;
179
180 x = round(e->x() / scale);
181 y = round(e->y() / scale);
182
184 view->updateMagnifyingGlass(x, y);
185 else
186 view->updateMagnifyingGlass(-1, -1);
187
188 x = KSUtils::clamp(x, 1.0, m_Width);
189 y = KSUtils::clamp(y, 1.0, m_Height);
190
191 emit newStatus(QString("X:%1 Y:%2").arg(static_cast<int>(x)).arg(static_cast<int>(y)), FITS_POSITION);
192
193 // Range is 0 to dim-1 when accessing array
194 x -= 1;
195 y -= 1;
196
197 emit mouseOverPixel(x, y);
198
199 int index = y * m_Width + x;
200 QString stringValue;
201
202 switch (imageData->getStatistics().dataType)
203 {
204 case TBYTE:
205 stringValue = QLocale().toString(buffer[index]);
206 break;
207
208 case TSHORT:
209 stringValue = QLocale().toString((reinterpret_cast<int16_t const*>(buffer))[index]);
210 break;
211
212 case TUSHORT:
213 stringValue = QLocale().toString((reinterpret_cast<uint16_t const*>(buffer))[index]);
214 break;
215
216 case TLONG:
217 stringValue = QLocale().toString((reinterpret_cast<int32_t const*>(buffer))[index]);
218 break;
219
220 case TULONG:
221 stringValue = QLocale().toString((reinterpret_cast<uint32_t const*>(buffer))[index]);
222 break;
223
224 case TFLOAT:
225 stringValue = QLocale().toString((reinterpret_cast<float const*>(buffer))[index], 'f', 5);
226 break;
227
228 case TLONGLONG:
229 stringValue = QLocale().toString(static_cast<int>((reinterpret_cast<int64_t const*>(buffer))[index]));
230 break;
231
232 case TDOUBLE:
233 stringValue = QLocale().toString((reinterpret_cast<float const*>(buffer))[index], 'f', 5);
234
235 break;
236
237 default:
238 break;
239
240 }
241
242 if(view->isSelectionRectShown())
243 {
244 if (roiRB->geometry().contains(e->pos()))
245 updateROIToolTip(e->globalPos());
246 else
248 }
249
250 emit newStatus(stringValue, FITS_VALUE);
251
252 if (imageData->hasWCS() &&
253 !view->isSelectionRectShown() &&
254 view->getCursorMode() != FITSView::selectCursor)
255 {
258 if(imageData->pixelToWCS(wcsPixelPoint, wcsCoord))
259 {
260 m_RA = wcsCoord.ra0();
261 m_DE = wcsCoord.dec0();
262 emit newStatus(QString("%1 , %2").arg(m_RA.toHMSString(), m_DE.toDMSString()), FITS_WCS);
263 }
264
265 bool objFound = false;
266 for (auto &listObject : imageData->getSkyObjects())
267 {
268 if ((std::abs(listObject->x() - x) < 5 / scale) && (std::abs(listObject->y() - y) < 5 / scale))
269 {
271 QToolTip::text() + '\n' + listObject->skyObject()->name() + '\n' + listObject->skyObject()->longname(), this);
272 objFound = true;
273 break;
274 }
275 }
276 if (!objFound && !view->isSelectionRectShown())
278 }
279
280 double HFR = view->imageData()->getHFR(x + 1, y + 1, scale);
281
282
283 if (HFR > 0)
284 {
285 QString tip = QToolTip::text();
286 // Don't i18n away HFR: because the RegExp below checks for HFR: to make sure there aren't duplicate strings added.
287 QString hfrStr = QString("HFR: %1").arg(HFR, 4, 'f', 2);
288 if (tip.isEmpty() || tip == hfrStr)
290 else
291 {
292 QRegExp hfrRegEx("HFR\\: \\d+\\.\\d\\d");
293 if (tip.contains(hfrRegEx))
295 else
296 QToolTip::showText(e->globalPos(), QToolTip::text() + '\n' + hfrStr, this);
297 }
298 }
299
300 e->accept();
301}
302
303/**
304I added some things to the top of this method to allow panning and Scope slewing to function.
305If in dragMouse mode, the Panning function works by storing the cursor position when the mouse was pressed and setting
306the mouseButtonDown variable to true.
307If in ScopeMouse mode and the mouse is clicked, if there is WCS data and a scope is available, the method will verify that you actually
308do want to slew to the WCS coordinates associated with the click location. If so, it calls the centerTelescope function.
309 */
310
311void FITSLabel::mousePressEvent(QMouseEvent *e)
312{
313 float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
314
315 double x = round(e->x() / scale);
316 double y = round(e->y() / scale);
317
318 m_p1.setX(x);//record first point for selection Rectangle
319 m_p1.setY(y);
320 prevPoint = QPoint(x, y);
321 prevscale = scale;
322 x = KSUtils::clamp(x, 1.0, m_Width);
323 y = KSUtils::clamp(y, 1.0, m_Height);
324
325 if(e->buttons() & Qt::LeftButton && view->getCursorMode() == FITSView::dragCursor)
326 {
327 if(roiRB->geometry().contains(x * scale, y * scale))
328 isRoiSelected = true;
329 }
330
331 if (view->getCursorMode() == FITSView::dragCursor && !isRoiSelected)
332 {
333 mouseButtonDown = true;
334 lastMousePoint = e->globalPos();
335 view->updateMouseCursor();
336 }
337 else if (e->buttons() & Qt::LeftButton && view->getCursorMode() == FITSView::scopeCursor)
338 {
339#ifdef HAVE_INDI
340 const QSharedPointer<FITSData> &view_data = view->imageData();
341 if (view_data->hasWCS())
342 {
345 if(view_data->pixelToWCS(wcsPixelPoint, wcsCoord))
346 {
347 auto ra = wcsCoord.ra0();
348 auto dec = wcsCoord.dec0();
350 nullptr,
351 "Slewing to Coordinates: \nRA: " + ra.toHMSString() +
352 "\nDec: " + dec.toDMSString(),
353 i18n("Continue Slew"), KStandardGuiItem::cont(),
354 KStandardGuiItem::cancel(), "continue_slew_warning"))
355 {
356 centerTelescope(ra.Hours(), dec.Degrees());
357 view->setCursorMode(view->lastMouseMode);
358 view->updateScopeButton();
359 }
360 }
361 }
362#endif
363 }
364
365
366#ifdef HAVE_INDI
367 const QSharedPointer<FITSData> &view_data = view->imageData();
368
369 if (e->buttons() & Qt::RightButton && view->getCursorMode() != FITSView::scopeCursor)
370 {
371 mouseReleaseEvent(e);
372 if (view_data->hasWCS())
373 {
374 for (auto &listObject : view_data->getSkyObjects())
375 {
376 if ((std::abs(listObject->x() - x) < 10 / scale) && (std::abs(listObject->y() - y) < 10 / scale))
377 {
378 SkyObject *object = listObject->skyObject();
379 KSPopupMenu *pmenu;
380 pmenu = new KSPopupMenu();
381 object->initPopupMenu(pmenu);
383 for (auto action : actions)
384 {
385 if (action->text().left(7) == "Starhop")
386 pmenu->removeAction(action);
387 if (action->text().left(7) == "Angular")
388 pmenu->removeAction(action);
389 if (action->text().left(8) == "Add flag")
390 pmenu->removeAction(action);
391 if (action->text().left(12) == "Attach Label")
392 pmenu->removeAction(action);
393 }
394 pmenu->popup(e->globalPos());
395 KStars::Instance()->map()->setClickedObject(object);
396 break;
397 }
398 }
399 }
400
401 if (fabs(view->markerCrosshair.x() - x) <= 15 && fabs(view->markerCrosshair.y() - y) <= 15)
402 emit markerSelected(0, 0);
403 }
404#endif
405
406 if (e->buttons() & Qt::LeftButton)
407 {
408 if (view->getCursorMode() == FITSView::selectCursor)
409 emit pointSelected(x, y);
410 else if (view->getCursorMode() == FITSView::crosshairCursor)
411 emit pointSelected(x + 5 / scale, y + 5 / scale);
412 }
413}
414
415void FITSLabel::mouseDoubleClickEvent(QMouseEvent *e)
416{
417 double x, y;
418
419 x = round(e->x() / (view->getCurrentZoom() / ZOOM_DEFAULT));
420 y = round(e->y() / (view->getCurrentZoom() / ZOOM_DEFAULT));
421
422 x = KSUtils::clamp(x, 1.0, m_Width);
423 y = KSUtils::clamp(y, 1.0, m_Height);
424
425 emit markerSelected(x, y);
426
427 return;
428}
429
430void FITSLabel::centerTelescope(double raJ2000, double decJ2000)
431{
432#ifdef HAVE_INDI
433
434 if (INDIListener::Instance()->size() == 0)
435 {
436 KSNotification::sorry(i18n("KStars did not find any active mounts."));
437 return;
438 }
439
440 for (auto &oneDevice : INDIListener::devices())
441 {
442 if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
443 continue;
444
445 if (oneDevice->isConnected() == false)
446 {
447 KSNotification::error(i18n("Mount %1 is offline. Please connect and retry again.", oneDevice->getDeviceName()));
448 return;
449 }
450
451 auto mount = oneDevice->getMount();
452 if (!mount)
453 continue;
454
455 SkyPoint selectedObject;
456 selectedObject.setRA0(raJ2000);
457 selectedObject.setDec0(decJ2000);
458 selectedObject.apparentCoord(J2000, KStarsData::Instance()->ut().djd());
459 mount->Slew(&selectedObject);
460 return;
461 }
462
463 KSNotification::sorry(i18n("KStars did not find any active mounts."));
464
465#else
466
469
470#endif
471}
472
473void FITSLabel::showRubberBand(bool on)
474{
475 if(on)
476 {
477 roiRB->show();
478 }
479 else
480 {
481 roiRB->hide();
482 }
483}
484
485/// Scales the rubberband on zoom
486void FITSLabel::zoomRubberBand(double scale)
487{
488 QRect r = roiRB->geometry() ;
489
490 if(prevscale == 0.0 )
491 prevscale = scale;
492
493 double ap = r.width() / r.height();
494 double ow = r.width() * scale / prevscale;
495 double oh = r.height() * scale / prevscale;
496
497 int rx, ry;
498 rx = round(r.topLeft().x() * scale / prevscale);
499 ry = round(r.topLeft().y() * scale / prevscale);
500 r.setTopLeft(QPoint(rx, ry));
501
502 rx = round(r.bottomRight().x() * scale / prevscale);
503 ry = round(r.bottomRight().y() * scale / prevscale);
504 r.setBottomRight(QPoint(rx, ry));
505
506 if (ap != r.width() / r.height())
507 {
508 r.setSize(QSize(ow, oh));
509 }
510
511 roiRB->setGeometry(r);
512 prevscale = scale;
513}
514/// Intended to take raw rect as input from FITSView context
515void FITSLabel::setRubberBand(QRect rect)
516{
517 float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
518
519 int rx, ry;
520 rx = round(rect.topLeft().x() * scale );
521 ry = round(rect.topLeft().y() * scale );
522 rect.setTopLeft(QPoint(rx, ry));
523
524 rx = round(rect.bottomRight().x() * scale );
525 ry = round(rect.bottomRight().y() * scale );
526 rect.setBottomRight(QPoint(rx, ry));
527
528 roiRB->setGeometry(rect);
529 prevscale = scale;
530}
531
532void FITSLabel::updateROIToolTip(const QPoint p)
533{
534 auto result = QString("σ %1").arg(QString::number(view->imageData()->getAverageStdDev(true), 'f', 2));
535 result += "\nx̄ " + QString::number(view->imageData()->getAverageMean(true), 'f', 2);
536 result += "\nM " + QString::number(view->imageData()->getAverageMedian(true), 'f', 2);
537 QToolTip::showText(p, result, this);
538}
INDIListener is responsible for creating ISD::GDInterface generic devices as new devices arrive from ...
The KStars Popup Menu.
Definition kspopupmenu.h:35
static KStars * Instance()
Definition kstars.h:123
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
The sky coordinates of a point in the sky.
Definition skypoint.h:45
void apparentCoord(long double jd0, long double jdf)
Computes the apparent coordinates for this SkyPoint for any epoch, accounting for the effects of prec...
Definition skypoint.cpp:700
void setRA0(dms r)
Sets RA0, the catalog Right Ascension.
Definition skypoint.h:94
void setDec0(dms d)
Sets Dec0, the catalog Declination.
Definition skypoint.h:119
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:287
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:378
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT SimpleJob * mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags=DefaultFlags)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
KGuiItem cont()
KGuiItem cancel()
QScrollBar * horizontalScrollBar() const const
QScrollBar * verticalScrollBar() const const
void setValue(int)
void accept()
Qt::KeyboardModifiers queryKeyboardModifiers()
Qt::KeyboardModifiers modifiers() const const
QString toString(QDate date, FormatType format) const const
void popup(const QPoint &p, QAction *atAction)
QPoint globalPos() const const
QPoint pos() const const
int x() const const
int y() const const
void setX(int x)
void setY(int y)
int x() const const
int y() const const
qreal x() const const
qreal y() const const
QPoint bottomRight() const const
int height() const const
void setBottomRight(const QPoint &position)
void setSize(const QSize &size)
void setTopLeft(const QPoint &position)
QPoint topLeft() const const
int width() const const
void setGeometry(const QRect &rect)
Qt::MouseButtons buttons() const const
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
ShiftModifier
LeftButton
WA_TransparentForMouseEvents
QTextStream & dec(QTextStream &stream)
void hideText()
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
QString text()
QList< QAction * > actions() const const
void hide()
void removeAction(QAction *action)
void show()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:03 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.