Kstars

mount.cpp
1/*
2 SPDX-FileCopyrightText: 2015 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "mount.h"
8
9#include <QQuickView>
10#include <QQuickItem>
11#include <indicom.h>
12
13#include <knotification.h>
14#include <KLocalizedContext>
15#include <KActionCollection>
16
17#include <kio_version.h>
18
19#include "Options.h"
20
21#include "ksmessagebox.h"
22#include "indi/driverinfo.h"
23#include "indi/indicommon.h"
24#include "indi/clientmanager.h"
25#include "indi/indigps.h"
26
27
28#include "mountadaptor.h"
29#include "mountcontrolpanel.h"
30
31#include "ekos/manager.h"
32#include "ekos/auxiliary/opticaltrainmanager.h"
33#include "ekos/auxiliary/profilesettings.h"
34#include "ekos/auxiliary/opticaltrainsettings.h"
35#include "ekos/manager/meridianflipstate.h"
36#include "ekos/align/polaralignmentassistant.h"
37
38#include "kstars.h"
39#include "skymapcomposite.h"
40#include "dialogs/finddialog.h"
41#include "kstarsdata.h"
42
43#include <basedevice.h>
44
45#include <ekos_mount_debug.h>
46
47extern const char *libindi_strings_context;
48
49#define ABORT_DISPATCH_LIMIT 3
50
51namespace Ekos
52{
53
54Mount::Mount()
55{
56 setupUi(this);
57
58 new MountAdaptor(this);
59 QDBusConnection::sessionBus().registerObject("/KStars/Ekos/Mount", this);
60 // Set up DBus interfaces
61 QPointer<QDBusInterface> ekosInterface = new QDBusInterface("org.kde.kstars", "/KStars/Ekos", "org.kde.kstars.Ekos",
63 qDBusRegisterMetaType<SkyPoint>();
64
65 // Connecting DBus signals
66 QDBusConnection::sessionBus().connect("org.kde.kstars", "/KStars/Ekos", "org.kde.kstars.Ekos", "newModule", this,
68
69 m_Mount = nullptr;
70
71 // initialize the state machine
72 mf_state.reset(new MeridianFlipState());
73 // connect to the MF state maichine
74 getMeridianFlipState()->connectMount(this);
75
76 // set the status message in the mount tab and write it to the log
77 connect(mf_state.get(), &MeridianFlipState::newMeridianFlipMountStatusText, [&](const QString & text)
78 {
79 meridianFlipStatusWidget->setStatus(text);
80 if (mf_state->getMeridianFlipMountState() != MeridianFlipState::MOUNT_FLIP_NONE &&
81 mf_state->getMeridianFlipMountState() != MeridianFlipState::MOUNT_FLIP_PLANNED)
82 appendLogText(text);
83 });
84 connect(mountToolBoxB, &QPushButton::clicked, this, &Mount::toggleMountToolBox);
85
86 connect(clearAlignmentModelB, &QPushButton::clicked, this, &Mount::resetModel);
87
88 connect(clearParkingB, &QPushButton::clicked, this, [this]()
89 {
90 if (m_Mount)
91 m_Mount->clearParking();
92
93 });
94
95 connect(purgeConfigB, &QPushButton::clicked, this, [this]()
96 {
97 if (m_Mount)
98 {
100 i18n("Are you sure you want to clear all mount configurations?"),
101 i18n("Mount Configuration")) == KMessageBox::Continue)
102 {
103 resetModel();
104 m_Mount->clearParking();
105 m_Mount->setConfig(PURGE_CONFIG);
106 }
107 }
108 });
109
110 // If time source changes, sync time source
111 connect(Options::self(), &Options::timeSourceChanged, this, &Mount::syncTimeSource);
112 connect(Options::self(), &Options::locationSourceChanged, this, &Mount::syncLocationSource);
113
114 connect(enableAltitudeLimits, &QCheckBox::toggled, this, [this](bool toggled)
115 {
116 m_AltitudeLimitEnabled = toggled;
117 setAltitudeLimits(toggled);
118
119 });
120 connect(enableAltitudeLimitsTrackingOnly, &QCheckBox::toggled, this, [this]()
121 {
122 setAltitudeLimits(enableAltitudeLimits->isChecked());
123 });
124
125 m_AltitudeLimitEnabled = enableAltitudeLimits->isChecked();
127
128 // meridian flip
129 connect(mf_state.get(), &MeridianFlipState::newMeridianFlipMountStatusText, meridianFlipStatusWidget,
130 &MeridianFlipStatusWidget::setStatus);
131
132 connect(&autoParkTimer, &QTimer::timeout, this, &Mount::startAutoPark);
133 connect(startTimerB, &QPushButton::clicked, this, &Mount::startParkTimer);
134 connect(stopTimerB, &QPushButton::clicked, this, &Mount::stopParkTimer);
135
136 // Setup Debounce timer to limit over-activation of settings changes
137 m_DebounceTimer.setInterval(500);
138 m_DebounceTimer.setSingleShot(true);
139 connect(&m_DebounceTimer, &QTimer::timeout, this, &Mount::settleSettings);
140
141 stopTimerB->setEnabled(false);
142
143 if (parkEveryDay->isChecked())
144 startTimerB->animateClick();
145
146 m_ControlPanel.reset(new MountControlPanel());
147 connect(m_ControlPanel.get(), &MountControlPanel::newMotionCommand, this, &Mount::motionCommand);
148 connect(m_ControlPanel.get(), &MountControlPanel::aborted, this, &Mount::abort);
149 connect(m_ControlPanel.get(), &MountControlPanel::newSlewRate, this, &Mount::setSlewRate);
150 connect(m_ControlPanel.get(), &MountControlPanel::slew, this, &Mount::slew);
151 connect(m_ControlPanel.get(), &MountControlPanel::sync, this, &Mount::sync);
152 connect(m_ControlPanel.get(), &MountControlPanel::park, this, &Mount::park);
153 connect(m_ControlPanel.get(), &MountControlPanel::unpark, this, &Mount::unpark);
154 connect(m_ControlPanel.get(), &MountControlPanel::updownReversed, this, &Mount::setUpDownReversed);
155 connect(m_ControlPanel.get(), &MountControlPanel::leftrightReversed, this, &Mount::setLeftRightReversed);
156 connect(m_ControlPanel.get(), &MountControlPanel::center, this, &Mount::centerMount);
157
158 connect(mountMotion, &MountMotionWidget::newMotionCommand, this, &Mount::motionCommand);
159 connect(mountMotion, &MountMotionWidget::newSlewRate, this, &Mount::setSlewRate);
160 connect(mountMotion, &MountMotionWidget::aborted, this, &Mount::abort);
161 connect(mountMotion, &MountMotionWidget::updownReversed, this, &Mount::setUpDownReversed);
162 connect(mountMotion, &MountMotionWidget::leftrightReversed, this, &Mount::setLeftRightReversed);
163
164 // forward J2000 selection to the target widget, which does not have its own selection
165 connect(mountPosition, &MountPositionWidget::J2000Enabled, mountTarget, &MountTargetWidget::setJ2000Enabled);
166
167 // forward target commands
168 connect(mountTarget, &MountTargetWidget::slew, this, &Mount::slew);
169 connect(mountTarget, &MountTargetWidget::sync, this, &Mount::sync);
170
171 //Note: This is to prevent a button from being called the default button
172 //and then executing when the user hits the enter key such as when on a Text Box
174 for (auto &button : qButtons)
175 button->setAutoDefault(false);
176
177 loadGlobalSettings();
178 connectSettings();
179
180 setupOpticalTrainManager();
181}
182
183Mount::~Mount()
184{
185 autoParkTimer.stop();
186}
187
188void Mount::setupParkUI()
189{
190 if (m_Mount == nullptr)
191 return;
192
193 if (m_Mount->canPark())
194 {
195 switch(m_Mount->parkStatus())
196 {
197 case ISD::PARK_PARKED:
198 parkingLabel->setText("Parked");
199 break;
200 case ISD::PARK_PARKING:
201 parkingLabel->setText("Parking");
202 break;
203 case ISD::PARK_UNPARKING:
204 parkingLabel->setText("Unparking");
205 break;
206 case ISD::PARK_UNPARKED:
207 parkingLabel->setText("Unparked");
208 break;
209 case ISD::PARK_ERROR:
210 parkingLabel->setText("Park Error");
211 break;
212 case ISD::PARK_UNKNOWN:
213 parkingLabel->setText("Park Status Unknown");
214 break;
215 }
216 parkB->setEnabled(m_Mount->parkStatus() == ISD::PARK_UNPARKED);
217 unparkB->setEnabled(m_Mount->parkStatus() == ISD::PARK_PARKED);
218 }
219 else
220 {
221 parkB->setEnabled(false);
222 unparkB->setEnabled(false);
223 parkingLabel->setText("");
224 }
225}
226
228{
229 if (device && device == m_Mount)
230 {
232 return false;
233 }
234
235 if (m_Mount)
236 m_Mount->disconnect(m_Mount, nullptr, this, nullptr);
237
238 m_Mount = device;
239
240 if (m_Mount)
241 {
242 connect(m_Mount, &ISD::ConcreteDevice::Connected, this, [this]()
243 {
244 setEnabled(true);
245 });
246 connect(m_Mount, &ISD::ConcreteDevice::Disconnected, this, [this]()
247 {
248 setEnabled(false);
249 opticalTrainCombo->setEnabled(true);
250 trainLabel->setEnabled(true);
251 });
252 }
253 else
254 return false;
255
256 mainLayout->setEnabled(true);
257
258 // forward the new mount to the meridian flip state machine
259 mf_state->setMountConnected(device != nullptr);
260
261 connect(m_Mount, &ISD::Mount::propertyUpdated, this, &Mount::updateProperty);
266 connect(m_Mount, &ISD::Mount::slewRateChanged, this, &Mount::slewRateChanged);
267 connect(m_Mount, &ISD::Mount::pierSideChanged, this, &Mount::pierSideChanged);
268 connect(m_Mount, &ISD::Mount::axisReversed, this, &Mount::syncAxisReversed);
269 connect(m_Mount, &ISD::Mount::Disconnected, this, [this]()
270 {
271 m_ControlPanel->hide();
272 });
273 connect(m_Mount, &ISD::Mount::newParkStatus, this, [&](ISD::ParkStatus status)
274 {
275 m_ParkStatus = status;
276 emit newParkStatus(status);
277
278 setupParkUI();
279
280 // If mount is unparked AND every day auto-paro check is ON
281 // AND auto park timer is not yet started, we try to initiate it.
282 if (status == ISD::PARK_UNPARKED && parkEveryDay->isChecked() && autoParkTimer.isActive() == false)
283 startTimerB->animateClick();
284 });
285
286 // If mount is ready then let's set it up.
287 if (m_Mount->isReady())
288 {
289 if (enableAltitudeLimits->isChecked())
290 m_Mount->setAltLimits(minimumAltLimit->value(), maximumAltLimit->value(), enableAltitudeLimitsTrackingOnly->isChecked());
291 else
292 m_Mount->setAltLimits(-91, +91, false);
293
295
296 // Send initial status
297 m_Status = m_Mount->status();
298 emit newStatus(m_Status);
299
300 m_ParkStatus = m_Mount->parkStatus();
301 emit newParkStatus(m_ParkStatus);
302 emit ready();
303 }
304 // Otherwise, let's wait for mount to be ready
305 else
306 {
307 connect(m_Mount, &ISD::Mount::ready, this, [this]()
308 {
309 if (enableAltitudeLimits->isChecked())
310 m_Mount->setAltLimits(minimumAltLimit->value(), maximumAltLimit->value(), enableAltitudeLimitsTrackingOnly->isChecked());
311 else
312 m_Mount->setAltLimits(-91, +91, false);
313
315
316 // Send initial status
317 m_Status = m_Mount->status();
318 emit newStatus(m_Status);
319
320 m_ParkStatus = m_Mount->parkStatus();
321 emit newParkStatus(m_ParkStatus);
322 emit ready();
323 });
324 }
325
326 return true;
327}
328
330{
331 // No duplicates
332 for (auto &oneSource : m_TimeSources)
333 {
334 if (oneSource->getDeviceName() == device->getDeviceName())
335 return false;
336 }
337
338 m_TimeSources.append(device);
339
340 timeSource->blockSignals(true);
341 timeSource->clear();
342 timeSource->addItem("KStars");
343
344 m_TimeSourcesList.clear();
345 m_TimeSourcesList.append("KStars");
346
347 for (auto &oneSource : m_TimeSources)
348 {
349 auto name = oneSource->getDeviceName();
350
351 m_TimeSourcesList.append(name);
352 timeSource->addItem(name);
353
354 if (name == Options::timeSource())
355 {
356 // If GPS, then refresh
357 auto refreshGPS = oneSource->getProperty("GPS_REFRESH");
358 if (refreshGPS)
359 {
360 auto sw = refreshGPS.getSwitch();
361 sw->at(0)->setState(ISS_ON);
362 oneSource->sendNewProperty(refreshGPS);
363 }
364 }
365 }
366
367 timeSource->setCurrentText(Options::timeSource());
368 timeSource->blockSignals(false);
369 return true;
370}
371
373{
374 // No duplicates
375 for (auto &oneSource : m_LocationSources)
376 {
377 if (oneSource->getDeviceName() == device->getDeviceName())
378 return false;
379 }
380
381 m_LocationSources.append(device);
382 locationSource->blockSignals(true);
383 locationSource->clear();
384 locationSource->addItem("KStars");
385
386 m_LocationSourcesList.clear();
387 m_LocationSourcesList.append("KStars");
388
389 for (auto &oneSource : m_LocationSources)
390 {
391 auto name = oneSource->getDeviceName();
392 locationSource->addItem(name);
393 m_LocationSourcesList.append(name);
394
395 if (name == Options::locationSource())
396 {
397 auto refreshGPS = oneSource->getProperty("GPS_REFRESH");
398 if (refreshGPS)
399 {
400 auto sw = refreshGPS.getSwitch();
401 sw->at(0)->setState(ISS_ON);
402 oneSource->sendNewProperty(refreshGPS);
403 }
404 }
405 }
406
407 locationSource->setCurrentText(Options::locationSource());
408 locationSource->blockSignals(false);
409 return true;
410}
411
412void Mount::removeDevice(const QSharedPointer<ISD::GenericDevice> &device)
413{
414 if (m_Mount && m_Mount->getDeviceName() == device->getDeviceName())
415 {
416 m_Mount->disconnect(this);
417 m_ControlPanel->hide();
418 qCDebug(KSTARS_EKOS_MOUNT) << "Removing mount driver" << m_Mount->getDeviceName();
419 m_Mount = nullptr;
420 }
421
422 m_TimeSources.erase(std::remove_if(m_TimeSources.begin(), m_TimeSources.end(), [device](const auto & oneSource)
423 {
424 return device->getDeviceName() == oneSource->getDeviceName();
425 }), m_TimeSources.end());
426
427 m_LocationSources.erase(std::remove_if(m_LocationSources.begin(), m_LocationSources.end(), [device](const auto & oneSource)
428 {
429 return device->getDeviceName() == oneSource->getDeviceName();
430 }), m_LocationSources.end());
431}
432
434{
435 if (!m_Mount || m_Mount->isConnected() == false)
436 return;
437
438 auto svp = m_Mount->getSwitch("TELESCOPE_SLEW_RATE");
439 // sync speed info on the UI and tht separate mount control
440 mountMotion->syncSpeedInfo(svp);
441 m_ControlPanel->mountMotion->syncSpeedInfo(svp);
442
443 if (m_Mount->canPark())
444 {
445 connect(parkB, &QPushButton::clicked, m_Mount, &ISD::Mount::park, Qt::UniqueConnection);
446 connect(unparkB, &QPushButton::clicked, m_Mount, &ISD::Mount::unpark, Qt::UniqueConnection);
447
448 m_ControlPanel->parkButtonObject->setEnabled(!m_Mount->isParked());
449 m_ControlPanel->unparkButtonObject->setEnabled(m_Mount->isParked());
450 }
451 else
452 {
453 disconnect(parkB, &QPushButton::clicked, m_Mount, &ISD::Mount::park);
454 disconnect(unparkB, &QPushButton::clicked, m_Mount, &ISD::Mount::unpark);
455
456 m_ControlPanel->parkButtonObject->setEnabled(false);
457 m_ControlPanel->unparkButtonObject->setEnabled(false);
458 }
459
460 m_ControlPanel->setProperty("isJ2000", m_Mount->isJ2000());
461 setupParkUI();
462
463 // Tracking State
464 svp = m_Mount->getSwitch("TELESCOPE_TRACK_STATE");
465 if (svp)
466 {
467 trackingLabel->setEnabled(true);
468 trackOnB->setEnabled(true);
469 trackOffB->setEnabled(true);
470 trackOnB->disconnect();
471 trackOffB->disconnect();
472 connect(trackOnB, &QPushButton::clicked, this, [&]()
473 {
474 m_Mount->setTrackEnabled(true);
475 });
476 connect(trackOffB, &QPushButton::clicked, this, [&]()
477 {
478#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0)
480 i18n("Are you sure you want to turn off mount tracking?"),
481 i18n("Mount Tracking"),
482 KGuiItem(i18nc("@action:button", "Yes")),
483 KGuiItem(i18nc("@action:button", "No")),
484 "turn_off_mount_tracking_dialog") == KMessageBox::ButtonCode::PrimaryAction)
485 m_Mount->setTrackEnabled(false);
486#else
487 if (KMessageBox::questionYesNo(KStars::Instance(),
488 i18n("Are you sure you want to turn off mount tracking?"),
489 i18n("Mount Tracking"),
490 KStandardGuiItem::yes(),
491 KStandardGuiItem::no(),
492 "turn_off_mount_tracking_dialog") == KMessageBox::Yes)
493 m_Mount->setTrackEnabled(false);
494#endif
495 });
496 }
497 else
498 {
499 trackingLabel->setEnabled(false);
500 trackOnB->setChecked(false);
501 trackOnB->setEnabled(false);
502 trackOffB->setChecked(false);
503 trackOffB->setEnabled(false);
504 }
505
506 m_ControlPanel->mountMotion->leftRightCheckObject->setProperty("checked", m_Mount->isReversed(AXIS_RA));
507 m_ControlPanel->mountMotion->upDownCheckObject->setProperty("checked", m_Mount->isReversed(AXIS_DE));
508}
509
511{
512 if (name == "Capture")
513 {
514 hasCaptureInterface = true;
515 mf_state->setHasCaptureInterface(true);
516 }
517}
518
519void Mount::updateTelescopeCoords(const SkyPoint &position, ISD::Mount::PierSide pierSide, const dms &ha)
520{
521 if (m_Mount == nullptr || !m_Mount->isConnected())
522 return;
523
524 telescopeCoord = position;
525
526 // forward the position to the position widget and control panel
527 mountPosition->updateTelescopeCoords(position, ha);
528 m_ControlPanel->mountPosition->updateTelescopeCoords(position, ha);
529
530 // No need to update coords if we are still parked.
531 if (m_Status == ISD::Mount::MOUNT_PARKED && m_Status == m_Mount->status())
532 return;
533
534 double currentAlt = telescopeCoord.altRefracted().Degrees();
535
536 if (minimumAltLimit->isEnabled() && (currentAlt < minimumAltLimit->value() || currentAlt > maximumAltLimit->value()) &&
537 (m_Mount->isTracking() || (!enableAltitudeLimitsTrackingOnly->isChecked() && m_Mount->isInMotion())))
538 {
539 if (currentAlt < minimumAltLimit->value())
540 {
541 // Only stop if current altitude is less than last altitude indicate worse situation
542 if (currentAlt < m_LastAltitude && m_AbortAltDispatch == -1)
543 {
544 appendLogText(i18n("Telescope altitude is below minimum altitude limit of %1. Aborting motion...",
545 QString::number(minimumAltLimit->value(), 'g', 3)));
546 m_Mount->abort();
547 m_Mount->setTrackEnabled(false);
548 //KNotification::event( QLatin1String( "OperationFailed" ));
550 m_AbortAltDispatch++;
551 }
552 }
553 else if (currentAlt > m_LastAltitude && m_AbortAltDispatch == -1)
554 {
555 // Only stop if current altitude is higher than last altitude indicate worse situation
556 appendLogText(i18n("Telescope altitude is above maximum altitude limit of %1. Aborting motion...",
557 QString::number(maximumAltLimit->value(), 'g', 3)));
558 m_Mount->abort();
559 m_Mount->setTrackEnabled(false);
560 //KNotification::event( QLatin1String( "OperationFailed" ));
562 m_AbortAltDispatch++;
563 }
564 }
565 else
566 m_AbortAltDispatch = -1;
567
568 //qCDebug(KSTARS_EKOS_MOUNT) << "MaximumHaLimit " << MaximumHaLimit->isEnabled() << " value " << MaximumHaLimit->value();
569
570 double haHours = rangeHA(ha.Hours());
571 // handle Ha limit:
572 // Telescope must report Pier Side
573 // MaximumHaLimit must be enabled
574 // for PierSide West -> East if Ha > MaximumHaLimit stop tracking
575 // for PierSide East -> West if Ha > MaximumHaLimit - 12 stop Tracking
576 if (maximumHaLimit->isEnabled())
577 {
578 // get hour angle limit
579 double haLimit = maximumHaLimit->value();
580 bool haLimitReached = false;
581 switch(pierSide)
582 {
583 case ISD::Mount::PierSide::PIER_WEST:
584 haLimitReached = haHours > haLimit;
585 break;
586 case ISD::Mount::PierSide::PIER_EAST:
587 haLimitReached = rangeHA(haHours + 12.0) > haLimit;
588 break;
589 default:
590 // can't tell so always false
591 haLimitReached = false;
592 break;
593 }
594
595 qCDebug(KSTARS_EKOS_MOUNT) << "Ha: " << haHours <<
596 " haLimit " << haLimit <<
597 " " << ISD::Mount::pierSideStateString(m_Mount->pierSide()) <<
598 " haLimitReached " << (haLimitReached ? "true" : "false") <<
599 " lastHa " << m_LastHourAngle;
600
601 // compare with last ha to avoid multiple calls
602 if (haLimitReached && (rangeHA(haHours - m_LastHourAngle) >= 0 ) &&
603 (m_AbortHADispatch == -1 ||
604 m_Mount->isInMotion()))
605 {
606 // moved past the limit, so stop
607 appendLogText(i18n("Telescope hour angle is more than the maximum hour angle of %1. Aborting motion...",
608 QString::number(maximumHaLimit->value(), 'g', 3)));
609 m_Mount->abort();
610 m_Mount->setTrackEnabled(false);
611 //KNotification::event( QLatin1String( "OperationFailed" ));
613 m_AbortHADispatch++;
614 // ideally we pause and wait until we have passed the pier flip limit,
615 // then do a pier flip and try to resume
616 // this will need changing to use a target position because the current HA has stopped.
617 }
618 }
619 else
620 m_AbortHADispatch = -1;
621
622 m_LastAltitude = currentAlt;
623 m_LastHourAngle = haHours;
624
625 ISD::Mount::Status currentStatus = m_Mount->status();
626 if (m_Status != currentStatus)
627 {
628 qCDebug(KSTARS_EKOS_MOUNT) << "Mount status changed from " << m_Mount->statusString(m_Status)
629 << " to " << m_Mount->statusString(currentStatus);
630
631 //setScopeStatus(currentStatus);
632
633 m_ControlPanel->statusTextObject->setProperty("text", m_Mount->statusString(currentStatus));
634 m_Status = currentStatus;
635 // forward
636 emit newStatus(m_Status);
637
638 setupParkUI();
639 m_ControlPanel->parkButtonObject->setEnabled(!m_Mount->isParked());
640 m_ControlPanel->unparkButtonObject->setEnabled(m_Mount->isParked());
641
642 QAction *a = KStars::Instance()->actionCollection()->action("telescope_track");
643 if (a != nullptr)
644 a->setChecked(currentStatus == ISD::Mount::MOUNT_TRACKING);
645 }
646
647 bool isTracking = (currentStatus == ISD::Mount::MOUNT_TRACKING);
648 if (trackOnB->isEnabled())
649 {
650 trackOnB->setChecked(isTracking);
651 trackOffB->setChecked(!isTracking);
652 }
653
654 // handle pier side display
655 pierSideLabel->setText(ISD::Mount::pierSideStateString(m_Mount->pierSide()));
656
657 // Auto Park Timer
658 if (autoParkTimer.isActive())
659 {
660 QTime remainingTime(0, 0, 0);
661 remainingTime = remainingTime.addMSecs(autoParkTimer.remainingTime());
662 countdownLabel->setText(remainingTime.toString("hh:mm:ss"));
663 emit autoParkCountdownUpdated(countdownLabel->text());
664 }
665}
666
667void Mount::updateProperty(INDI::Property prop)
668{
669 if (prop.isNameMatch("EQUATORIAL_EOD_COORD") || prop.isNameMatch("EQUATORIAL_COORD"))
670 {
671 auto nvp = prop.getNumber();
672
673 // if the meridian flip state machine is not initialized, return
674 if (getMeridianFlipState().isNull())
675 return;
676
677 switch (getMeridianFlipState()->getMeridianFlipStage())
678 {
679 case MeridianFlipState::MF_INITIATED:
680 if (nvp->s == IPS_BUSY && m_Mount != nullptr && m_Mount->isSlewing())
681 getMeridianFlipState()->updateMeridianFlipStage(MeridianFlipState::MF_FLIPPING);
682 break;
683
684 default:
685 break;
686 }
687 }
688 else if (prop.isNameMatch("TELESCOPE_SLEW_RATE"))
689 {
690 auto svp = prop.getSwitch();
691 mountMotion->updateSpeedInfo(svp);
692 m_ControlPanel->mountMotion->updateSpeedInfo(svp);
693 }
694}
695
696bool Mount::setSlewRate(int index)
697{
698 if (m_Mount)
699 return m_Mount->setSlewRate(index);
700
701 return false;
702}
703
704void Mount::setUpDownReversed(bool enabled)
705{
706 Options::setUpDownReversed(enabled);
707 if (m_Mount)
708 m_Mount->setReversedEnabled(AXIS_DE, enabled);
709}
710
711void Mount::setLeftRightReversed(bool enabled)
712{
713 Options::setLeftRightReversed(enabled);
714 if (m_Mount)
715 m_Mount->setReversedEnabled(AXIS_RA, enabled);
716}
717
718void Mount::setMeridianFlipValues(bool activate, double degrees)
719{
720 executeMeridianFlip->setChecked(activate);
721 meridianFlipOffsetDegrees->setValue(degrees);
722}
723
725{
726 // Clear the current target position is necessary due to a bug in some mount drivers
727 // which report a mount slew instead of a mount motion. For these mounts, ending a slew
728 // leads to setting the current target position, which is necessary for meridian flips
729 // Since we want to avoid meridian flips during and after finishing PAA, it needs to
730 // be set to nullptr.
731
732 if (stage != PolarAlignmentAssistant::PAH_IDLE)
733 mf_state->clearTargetPosition();
734
735 switch (stage)
736 {
737 // deactivate the meridian flip when the first capture is taken
738 case PolarAlignmentAssistant::PAH_FIRST_CAPTURE:
739 case PolarAlignmentAssistant::PAH_FIRST_SOLVE:
740 if (mf_state->isEnabled())
741 {
742 appendLogText(i18n("Meridian flip set inactive during polar alignment."));
743 mf_state->setEnabled(false);
744 }
745 break;
746 // activate it when the last rotation is finished or stopped
747 // for safety reasons, we add all stages after the last rotation
748 case PolarAlignmentAssistant::PAH_THIRD_CAPTURE:
749 case PolarAlignmentAssistant::PAH_THIRD_SOLVE:
750 case PolarAlignmentAssistant::PAH_STAR_SELECT:
751 case PolarAlignmentAssistant::PAH_REFRESH:
752 case PolarAlignmentAssistant::PAH_POST_REFRESH:
753 case PolarAlignmentAssistant::PAH_IDLE:
754 if (executeMeridianFlip->isChecked() && mf_state->isEnabled() == false)
755 {
756 appendLogText(i18n("Polar alignment motions finished, meridian flip activated."));
757 mf_state->setEnabled(executeMeridianFlip->isChecked());
758 }
759 break;
760 }
761}
762
763void Mount::appendLogText(const QString &text)
764{
765 m_LogText.insert(0, i18nc("log entry; %1 is the date, %2 is the text", "%1 %2",
766 KStarsData::Instance()->lt().toString("yyyy-MM-ddThh:mm:ss"), text));
767
768 qCInfo(KSTARS_EKOS_MOUNT) << text;
769
770 emit newLog(text);
771}
772
773void Mount::updateLog(int messageID)
774{
775 if (m_Mount == nullptr)
776 return;
777
778 auto message = m_Mount->getMessage(messageID);
779 m_LogText.insert(0, i18nc("Message shown in Ekos Mount module", "%1", message));
780
781 emit newLog(message);
782}
783
784void Mount::clearLog()
785{
786 m_LogText.clear();
787 emit newLog(QString());
788}
789
790void Mount::motionCommand(int command, int NS, int WE)
791{
792 if (m_Mount == nullptr || !m_Mount->isConnected())
793 return;
794
795 if (NS != -1)
796 {
797 m_Mount->MoveNS(static_cast<ISD::Mount::VerticalMotion>(NS),
798 static_cast<ISD::Mount::MotionCommand>(command));
799 }
800
801 if (WE != -1)
802 {
803 m_Mount->MoveWE(static_cast<ISD::Mount::HorizontalMotion>(WE),
804 static_cast<ISD::Mount::MotionCommand>(command));
805 }
806}
807
808
809void Mount::doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs)
810{
811 if (m_Mount == nullptr || !m_Mount->isConnected())
812 return;
813
814 m_Mount->doPulse(ra_dir, ra_msecs, dec_dir, dec_msecs);
815}
816
818{
819 if (m_Mount == nullptr)
820 return;
821
822 m_Mount->setAltLimits(minimumAltLimit->value(), maximumAltLimit->value(), enableAltitudeLimitsTrackingOnly->isChecked());
823}
824
826{
827 minAltLabel->setEnabled(enable);
828 maxAltLabel->setEnabled(enable);
829
830 minimumAltLimit->setEnabled(enable);
831 maximumAltLimit->setEnabled(enable);
832
833 enableAltitudeLimitsTrackingOnly->setEnabled(enable);
834
835 if (m_Mount)
836 {
837 if (enable)
838 m_Mount->setAltLimits(minimumAltLimit->value(), maximumAltLimit->value(), enableAltitudeLimitsTrackingOnly->isChecked());
839 else
840 m_Mount->setAltLimits(-91, +91, false);
841 }
842}
843
844// Used for meridian flip
846{
847 //Only enable if it was already enabled before and the MinimumAltLimit is currently disabled.
848 if (m_AltitudeLimitEnabled && minimumAltLimit->isEnabled() == false)
849 setAltitudeLimits(true);
850}
851
852// Used for meridian flip
854{
855 m_AltitudeLimitEnabled = enableAltitudeLimits->isChecked();
856 setAltitudeLimits(false);
857}
858
860{
861 maxHaLabel->setEnabled(enable);
862 maximumHaLimit->setEnabled(enable);
863}
864
866{
867 //Only enable if it was already enabled before and the minHaLimit is currently disabled.
868 if (m_HourAngleLimitEnabled && maximumHaLimit->isEnabled() == false)
870}
871
873{
874 m_HourAngleLimitEnabled = enableHaLimit->isChecked();
875
877}
878
879QList<double> Mount::altitudeLimits()
880{
881 QList<double> limits;
882
883 limits.append(minimumAltLimit->value());
884 limits.append(maximumAltLimit->value());
885
886 return limits;
887}
888
890{
891 minimumAltLimit->setValue(limits[0]);
892 maximumAltLimit->setValue(limits[1]);
893}
894
896{
897 enableAltitudeLimits->setChecked(enable);
898}
899
900bool Mount::altitudeLimitsEnabled()
901{
902 return enableAltitudeLimits->isChecked();
903}
904
905double Mount::hourAngleLimit()
906{
907 return maximumHaLimit->value();
908}
909
910void Mount::setHourAngleLimit(double limit)
911{
912 maximumHaLimit->setValue(limit);
913}
914
916{
917 enableHaLimit->setChecked(enable);
918}
919
920bool Mount::hourAngleLimitEnabled()
921{
922 return enableHaLimit->isChecked();
923}
924
925void Mount::setJ2000Enabled(bool enabled)
926{
927 m_ControlPanel->setJ2000Enabled(enabled);
928 mountPosition->setJ2000Enabled(enabled);
929}
930
931bool Mount::gotoTarget(const QString &target)
932{
933 auto object = KStarsData::Instance()->skyComposite()->findByName(target, false);
934
935 if (object != nullptr)
936 {
937 object->updateCoordsNow(KStarsData::Instance()->updateNum());
938 return slew(object->ra().Hours(), object->dec().Degrees());
939 }
940
941 return false;
942}
943
944bool Mount::gotoTarget(const SkyPoint &target)
945{
946 return slew(target.ra().Hours(), target.dec().Degrees());
947}
948
950{
951 mountTarget->setTargetName(name);
952 m_ControlPanel->setTargetName(name);
953}
954
955bool Mount::syncTarget(const QString &target)
956{
957 SkyObject *object = KStarsData::Instance()->skyComposite()->findByName(target, false);
958
959 if (object != nullptr)
960 {
961 object->updateCoordsNow(KStarsData::Instance()->updateNum());
962 return sync(object->ra().Hours(), object->dec().Degrees());
963 }
964
965 return false;
966}
967
968bool Mount::slew(double RA, double DEC)
969{
970 if (m_Mount == nullptr || m_Mount->isConnected() == false)
971 return false;
972
973 // calculate the new target
974 targetPosition = new SkyPoint(RA, DEC);
975 SkyPoint J2000Coord(targetPosition->ra(), targetPosition->dec());
976 J2000Coord.catalogueCoord(KStarsData::Instance()->ut().djd());
977 targetPosition->setRA0(J2000Coord.ra());
978 targetPosition->setDec0(J2000Coord.dec());
979
980 mf_state->setTargetPosition(targetPosition);
981 mf_state->resetMeridianFlip();
982
983 m_ControlPanel->setTargetPosition(new SkyPoint(RA, DEC));
984 mountTarget->setTargetPosition(new SkyPoint(RA, DEC));
985
986 qCDebug(KSTARS_EKOS_MOUNT) << "Slewing to RA=" <<
987 targetPosition->ra().toHMSString() <<
988 "DEC=" << targetPosition->dec().toDMSString();
989 qCDebug(KSTARS_EKOS_MOUNT) << "Initial HA " << initialHA() << ", flipDelayHrs " << mf_state->getFlipDelayHrs() <<
990 "MFStatus " << MeridianFlipState::meridianFlipStatusString(mf_state->getMeridianFlipMountState());
991
992 // start the slew
993 return(m_Mount->Slew(targetPosition));
994}
995
996
998{
999 if (targetPosition != nullptr)
1000 return *targetPosition;
1001
1002 qCWarning(KSTARS_EKOS_MOUNT) << "No target position defined!";
1003 // since we need to answer something, we take the current mount position
1004 return telescopeCoord;
1005}
1006
1007bool Mount::sync(double RA, double DEC)
1008{
1009 if (m_Mount == nullptr || m_Mount->isConnected() == false)
1010 return false;
1011
1012 return m_Mount->Sync(RA, DEC);
1013}
1014
1016{
1017 if (m_Mount == nullptr)
1018 return false;
1019
1020 return m_Mount->abort();
1021}
1022
1023IPState Mount::slewStatus()
1024{
1025 if (m_Mount == nullptr)
1026 return IPS_ALERT;
1027
1028 return m_Mount->getState("EQUATORIAL_EOD_COORD");
1029}
1030
1031QList<double> Mount::equatorialCoords()
1032{
1033 double ra {0}, dec {0};
1034 QList<double> coords;
1035
1036 if (m_Mount)
1037 m_Mount->getEqCoords(&ra, &dec);
1038 coords.append(ra);
1039 coords.append(dec);
1040
1041 return coords;
1042}
1043
1044QList<double> Mount::horizontalCoords()
1045{
1046 QList<double> coords;
1047
1048 coords.append(telescopeCoord.az().Degrees());
1049 coords.append(telescopeCoord.alt().Degrees());
1050
1051 return coords;
1052}
1053
1054///
1055/// \brief Mount::hourAngle
1056/// \return returns the current mount hour angle in hours in the range -12 to +12
1057///
1058double Mount::hourAngle()
1059{
1060 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst());
1061 dms ha(lst.Degrees() - telescopeCoord.ra().Degrees());
1062 return rangeHA(ha.Hours());
1063}
1064
1065bool Mount::canPark()
1066{
1067 if (m_Mount == nullptr)
1068 return false;
1069
1070 return m_Mount->canPark();
1071}
1072
1074{
1075 if (m_Mount == nullptr || m_Mount->canPark() == false)
1076 return false;
1077
1078 return m_Mount->park();
1079}
1080
1082{
1083 if (m_Mount == nullptr || m_Mount->canPark() == false)
1084 return false;
1085
1086 return m_Mount->unpark();
1087}
1088
1089
1090void Mount::toggleMountToolBox()
1091{
1092 if (m_ControlPanel->isVisible())
1093 {
1094 m_ControlPanel->hide();
1095 QAction *a = KStars::Instance()->actionCollection()->action("show_mount_box");
1096 if (a)
1097 a->setChecked(false);
1098 }
1099 else
1100 {
1101 m_ControlPanel->show();
1102 QAction *a = KStars::Instance()->actionCollection()->action("show_mount_box");
1103 if (a)
1104 a->setChecked(true);
1105 }
1106}
1107
1108//++++ converters for target coordinate display in Mount Control box
1109
1110bool Mount::raDecToAzAlt(QString qsRA, QString qsDec)
1111{
1112 return m_ControlPanel->mountTarget->raDecToAzAlt(qsRA, qsDec);
1113}
1114
1115bool Mount::raDecToHaDec(QString qsRA)
1116{
1117 return m_ControlPanel->mountTarget->raDecToHaDec(qsRA);
1118}
1119
1120bool Mount::azAltToRaDec(QString qsAz, QString qsAlt)
1121{
1122 return m_ControlPanel->mountTarget->azAltToRaDec(qsAz, qsAlt);
1123}
1124
1125bool Mount::azAltToHaDec(QString qsAz, QString qsAlt)
1126{
1127 return m_ControlPanel->mountTarget->azAltToHaDec(qsAz, qsAlt);
1128}
1129
1130bool Mount::haDecToRaDec(QString qsHA)
1131{
1132 return m_ControlPanel->mountTarget->haDecToRaDec(qsHA);
1133}
1134
1135bool Mount::haDecToAzAlt(QString qsHA, QString qsDec)
1136{
1137 return m_ControlPanel->mountTarget->haDecToAzAlt(qsHA, qsDec);
1138}
1139
1140//---- end: converters for target coordinate display in Mount Control box
1141
1142void Mount::centerMount()
1143{
1144 if (m_Mount)
1145 m_Mount->find();
1146}
1147
1149{
1150 if (m_Mount == nullptr)
1151 return false;
1152
1153 if (m_Mount->hasAlignmentModel() == false)
1154 return false;
1155
1156 if (m_Mount->clearAlignmentModel())
1157 {
1158 appendLogText(i18n("Alignment Model cleared."));
1159 return true;
1160 }
1161
1162 appendLogText(i18n("Failed to clear Alignment Model."));
1163 return false;
1164}
1165
1166
1167void Mount::setScopeStatus(ISD::Mount::Status status)
1168{
1169 if (m_Status != status)
1170 {
1171 m_ControlPanel->statusTextObject->setProperty("text", m_Mount->statusString(status));
1172 m_Status = status;
1173 // forward
1174 emit newStatus(status);
1175 }
1176}
1177
1178
1179
1180void Mount::setTrackEnabled(bool enabled)
1181{
1182 if (enabled)
1183 trackOnB->click();
1184 else
1185 trackOffB->click();
1186}
1187
1188int Mount::slewRate()
1189{
1190 if (m_Mount == nullptr)
1191 return -1;
1192
1193 return m_Mount->getSlewRate();
1194}
1195
1196//QJsonArray Mount::getScopes() const
1197//{
1198// QJsonArray scopes;
1199// if (currentTelescope == nullptr)
1200// return scopes;
1201
1202// QJsonObject primary =
1203// {
1204// {"name", "Primary"},
1205// {"mount", currentTelescope->getDeviceName()},
1206// {"aperture", primaryScopeApertureIN->value()},
1207// {"focalLength", primaryScopeFocalIN->value()},
1208// };
1209
1210// scopes.append(primary);
1211
1212// QJsonObject guide =
1213// {
1214// {"name", "Guide"},
1215// {"mount", currentTelescope->getDeviceName()},
1216// {"aperture", primaryScopeApertureIN->value()},
1217// {"focalLength", primaryScopeFocalIN->value()},
1218// };
1219
1220// scopes.append(guide);
1221
1222// return scopes;
1223//}
1224
1225bool Mount::autoParkEnabled()
1226{
1227 return autoParkTimer.isActive();
1228}
1229
1231{
1232 if (enable)
1233 startParkTimer();
1234 else
1235 stopParkTimer();
1236}
1237
1239{
1240 parkEveryDay->setChecked(enabled);
1241}
1242
1244{
1245 autoParkTime->setTime(startup);
1246}
1247
1248bool Mount::meridianFlipEnabled()
1249{
1250 return executeMeridianFlip->isChecked();
1251}
1252
1253double Mount::meridianFlipValue()
1254{
1255 return meridianFlipOffsetDegrees->value();
1256}
1257
1259{
1260 autoParkTimer.stop();
1261 if (m_Mount)
1262 m_Mount->stopTimers();
1263}
1264
1265void Mount::startParkTimer()
1266{
1267 if (m_Mount == nullptr || m_ParkStatus == ISD::PARK_UNKNOWN)
1268 return;
1269
1270 if (m_Mount->isParked())
1271 {
1272 appendLogText(i18n("Mount already parked."));
1273 return;
1274 }
1275
1276 auto parkTime = autoParkTime->time();
1277
1278 qCDebug(KSTARS_EKOS_MOUNT) << "Parking time is" << parkTime.toString();
1279 QDateTime currentDateTime = KStarsData::Instance()->lt();
1280 QDateTime parkDateTime(currentDateTime);
1281
1282 parkDateTime.setTime(parkTime);
1283 qint64 parkMilliSeconds = parkDateTime.msecsTo(currentDateTime);
1284 qCDebug(KSTARS_EKOS_MOUNT) << "Until parking time:" << parkMilliSeconds << "ms or" << parkMilliSeconds / (60 * 60 * 1000)
1285 << "hours";
1286 if (parkMilliSeconds > 0)
1287 {
1288 qCDebug(KSTARS_EKOS_MOUNT) << "Added a day to parking time...";
1289 parkDateTime = parkDateTime.addDays(1);
1290 parkMilliSeconds = parkDateTime.msecsTo(currentDateTime);
1291
1292 int hours = static_cast<int>(parkMilliSeconds / (1000 * 60 * 60));
1293 if (hours > 0)
1294 {
1295 // No need to display warning for every day check
1296 if (parkEveryDay->isChecked() == false)
1297 appendLogText(i18n("Parking time cannot be in the past."));
1298 return;
1299 }
1300 }
1301
1302 parkMilliSeconds = std::abs(parkMilliSeconds);
1303
1304 if (parkMilliSeconds > 24 * 60 * 60 * 1000)
1305 {
1306 appendLogText(i18n("Parking time must be within 24 hours of current time."));
1307 return;
1308 }
1309
1310 if (parkMilliSeconds > 12 * 60 * 60 * 1000)
1311 appendLogText(i18n("Warning! Parking time is more than 12 hours away."));
1312
1313 appendLogText(i18n("Caution: do not use Auto Park while scheduler is active."));
1314
1315 autoParkTimer.setInterval(static_cast<int>(parkMilliSeconds));
1316 autoParkTimer.start();
1317
1318 startTimerB->setEnabled(false);
1319 stopTimerB->setEnabled(true);
1320}
1321
1322void Mount::stopParkTimer()
1323{
1324 autoParkTimer.stop();
1325 countdownLabel->setText("00:00:00");
1326 emit autoParkCountdownUpdated("00:00:00");
1327 stopTimerB->setEnabled(false);
1328 startTimerB->setEnabled(true);
1329}
1330
1331void Mount::startAutoPark()
1332{
1333 appendLogText(i18n("Parking timer is up."));
1334 autoParkTimer.stop();
1335 startTimerB->setEnabled(true);
1336 stopTimerB->setEnabled(false);
1337 countdownLabel->setText("00:00:00");
1338 emit autoParkCountdownUpdated("00:00:00");
1339 if (m_Mount)
1340 {
1341 if (m_Mount->isParked() == false)
1342 {
1343 appendLogText(i18n("Starting auto park..."));
1344 park();
1345 }
1346 }
1347}
1348
1349void Mount::syncAxisReversed(INDI_EQ_AXIS axis, bool reversed)
1350{
1351 if (axis == AXIS_RA)
1352 m_ControlPanel->mountMotion->leftRightCheckObject->setProperty("checked", reversed);
1353 else
1354 m_ControlPanel->mountMotion->upDownCheckObject->setProperty("checked", reversed);
1355}
1356
1357void Mount::setupOpticalTrainManager()
1358{
1359 connect(OpticalTrainManager::Instance(), &OpticalTrainManager::updated, this, &Mount::refreshOpticalTrain);
1360 connect(trainB, &QPushButton::clicked, this, [this]()
1361 {
1362 OpticalTrainManager::Instance()->openEditor(opticalTrainCombo->currentText());
1363 });
1364 connect(opticalTrainCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index)
1365 {
1366 ProfileSettings::Instance()->setOneSetting(ProfileSettings::MountOpticalTrain,
1367 OpticalTrainManager::Instance()->id(opticalTrainCombo->itemText(index)));
1368 refreshOpticalTrain();
1369 emit trainChanged();
1370 });
1371}
1372
1373void Mount::refreshOpticalTrain()
1374{
1375 opticalTrainCombo->blockSignals(true);
1376 opticalTrainCombo->clear();
1377 opticalTrainCombo->addItems(OpticalTrainManager::Instance()->getTrainNames());
1378 trainB->setEnabled(true);
1379
1380 QVariant trainID = ProfileSettings::Instance()->getOneSetting(ProfileSettings::MountOpticalTrain);
1381
1382 if (trainID.isValid())
1383 {
1384 auto id = trainID.toUInt();
1385
1386 // If train not found, select the first one available.
1387 if (OpticalTrainManager::Instance()->exists(id) == false)
1388 {
1389 qCWarning(KSTARS_EKOS_MOUNT) << "Optical train doesn't exist for id" << id;
1390 id = OpticalTrainManager::Instance()->id(opticalTrainCombo->itemText(0));
1391 }
1392
1393 auto name = OpticalTrainManager::Instance()->name(id);
1394
1395 opticalTrainCombo->setCurrentText(name);
1396
1397 auto mount = OpticalTrainManager::Instance()->getMount(name);
1398 setMount(mount);
1399
1400 auto scope = OpticalTrainManager::Instance()->getScope(name);
1401 opticalTrainCombo->setToolTip(scope["name"].toString());
1402
1403 // Load train settings
1404 OpticalTrainSettings::Instance()->setOpticalTrainID(id);
1405 auto settings = OpticalTrainSettings::Instance()->getOneSetting(OpticalTrainSettings::Mount);
1406 if (settings.isValid())
1407 {
1408 auto map = settings.toJsonObject().toVariantMap();
1409 if (map != m_Settings)
1410 {
1411 m_Settings.clear();
1412 setAllSettings(map);
1413 }
1414 }
1415 else
1416 m_Settings = m_GlobalSettings;
1417 }
1418
1419 opticalTrainCombo->blockSignals(false);
1420}
1421
1422///////////////////////////////////////////////////////////////////////////////////////////
1423///
1424///////////////////////////////////////////////////////////////////////////////////////////
1425QVariantMap Mount::getAllSettings() const
1426{
1427 QVariantMap settings;
1428
1429 // All Combo Boxes
1430 for (auto &oneWidget : findChildren<QComboBox*>())
1431 settings.insert(oneWidget->objectName(), oneWidget->currentText());
1432
1433 // All Double Spin Boxes
1434 for (auto &oneWidget : findChildren<QDoubleSpinBox*>())
1435 settings.insert(oneWidget->objectName(), oneWidget->value());
1436
1437 // All Spin Boxes
1438 for (auto &oneWidget : findChildren<QSpinBox*>())
1439 settings.insert(oneWidget->objectName(), oneWidget->value());
1440
1441 // All Checkboxes
1442 for (auto &oneWidget : findChildren<QCheckBox*>())
1443 settings.insert(oneWidget->objectName(), oneWidget->isChecked());
1444
1445 // All Time
1446 for (auto &oneWidget : findChildren<QTimeEdit*>())
1447 settings.insert(oneWidget->objectName(), oneWidget->time().toString());
1448
1449 return settings;
1450}
1451
1452///////////////////////////////////////////////////////////////////////////////////////////
1453///
1454///////////////////////////////////////////////////////////////////////////////////////////
1455void Mount::setAllSettings(const QVariantMap &settings)
1456{
1457 // Disconnect settings that we don't end up calling syncSettings while
1458 // performing the changes.
1459 disconnectSyncSettings();
1460
1461 for (auto &name : settings.keys())
1462 {
1463 // Combo
1464 auto comboBox = findChild<QComboBox*>(name);
1465 if (comboBox)
1466 {
1467 syncControl(settings, name, comboBox);
1468 continue;
1469 }
1470
1471 // Double spinbox
1472 auto doubleSpinBox = findChild<QDoubleSpinBox*>(name);
1473 if (doubleSpinBox)
1474 {
1475 syncControl(settings, name, doubleSpinBox);
1476 continue;
1477 }
1478
1479 // spinbox
1480 auto spinBox = findChild<QSpinBox*>(name);
1481 if (spinBox)
1482 {
1483 syncControl(settings, name, spinBox);
1484 continue;
1485 }
1486
1487 // checkbox
1488 auto checkbox = findChild<QCheckBox*>(name);
1489 if (checkbox)
1490 {
1491 syncControl(settings, name, checkbox);
1492 continue;
1493 }
1494
1495 // timeEdit
1496 auto timeEdit = findChild<QTimeEdit*>(name);
1497 if (timeEdit)
1498 {
1499 syncControl(settings, name, timeEdit);
1500 continue;
1501 }
1502 }
1503
1504 // Sync to options
1505 for (auto &key : settings.keys())
1506 {
1507 auto value = settings[key];
1508 // Save immediately
1509 Options::self()->setProperty(key.toLatin1(), value);
1510 Options::self()->save();
1511
1512 m_Settings[key] = value;
1513 m_GlobalSettings[key] = value;
1514 }
1515
1516 emit settingsUpdated(getAllSettings());
1517
1518 // Save to optical train specific settings as well
1519 OpticalTrainSettings::Instance()->setOpticalTrainID(OpticalTrainManager::Instance()->id(opticalTrainCombo->currentText()));
1520 OpticalTrainSettings::Instance()->setOneSetting(OpticalTrainSettings::Mount, m_Settings);
1521
1522 // Restablish connections
1523 connectSyncSettings();
1524}
1525
1526///////////////////////////////////////////////////////////////////////////////////////////
1527///
1528///////////////////////////////////////////////////////////////////////////////////////////
1529bool Mount::syncControl(const QVariantMap &settings, const QString &key, QWidget * widget)
1530{
1531 QSpinBox *pSB = nullptr;
1532 QDoubleSpinBox *pDSB = nullptr;
1533 QCheckBox *pCB = nullptr;
1534 QComboBox *pComboBox = nullptr;
1535 QTimeEdit *pTimeEdit = nullptr;
1536 QRadioButton *pRadioButton = nullptr;
1537 bool ok = false;
1538
1539 if ((pSB = qobject_cast<QSpinBox *>(widget)))
1540 {
1541 const int value = settings[key].toInt(&ok);
1542 if (ok)
1543 {
1544 pSB->setValue(value);
1545 return true;
1546 }
1547 }
1548 else if ((pDSB = qobject_cast<QDoubleSpinBox *>(widget)))
1549 {
1550 const double value = settings[key].toDouble(&ok);
1551 if (ok)
1552 {
1553 pDSB->setValue(value);
1554 return true;
1555 }
1556 }
1557 else if ((pCB = qobject_cast<QCheckBox *>(widget)))
1558 {
1559 const bool value = settings[key].toBool();
1560 if (value != pCB->isChecked())
1561 pCB->click();
1562 return true;
1563 }
1564 else if ((pRadioButton = qobject_cast<QRadioButton *>(widget)))
1565 {
1566 const bool value = settings[key].toBool();
1567 if (value)
1568 pRadioButton->click();
1569 return true;
1570 }
1571 // ONLY FOR STRINGS, not INDEX
1572 else if ((pComboBox = qobject_cast<QComboBox *>(widget)))
1573 {
1574 const QString value = settings[key].toString();
1575 pComboBox->setCurrentText(value);
1576 return true;
1577 }
1578 else if ((pTimeEdit = qobject_cast<QTimeEdit *>(widget)))
1579 {
1580 const QString value = settings[key].toString();
1581 pTimeEdit->setTime(QTime::fromString(value));
1582 return true;
1583 }
1584
1585 return false;
1586};
1587
1588///////////////////////////////////////////////////////////////////////////////////////////
1589///
1590///////////////////////////////////////////////////////////////////////////////////////////
1591void Mount::syncSettings()
1592{
1593 QDoubleSpinBox *dsb = nullptr;
1594 QSpinBox *sb = nullptr;
1595 QCheckBox *cb = nullptr;
1596 QComboBox *cbox = nullptr;
1597 QTimeEdit *timeEdit = nullptr;
1598 QRadioButton *rb = nullptr;
1599
1600 QString key;
1601 QVariant value;
1602
1603 if ( (dsb = qobject_cast<QDoubleSpinBox*>(sender())))
1604 {
1605 key = dsb->objectName();
1606 value = dsb->value();
1607
1608 }
1609 else if ( (sb = qobject_cast<QSpinBox*>(sender())))
1610 {
1611 key = sb->objectName();
1612 value = sb->value();
1613 }
1614 else if ( (cb = qobject_cast<QCheckBox*>(sender())))
1615 {
1616 key = cb->objectName();
1617 value = cb->isChecked();
1618 }
1619 else if ( (rb = qobject_cast<QRadioButton*>(sender())))
1620 {
1621 key = rb->objectName();
1622 if (rb->isChecked() == false)
1623 {
1624 m_Settings.remove(key);
1625 return;
1626 }
1627 value = true;
1628 }
1629 else if ( (cbox = qobject_cast<QComboBox*>(sender())))
1630 {
1631 key = cbox->objectName();
1632 value = cbox->currentText();
1633 }
1634 else if ( (timeEdit = qobject_cast<QTimeEdit*>(sender())))
1635 {
1636 key = timeEdit->objectName();
1637 value = timeEdit->time().toString();
1638 }
1639
1640 // Save immediately
1641 Options::self()->setProperty(key.toLatin1(), value);
1642 m_Settings[key] = value;
1643 m_GlobalSettings[key] = value;
1644
1645 m_DebounceTimer.start();
1646}
1647
1648///////////////////////////////////////////////////////////////////////////////////////////
1649///
1650///////////////////////////////////////////////////////////////////////////////////////////
1651void Mount::settleSettings()
1652{
1653 Options::self()->save();
1654 emit settingsUpdated(getAllSettings());
1655 // Save to optical train specific settings as well
1656 OpticalTrainSettings::Instance()->setOpticalTrainID(OpticalTrainManager::Instance()->id(opticalTrainCombo->currentText()));
1657 OpticalTrainSettings::Instance()->setOneSetting(OpticalTrainSettings::Mount, m_Settings);
1658}
1659
1660///////////////////////////////////////////////////////////////////////////////////////////
1661///
1662///////////////////////////////////////////////////////////////////////////////////////////
1663void Mount::loadGlobalSettings()
1664{
1665 QString key;
1666 QVariant value;
1667
1668 QVariantMap settings;
1669 // All Combo Boxes
1670 for (auto &oneWidget : findChildren<QComboBox*>())
1671 {
1672 if (oneWidget->objectName() == "opticalTrainCombo")
1673 continue;
1674
1675 key = oneWidget->objectName();
1676 value = Options::self()->property(key.toLatin1());
1677 if (value.isValid() && oneWidget->count() > 0)
1678 {
1679 oneWidget->setCurrentText(value.toString());
1680 settings[key] = value;
1681 }
1682 }
1683
1684 // All Double Spin Boxes
1685 for (auto &oneWidget : findChildren<QDoubleSpinBox*>())
1686 {
1687 key = oneWidget->objectName();
1688 value = Options::self()->property(key.toLatin1());
1689 if (value.isValid())
1690 {
1691 oneWidget->setValue(value.toDouble());
1692 settings[key] = value;
1693 }
1694 }
1695
1696 // All Spin Boxes
1697 for (auto &oneWidget : findChildren<QSpinBox*>())
1698 {
1699 key = oneWidget->objectName();
1700 value = Options::self()->property(key.toLatin1());
1701 if (value.isValid())
1702 {
1703 oneWidget->setValue(value.toInt());
1704 settings[key] = value;
1705 }
1706 }
1707
1708 // All Checkboxes
1709 for (auto &oneWidget : findChildren<QCheckBox*>())
1710 {
1711 key = oneWidget->objectName();
1712 value = Options::self()->property(key.toLatin1());
1713 if (value.isValid())
1714 {
1715 oneWidget->setChecked(value.toBool());
1716 settings[key] = value;
1717 }
1718 }
1719
1720 // initialize meridian flip state machine values
1721 mf_state->setEnabled(Options::executeMeridianFlip());
1722 mf_state->setOffset(Options::meridianFlipOffsetDegrees());
1723
1724 m_GlobalSettings = m_Settings = settings;
1725}
1726
1727///////////////////////////////////////////////////////////////////////////////////////////
1728///
1729///////////////////////////////////////////////////////////////////////////////////////////
1730void Mount::connectSyncSettings()
1731{
1732 // All Combo Boxes
1733 for (auto &oneWidget : findChildren<QComboBox*>())
1734 connect(oneWidget, QOverload<int>::of(&QComboBox::activated), this, &Ekos::Mount::syncSettings);
1735
1736 // All Double Spin Boxes
1737 for (auto &oneWidget : findChildren<QDoubleSpinBox*>())
1738 connect(oneWidget, &QDoubleSpinBox::editingFinished, this, &Ekos::Mount::syncSettings);
1739
1740 // All Spin Boxes
1741 for (auto &oneWidget : findChildren<QSpinBox*>())
1742 connect(oneWidget, &QSpinBox::editingFinished, this, &Ekos::Mount::syncSettings);
1743
1744 // All Checkboxes
1745 for (auto &oneWidget : findChildren<QCheckBox*>())
1746 connect(oneWidget, &QCheckBox::toggled, this, &Ekos::Mount::syncSettings);
1747
1748 // All Radio buttons
1749 for (auto &oneWidget : findChildren<QRadioButton*>())
1750 connect(oneWidget, &QCheckBox::toggled, this, &Ekos::Mount::syncSettings);
1751
1752 // All QDateTimeEdit
1753 for (auto &oneWidget : findChildren<QDateTimeEdit*>())
1754 connect(oneWidget, &QDateTimeEdit::editingFinished, this, &Ekos::Mount::syncSettings);
1755}
1756
1757///////////////////////////////////////////////////////////////////////////////////////////
1758///
1759///////////////////////////////////////////////////////////////////////////////////////////
1760void Mount::disconnectSyncSettings()
1761{
1762 // All Combo Boxes
1763 for (auto &oneWidget : findChildren<QComboBox*>())
1764 disconnect(oneWidget, QOverload<int>::of(&QComboBox::activated), this, &Ekos::Mount::syncSettings);
1765
1766 // All Double Spin Boxes
1767 for (auto &oneWidget : findChildren<QDoubleSpinBox*>())
1768 disconnect(oneWidget, &QDoubleSpinBox::editingFinished, this, &Ekos::Mount::syncSettings);
1769
1770 // All Spin Boxes
1771 for (auto &oneWidget : findChildren<QSpinBox*>())
1772 disconnect(oneWidget, &QSpinBox::editingFinished, this, &Ekos::Mount::syncSettings);
1773
1774 // All Checkboxes
1775 for (auto &oneWidget : findChildren<QCheckBox*>())
1776 disconnect(oneWidget, &QCheckBox::toggled, this, &Ekos::Mount::syncSettings);
1777
1778 // All Radio buttons
1779 for (auto &oneWidget : findChildren<QRadioButton*>())
1780 disconnect(oneWidget, &QCheckBox::toggled, this, &Ekos::Mount::syncSettings);
1781
1782 for (auto &oneWidget : findChildren<QDateTimeEdit*>())
1783 disconnect(oneWidget, &QDateTimeEdit::editingFinished, this, &Ekos::Mount::syncSettings);
1784}
1785
1786///////////////////////////////////////////////////////////////////////////////////////////
1787///
1788///////////////////////////////////////////////////////////////////////////////////////////
1789void Mount::connectSettings()
1790{
1791 connectSyncSettings();
1792
1793 // connections to the meridian flip state machine
1794 connect(executeMeridianFlip, &QCheckBox::toggled, mf_state.get(), &MeridianFlipState::setEnabled);
1795 connect(meridianFlipOffsetDegrees, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
1796 mf_state.get(), &MeridianFlipState::setOffset);
1797 connect(this, &Mount::newParkStatus, mf_state.get(), &MeridianFlipState::setMountParkStatus);
1798 connect(mf_state.get(), &MeridianFlipState::slewTelescope, [&](SkyPoint pos)
1799 {
1800 if (m_Mount)
1801 m_Mount->Slew(&pos, (m_Mount->canFlip() && Options::forcedFlip()));
1802 });
1803
1804 // Train combo box should NOT be synced.
1805 disconnect(opticalTrainCombo, QOverload<int>::of(&QComboBox::activated), this, &Ekos::Mount::syncSettings);
1806}
1807
1808///////////////////////////////////////////////////////////////////////////////////////////
1809///
1810///////////////////////////////////////////////////////////////////////////////////////////
1811void Mount::disconnectSettings()
1812{
1813 disconnectSyncSettings();
1814
1815 // cut connections to the meridian flip state machine
1816 disconnect(executeMeridianFlip, &QCheckBox::toggled, mf_state.get(), &MeridianFlipState::setEnabled);
1817 disconnect(meridianFlipOffsetDegrees, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged),
1818 mf_state.get(), &MeridianFlipState::setOffset);
1819 disconnect(this, &Mount::newParkStatus, mf_state.get(), &MeridianFlipState::setMountParkStatus);
1820 disconnect(mf_state.get(), &MeridianFlipState::slewTelescope, nullptr, nullptr);
1821}
1822
1823///////////////////////////////////////////////////////////////////////////////////////////
1824///
1825///////////////////////////////////////////////////////////////////////////////////////////
1827{
1828 return mf_state->initialPositionHA();
1829}
1830
1831///////////////////////////////////////////////////////////////////////////////////////////
1832///
1833///////////////////////////////////////////////////////////////////////////////////////////
1835{
1836 appendLogText(i18n("Updating master time source to %1.", Options::locationSource()));
1837}
1838
1839///////////////////////////////////////////////////////////////////////////////////////////
1840///
1841///////////////////////////////////////////////////////////////////////////////////////////
1843{
1844 auto name = Options::locationSource();
1845 auto it = std::find_if(m_LocationSources.begin(), m_LocationSources.end(), [name](const auto & oneSource)
1846 {
1847 return oneSource->getDeviceName() == name;
1848 });
1849 if (it != m_LocationSources.end())
1850 {
1851 auto property = (*it)->getProperty("GPS_REFRESH");
1852 if (property)
1853 {
1854 auto sw = property.getSwitch();
1855 sw->at(0)->setState(ISS_ON);
1856 (*it)->sendNewProperty(property);
1857 appendLogText(i18n("Updating master location source to %1. Updating GPS...", name));
1858 }
1859 else
1860 {
1861 property = (*it)->getProperty("GEOGRAPHIC_COORD");
1862 if (property)
1863 {
1864 (*it)->processNumber(property);
1865 appendLogText(i18n("Updating master location source to %1.", name));
1866 }
1867 }
1868 }
1869}
1870}
void newTarget(SkyPoint &currentCoord)
The mount has finished the slew to a new target.
void saveLimits()
saveLimits Saves altitude limit to the user options and updates the INDI telescope driver limits
Definition mount.cpp:817
Q_SCRIPTABLE void setAutoParkDailyEnabled(bool enabled)
setAutoParkDailyEnabled toggles everyday Auto Park
Definition mount.cpp:1238
Q_SCRIPTABLE void setAltitudeLimitsEnabled(bool enable)
DBUS interface function.
Definition mount.cpp:895
void enableHaLimits()
enableHaLimits calls enableHourAngleLimits(true).
Definition mount.cpp:865
Q_INVOKABLE Q_SCRIPTABLE bool park()
DBUS interface function.
Definition mount.cpp:1073
Q_INVOKABLE Q_SCRIPTABLE bool sync(double RA, double DEC)
DBUS interface function.
Definition mount.cpp:1007
bool addLocationSource(const QSharedPointer< ISD::GenericDevice > &device)
addLocationSource Add an INDI driver that can be used for a master location source
Definition mount.cpp:372
void syncLocationSource()
syncLocationSource When location source changes, update all INDI drivers to this location source
Definition mount.cpp:1842
void updateProperty(INDI::Property prop)
updateProperty Update properties under watch in the mount module
Definition mount.cpp:667
void doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs)
Send a guide pulse to the telescope.
Definition mount.cpp:809
Q_INVOKABLE Q_SCRIPTABLE bool resetModel()
DBUS interface function.
Definition mount.cpp:1148
Q_INVOKABLE Q_SCRIPTABLE bool gotoTarget(const QString &target)
DBUS interface function.
Definition mount.cpp:931
void syncTimeSource()
syncTimeSource When time source changes, update all INDI drivers to this time source
Definition mount.cpp:1834
void setTargetName(const QString &name)
setTargetName Set the name of the current target
Definition mount.cpp:949
Q_INVOKABLE void setTrackEnabled(bool enabled)
DBUS interface function.
Definition mount.cpp:1180
void disableHaLimits()
disableAltLimits calls enableHourAngleLimits(false).
Definition mount.cpp:872
Q_SCRIPTABLE double initialHA()
DBUS interface function.
Definition mount.cpp:1826
Q_SCRIPTABLE void setAutoParkStartup(QTime startup)
setAutoParkStartup Set time when automatic parking is activated.
Definition mount.cpp:1243
void paaStageChanged(int stage)
React upon status changes of the polar alignment - mainly to avoid meridian flips happening during po...
Definition mount.cpp:724
void setMeridianFlipValues(bool activate, double degrees)
set meridian flip activation and hours
Definition mount.cpp:718
void syncTelescopeInfo()
syncTelescopeInfo Update telescope information to reflect any property changes
Definition mount.cpp:433
void syncAxisReversed(INDI_EQ_AXIS axis, bool reversed)
syncAxisReversed Update Mount Control GUI on the reverse motion toggled state.
Definition mount.cpp:1349
void motionCommand(int command, int NS, int WE)
move Issues motion command to the mount to move in a particular direction based the request NS and WE...
Definition mount.cpp:790
void registerNewModule(const QString &name)
registerNewModule Register an Ekos module as it arrives via DBus and create the appropriate DBus inte...
Definition mount.cpp:510
Q_INVOKABLE Q_SCRIPTABLE bool unpark()
DBUS interface function.
Definition mount.cpp:1081
double hourAngle
Mount::hourAngle.
Definition mount.h:47
bool addTimeSource(const QSharedPointer< ISD::GenericDevice > &device)
addTimeSource Add an INDI driver that can be used for a master time source
Definition mount.cpp:329
void newTargetName(const QString &name)
The mount has finished the slew to a new target.
Q_INVOKABLE Q_SCRIPTABLE bool syncTarget(const QString &target)
DBUS interface function.
Definition mount.cpp:955
void stopTimers()
stopTimers Need to stop update timers when profile is disconnected but due to timing and race conditi...
Definition mount.cpp:1258
Q_SCRIPTABLE Q_NOREPLY void setHourAngleLimit(double limit)
DBUS interface function.
Definition mount.cpp:910
void newStatus(ISD::Mount::Status status)
Change in the mount status.
Q_INVOKABLE Q_SCRIPTABLE bool abort()
DBUS interface function.
Definition mount.cpp:1015
Q_SCRIPTABLE SkyPoint currentTarget()
DBUS interface function.
Definition mount.cpp:997
void updateLog(int messageID)
updateLog Update mount module log to include any messages arriving for the telescope driver
Definition mount.cpp:773
bool setMount(ISD::Mount *device)
addMount Add a new Mount device
Definition mount.cpp:227
QSharedPointer< MeridianFlipState > getMeridianFlipState() const
getMeridianFlipState
Definition mount.h:138
Q_SCRIPTABLE Q_NOREPLY void setAltitudeLimits(QList< double > limits)
DBUS interface function.
Definition mount.cpp:889
Q_SCRIPTABLE void setHourAngleLimitEnabled(bool enable)
DBUS interface function.
Definition mount.cpp:915
void enableHourAngleLimits(bool enable)
enableHourAngleLimits Enable or disable hour angle limits
Definition mount.cpp:859
void suspendAltLimits()
suspendAltLimits calls enableAltitudeLimits(false).
Definition mount.cpp:853
void updateTelescopeCoords(const SkyPoint &position, ISD::Mount::PierSide pierSide, const dms &ha)
updateTelescopeCoords is triggered by the ISD::Mount::newCoord() event and updates the displayed coor...
Definition mount.cpp:519
Q_INVOKABLE Q_SCRIPTABLE bool slew(double RA, double DEC)
DBUS interface function.
Definition mount.cpp:968
void newCoords(const SkyPoint &position, ISD::Mount::PierSide pierSide, const dms &ha)
Update event with the current telescope position.
void resumeAltLimits()
resumeAltLimits calls enableAltitudeLimits(true).
Definition mount.cpp:845
Q_SCRIPTABLE void setAutoParkEnabled(bool enable)
setAutoParkEnabled Toggle Auto Park
Definition mount.cpp:1230
INDI::PropertyView< ISwitch > * getSwitch(const QString &name) const
device handle controlling Mounts.
Definition indimount.h:29
void stopTimers()
stopTimers Stop timers to prevent timing race condition when device is unavailable and timer is still...
void newTarget(SkyPoint &currentCoords)
The mount has finished the slew to a new target.
void newCoords(const SkyPoint &position, const PierSide pierside, const dms &ha)
Update event with the current telescope position.
void newTargetName(const QString &name)
The mount has finished the slew to a new target.
Q_INVOKABLE QAction * action(const QString &name) const
static void beep(const QString &reason=QString())
const KStarsDateTime & lt() const
Definition kstarsdata.h:153
GeoLocation * geo()
Definition kstarsdata.h:232
SkyMapComposite * skyComposite()
Definition kstarsdata.h:168
static KStars * Instance()
Definition kstars.h:121
virtual KActionCollection * actionCollection() const
SkyObject * findByName(const QString &name, bool exact=true) override
Search the children of this SkyMapComposite for a SkyObject whose name matches the argument.
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
const CachingDms & dec() const
Definition skypoint.h:269
const CachingDms & ra() const
Definition skypoint.h:263
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
const double & Degrees() const
Definition dms.h:141
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:83
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)
ButtonCode questionTwoActions(QWidget *parent, const QString &text, const QString &title, const KGuiItem &primaryAction, const KGuiItem &secondaryAction, const QString &dontAskAgainName=QString(), Options options=Notify)
QString name(StandardAction id)
bool isChecked() const const
void clicked(bool checked)
void toggled(bool checked)
void editingFinished()
void setChecked(bool)
void activated(int index)
void currentIndexChanged(int index)
void setCurrentText(const QString &text)
void setTime(QTime time)
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
void setValue(double val)
void valueChanged(double d)
void append(QList< T > &&value)
iterator begin()
void clear()
iterator end()
iterator erase(const_iterator begin, const_iterator end)
iterator insert(const_iterator before, parameter_type value)
T value(qsizetype i) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T findChild(const QString &name, Qt::FindChildOptions options) const const
QList< T > findChildren(Qt::FindChildOptions options) const const
QVariant property(const char *name) const const
T qobject_cast(QObject *object)
QObject * sender() const const
T * get() const const
void setValue(int val)
QString number(double n, char format, int precision)
QByteArray toLatin1() const const
UniqueConnection
QTextStream & dec(QTextStream &stream)
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QTime addMSecs(int ms) const const
QTime fromString(QStringView string, QStringView format)
QString toString(QStringView format) const const
void setInterval(int msec)
bool isActive() const const
void setSingleShot(bool singleShot)
void start()
void stop()
void timeout()
bool isValid() const const
bool toBool() const const
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
QString toString() const const
uint toUInt(bool *ok) const const
void setEnabled(bool)
void setupUi(QWidget *widget)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 13 2024 11:47:11 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.