Kstars

opticaltrainmanager.cpp
1/*
2 SPDX-FileCopyrightText: 2022 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "opticaltrainmanager.h"
8#include <kstars_debug.h>
9
10#include "ksnotification.h"
11#include "kstarsdata.h"
12#include "Options.h"
13#include "kstars.h"
14#include "indi/indilistener.h"
15#include "ekos/auxiliary/profilesettings.h"
16#include "oal/equipmentwriter.h"
17
18#include <QTimer>
19#include <QSqlTableModel>
20#include <QSqlDatabase>
21#include <QSqlRecord>
22
23#include <basedevice.h>
24
25#include <algorithm>
26
27namespace Ekos
28{
29
30OpticalTrainManager *OpticalTrainManager::m_Instance = nullptr;
31
32////////////////////////////////////////////////////////////////////////////
33///
34////////////////////////////////////////////////////////////////////////////
35OpticalTrainManager *OpticalTrainManager::Instance()
36{
37 if (m_Instance == nullptr)
38 m_Instance = new OpticalTrainManager();
39
40 return m_Instance;
41}
42
43////////////////////////////////////////////////////////////////////////////
44///
45////////////////////////////////////////////////////////////////////////////
46void OpticalTrainManager::release()
47{
48 delete(m_Instance);
49 m_Instance = nullptr;
50}
51
52////////////////////////////////////////////////////////////////////////////
53///
54////////////////////////////////////////////////////////////////////////////
55OpticalTrainManager::OpticalTrainManager() : QDialog(KStars::Instance())
56{
57#ifdef Q_OS_MACOS
58 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
59#endif
60
61 setupUi(this);
62
63 connect(this, &QDialog::finished, this, [this]()
64 {
65 emit configurationRequested(false);
66 });
67
68 // Mount Combo
69 connect(mountComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
70 [this]()
71 {
72 updateOpticalTrainValue(mountComboBox, "mount");
73 });
74
75 // DustCap Combo
76 connect(dustCapComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
77 [this]()
78 {
79 updateOpticalTrainValue(dustCapComboBox, "dustcap");
80 });
81
82 // Light Box
83 connect(lightBoxComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
84 [this]()
85 {
86 updateOpticalTrainValue(lightBoxComboBox, "lightbox");
87 });
88
89 // Scope / Lens
90 connect(scopeComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
91 [this]()
92 {
93 updateOpticalTrainValue(scopeComboBox, "scope");
94 });
95
96 // Reducer
97 connect(reducerSpinBox, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
98 [this](double value)
99 {
100 updateOpticalTrainValue(value, "reducer");
101 });
102
103 // Rotator
104 connect(rotatorComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
105 [this]()
106 {
107 updateOpticalTrainValue(rotatorComboBox, "rotator");
108 });
109
110 // Focuser
111 connect(focusComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
112 [this]()
113 {
114 updateOpticalTrainValue(focusComboBox, "focuser");
115 });
116
117 // Filter Wheel
118 connect(filterComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
119 [this]()
120 {
121 updateOpticalTrainValue(filterComboBox, "filterwheel");
122 });
123
124 // Camera
125 connect(cameraComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
126 [this]()
127 {
128 updateOpticalTrainValue(cameraComboBox, "camera");
129 });
130
131 // Guider
132 connect(guiderComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
133 [this]()
134 {
135 updateOpticalTrainValue(guiderComboBox, "guider");
136 });
137
138 connect(addB, &QPushButton::clicked, this, [this]()
139 {
140 addOpticalTrain(m_TrainNames.count(), i18n("New Train"));
141 m_OpticalTrainsModel->select();
142 refreshModel();
143 trainNamesList->setCurrentRow(trainNamesList->count() - 1);
144 selectOpticalTrain(trainNamesList->currentItem());
145 });
146
147 connect(removeB, &QPushButton::clicked, this, [this]()
148 {
149 if (trainNamesList->currentItem() != nullptr)
150 {
151 removeOpticalTrain(trainNamesList->currentItem()->text());
152 removeB->setEnabled(false);
153 }
154 });
155
156 connect(resetB, &QPushButton::clicked, this, &OpticalTrainManager::reset);
157
158 connect(opticalElementsB, &QPushButton::clicked, this, [this]()
159 {
160 QScopedPointer<EquipmentWriter> writer(new EquipmentWriter());
161 writer->loadEquipment();
162 writer->exec();
163 refreshOpticalElements();
164 });
165
166 connect(trainNamesList, &QListWidget::itemClicked, this, [this](QListWidgetItem * item)
167 {
168 selectOpticalTrain(item);
169 });
170 connect(trainNamesList, &QListWidget::itemChanged, this, [this](QListWidgetItem * item)
171 {
172 renameCurrentOpticalTrain(item->text());
173 });
174 connect(trainNamesList, &QListWidget::currentRowChanged, this, [this](int row)
175 {
176 if (row >= 0)
177 selectOpticalTrain(trainNamesList->currentItem());
178 });
179
180 m_CheckMissingDevicesTimer.setInterval(5000);
181 m_CheckMissingDevicesTimer.setSingleShot(true);
182 connect(&m_CheckMissingDevicesTimer, &QTimer::timeout, this, &OpticalTrainManager::checkMissingDevices);
183
184 m_DelegateTimer.setInterval(1000);
185 m_DelegateTimer.setSingleShot(true);
186 connect(&m_DelegateTimer, &QTimer::timeout, this, [this]()
187 {
188 if (m_Profile)
189 setProfile(m_Profile);
190 });
191
192 initModel();
193}
194
195////////////////////////////////////////////////////////////////////////////
196///
197////////////////////////////////////////////////////////////////////////////
198void OpticalTrainManager::initModel()
199{
200 auto userdb = QSqlDatabase::database(KStarsData::Instance()->userdb()->connectionName());
201 m_OpticalTrainsModel = new QSqlTableModel(this, userdb);
202 connect(m_OpticalTrainsModel, &QSqlTableModel::dataChanged, this, [this]()
203 {
204 m_OpticalTrains.clear();
205 for (int i = 0; i < m_OpticalTrainsModel->rowCount(); ++i)
206 {
207 QVariantMap recordMap;
208 QSqlRecord record = m_OpticalTrainsModel->record(i);
209 for (int j = 0; j < record.count(); j++)
210 recordMap[record.fieldName(j)] = record.value(j);
211
212 m_OpticalTrains.append(recordMap);
213 }
214
215 m_TrainNames.clear();
216 for (auto &oneTrain : m_OpticalTrains)
217 m_TrainNames << oneTrain["name"].toString();
218
219 trainNamesList->clear();
220 trainNamesList->addItems(m_TrainNames);
221 trainNamesList->setEditTriggers(QAbstractItemView::AllEditTriggers);
222 emit updated();
223 });
224}
225
226void OpticalTrainManager::syncDevices()
227{
228 syncDelegatesToDevices();
229 if (m_Profile)
230 {
231 refreshModel();
232 emit updated();
233 }
234}
235
236////////////////////////////////////////////////////////////////////////////
237///
238////////////////////////////////////////////////////////////////////////////
239void OpticalTrainManager::refreshModel()
240{
241 KStars::Instance()->data()->userdb()->GetOpticalTrains(m_Profile->id, m_OpticalTrains);
242 m_TrainNames.clear();
243 for (auto &oneTrain : m_OpticalTrains)
244 m_TrainNames << oneTrain["name"].toString();
245
246 trainNamesList->clear();
247 trainNamesList->addItems(m_TrainNames);
248}
249
250////////////////////////////////////////////////////////////////////////////
251///
252////////////////////////////////////////////////////////////////////////////
253void OpticalTrainManager::syncActiveDevices()
254{
255 for (auto &oneTrain : m_OpticalTrains)
256 {
257 auto train = oneTrain["name"].toString();
259
260 if (getGenericDevice(train, Mount, device))
261 syncActiveProperties(oneTrain, device);
262 if (getGenericDevice(train, Camera, device))
263 syncActiveProperties(oneTrain, device);
264 if (getGenericDevice(train, GuideVia, device))
265 syncActiveProperties(oneTrain, device);
266 if (getGenericDevice(train, Focuser, device))
267 syncActiveProperties(oneTrain, device);
268 if (getGenericDevice(train, FilterWheel, device))
269 syncActiveProperties(oneTrain, device);
270 if (getGenericDevice(train, Rotator, device))
271 syncActiveProperties(oneTrain, device);
272 if (getGenericDevice(train, DustCap, device))
273 syncActiveProperties(oneTrain, device);
274 if (getGenericDevice(train, LightBox, device))
275 syncActiveProperties(oneTrain, device);
276 }
277
278 // For non-train specific devices, we only sync them with primary train
280 if (!m_OpticalTrains.isEmpty())
281 {
282 auto name = m_OpticalTrains[0]["name"].toString();
283 if (getGenericDevice(name, Dome, device))
284 syncActiveProperties(m_OpticalTrains[0], device);
285 if (getGenericDevice(name, Weather, device))
286 syncActiveProperties(m_OpticalTrains[0], device);
287 if (getGenericDevice(name, GPS, device))
288 syncActiveProperties(m_OpticalTrains[0], device);
289 }
290}
291
292////////////////////////////////////////////////////////////////////////////
293///
294////////////////////////////////////////////////////////////////////////////
295void OpticalTrainManager::syncActiveProperties(const QVariantMap &train, const QSharedPointer<ISD::GenericDevice> &device)
296{
297 auto tvp = device->getProperty("ACTIVE_DEVICES");
298 if (!tvp)
299 return;
300
301 auto name = train["name"].toString();
302
303 for (auto &it : *tvp.getText())
304 {
306 QString elementText = it.getText();
307 if (it.isNameMatch("ACTIVE_TELESCOPE"))
308 {
309 auto activeDevice = train["mount"].toString();
310 if (activeDevice == "--")
311 elementText.clear();
312 else if (activeDevice != elementText)
313 {
315 if (getGenericDevice(name, Mount, genericDevice))
316 devs.append(genericDevice);
317 }
318 }
319 else if (it.isNameMatch("ACTIVE_DOME"))
320 {
321 devs = INDIListener::devicesByInterface(INDI::BaseDevice::DOME_INTERFACE);
322 }
323 else if (it.isNameMatch("ACTIVE_GPS"))
324 {
325 devs = INDIListener::devicesByInterface(INDI::BaseDevice::GPS_INTERFACE);
326 // If GPS device is neither the location nor time source, then it should be removed
327 // so that INDI devices are NOT synced to it. We only explicitly sync to GPS if it is specified
328 // as either the location or time source
329 devs.erase(std::remove_if(devs.begin(), devs.end(), [](const auto & oneDevice)
330 {
331 return oneDevice->getDeviceName() != Options::locationSource() && oneDevice->getDeviceName() != Options::timeSource();
332 }), devs.end());
333
334 if (devs.isEmpty())
335 elementText.clear();
336 }
337 else if (it.isNameMatch("ACTIVE_ROTATOR"))
338 {
339 auto activeDevice = train["rotator"].toString();
340 if (activeDevice == "--")
341 elementText.clear();
342 else if (activeDevice != elementText)
343 {
345 if (getGenericDevice(name, Rotator, genericDevice))
346 devs.append(genericDevice);
347 }
348 }
349 else if (it.isNameMatch("ACTIVE_FOCUSER"))
350 {
351 auto activeDevice = train["focuser"].toString();
352 if (activeDevice == "--")
353 elementText.clear();
354 else if (activeDevice != elementText)
355 {
357 if (getGenericDevice(name, Focuser, genericDevice))
358 devs.append(genericDevice);
359 }
360 }
361 else if (it.isNameMatch("ACTIVE_FILTER"))
362 {
363 auto activeDevice = train["filterwheel"].toString();
364 if (activeDevice == "--")
365 elementText.clear();
366 else if (activeDevice != elementText)
367 {
369 if (getGenericDevice(name, FilterWheel, genericDevice))
370 devs.append(genericDevice);
371 }
372 }
373
374 if (!devs.empty())
375 {
376 if (it.getText() != devs.first()->getDeviceName())
377 {
378 it.setText(devs.first()->getDeviceName().toLatin1().constData());
379 device->sendNewProperty(tvp.getText());
380 }
381 }
382 // Clear element if required
383 else if (elementText.isEmpty() && !QString(it.getText()).isEmpty())
384 {
385 it.setText("");
386 device->sendNewProperty(tvp.getText());
387 }
388 }
389
390}
391
392////////////////////////////////////////////////////////////////////////////
393///
394////////////////////////////////////////////////////////////////////////////
395void OpticalTrainManager::setProfile(const QSharedPointer<ProfileInfo> &profile)
396{
397 m_DelegateTimer.stop();
398
399 // Load optical train model
400 if (m_Profile != profile)
401 {
402 m_Profile = profile;
403 refreshModel();
404 }
405
406 // Are we still updating delegates? If yes, return.
407 if (syncDelegatesToDevices())
408 {
409 m_CheckMissingDevicesTimer.start();
410
411 // Start delegate timer to ensure no more changes are pending.
412 m_DelegateTimer.start();
413
414 syncActiveDevices();
415 }
416 else
417 {
418 checkOpticalTrains();
419 }
420}
421
422////////////////////////////////////////////////////////////////////////////
423///
424////////////////////////////////////////////////////////////////////////////
425void OpticalTrainManager::checkOpticalTrains()
426{
427 if (m_OpticalTrains.empty())
428 {
429 generateOpticalTrains();
430 refreshModel();
431 if (!m_OpticalTrains.empty())
432 {
433 auto primaryTrainID = m_OpticalTrains[0]["id"].toUInt();
434 ProfileSettings::Instance()->setOneSetting(ProfileSettings::PrimaryOpticalTrain, primaryTrainID);
435 ProfileSettings::Instance()->setOneSetting(ProfileSettings::CaptureOpticalTrain, primaryTrainID);
436 ProfileSettings::Instance()->setOneSetting(ProfileSettings::FocusOpticalTrain, primaryTrainID);
437 ProfileSettings::Instance()->setOneSetting(ProfileSettings::MountOpticalTrain, primaryTrainID);
438 ProfileSettings::Instance()->setOneSetting(ProfileSettings::AlignOpticalTrain, primaryTrainID);
439 ProfileSettings::Instance()->setOneSetting(ProfileSettings::DarkLibraryOpticalTrain, primaryTrainID);
440 if (m_OpticalTrains.count() > 1)
441 ProfileSettings::Instance()->setOneSetting(ProfileSettings::GuideOpticalTrain, m_OpticalTrains[1]["id"].toInt());
442 else
443 ProfileSettings::Instance()->setOneSetting(ProfileSettings::GuideOpticalTrain, primaryTrainID);
444 }
445
446 emit updated();
447 show();
448 raise();
449 emit configurationRequested(true);
450 }
451 else
452 {
453 m_CheckMissingDevicesTimer.start();
454 emit updated();
455 }
456}
457////////////////////////////////////////////////////////////////////////////
458/// This method tries to guess possible optical train configuration
459////////////////////////////////////////////////////////////////////////////
460void OpticalTrainManager::generateOpticalTrains()
461{
462 // We should have primary train
463 addOpticalTrain(0, i18n("Primary"));
464 // Check if need secondary train
465 if (cameraComboBox->count() > 2)
466 addOpticalTrain(1, i18n("Secondary"));
467 // Check if need tertiary train
468 if (cameraComboBox->count() > 3)
469 addOpticalTrain(2, i18n("Tertiary"));
470}
471
472////////////////////////////////////////////////////////////////////////////
473///
474////////////////////////////////////////////////////////////////////////////
475QString OpticalTrainManager::addOpticalTrain(uint8_t index, const QString &name)
476{
477 QVariantMap train;
478 train["profile"] = m_Profile->id;
479 train["name"] = uniqueTrainName(name);
480
481 train["mount"] = mountComboBox->itemText(mountComboBox->count() - 1);
482 train["dustcap"] = dustCapComboBox->itemText(dustCapComboBox->count() - 1);
483 train["lightbox"] = lightBoxComboBox->itemText(lightBoxComboBox->count() - 1);
484 train["reducer"] = 1.0;
485 train["rotator"] = rotatorComboBox->itemText(rotatorComboBox->count() - 1);
486 train["focuser"] = focusComboBox->itemText(focusComboBox->count() - 1);
487 train["filterwheel"] = filterComboBox->itemText(filterComboBox->count() - 1);
488 train["guider"] = guiderComboBox->itemText(guiderComboBox->count() - 1);
489
490 QJsonObject opticalElement;
491 if (KStars::Instance()->data()->userdb()->getLastOpticalElement(opticalElement))
492 train["scope"] = opticalElement["name"].toString();
493
494 train["camera"] = "--";
495 // Primary train
496 if (index == 0 && cameraComboBox->count() > 1)
497 train["camera"] = cameraComboBox->itemText(1);
498 // Any other trains
499 else if (index > 0)
500 {
501 // For 2nd train and beyond, we get the N camera appropiate for this train if one exist.
502 // We add + 1 because first element in combobox is "--"
503 auto cameraIndex = index + 1;
504 if (cameraComboBox->count() >= cameraIndex)
505 train["camera"] = cameraComboBox->itemText(cameraIndex);
506 }
507
508 KStarsData::Instance()->userdb()->AddOpticalTrain(train);
509 return train["name"].toString();
510}
511
512////////////////////////////////////////////////////////////////////////////
513///
514////////////////////////////////////////////////////////////////////////////
515void OpticalTrainManager::addOpticalTrain(const QJsonObject &value)
516{
517 auto newTrain = value.toVariantMap();
518 newTrain["profile"] = m_Profile->id;
519 KStarsData::Instance()->userdb()->AddOpticalTrain(newTrain);
520
521 refreshTrains();
522}
523
524////////////////////////////////////////////////////////////////////////////
525///
526////////////////////////////////////////////////////////////////////////////
527bool OpticalTrainManager::setOpticalTrainValue(const QString &name, const QString &field, const QVariant &value)
528{
529 for (auto &oneTrain : m_OpticalTrains)
530 {
531 if (oneTrain["name"].toString() == name)
532 {
533 // If value did not change, just return true
534 if (oneTrain[field] == value)
535 return true;
536
537 // Update field and database.
538 oneTrain[field] = value;
539 KStarsData::Instance()->userdb()->UpdateOpticalTrain(oneTrain, oneTrain["id"].toInt());
540 syncActiveDevices();
541 emit updated();
542 return true;
543 }
544 }
545 return false;
546}
547
548////////////////////////////////////////////////////////////////////////////
549///
550////////////////////////////////////////////////////////////////////////////
551void OpticalTrainManager::renameCurrentOpticalTrain(const QString &name)
552{
553 if (m_CurrentOpticalTrain != nullptr && (*m_CurrentOpticalTrain)["name"] != name)
554 {
555 auto pos = trainNamesList->currentRow();
556 // ensure train name uniqueness
557 auto unique = uniqueTrainName(name);
558 // update the train database entry
559 setOpticalTrainValue((*m_CurrentOpticalTrain)["name"].toString(), "name", unique);
560 // propagate the unique name to the current selection
561 trainNamesList->currentItem()->setText(unique);
562 // refresh the trains
563 refreshTrains();
564 // refresh selection
565 selectOpticalTrain(unique);
566 trainNamesList->setCurrentRow(pos);
567 }
568}
569
570////////////////////////////////////////////////////////////////////////////
571///
572////////////////////////////////////////////////////////////////////////////
573bool OpticalTrainManager::setOpticalTrain(const QJsonObject &train)
574{
575 auto oneOpticalTrain = getOpticalTrain(train["id"].toInt());
576 if (!oneOpticalTrain.empty())
577 {
578 KStarsData::Instance()->userdb()->UpdateOpticalTrain(train.toVariantMap(), oneOpticalTrain["id"].toInt());
579 refreshTrains();
580 return true;
581 }
582 return false;
583}
584
585////////////////////////////////////////////////////////////////////////////
586///
587////////////////////////////////////////////////////////////////////////////
588bool OpticalTrainManager::removeOpticalTrain(const QString &name)
589{
590 for (auto &oneTrain : m_OpticalTrains)
591 {
592 if (oneTrain["name"].toString() == name)
593 {
594 auto id = oneTrain["id"].toInt();
595 KStarsData::Instance()->userdb()->DeleteOpticalTrain(id);
596 KStarsData::Instance()->userdb()->DeleteOpticalTrainSettings(id);
597 refreshTrains();
598 selectOpticalTrain(nullptr);
599 return true;
600 }
601 }
602
603 return false;
604}
605
606////////////////////////////////////////////////////////////////////////////
607///
608////////////////////////////////////////////////////////////////////////////
609bool OpticalTrainManager::syncDelegatesToDevices()
610{
611 auto changed = false;
612 // Must block signals otherwise all the combo boxes will fire signals that should not be processed.
613 for (auto &oneWidget : findChildren<QComboBox*>())
614 oneWidget->blockSignals(true);
615
616 // Mounts
617 auto mounts = INDIListener::devicesByInterface(INDI::BaseDevice::TELESCOPE_INTERFACE);
618 QStringList values;
619 for (auto &oneMount : mounts)
620 values << oneMount->getDeviceName();
621 changed |= !values.empty() && values != m_MountNames;
622 m_MountNames = values;
623 auto currentMount = mountComboBox->currentText();
624 mountComboBox->clear();
625 mountComboBox->addItems(QStringList() << "--" << values);
626 mountComboBox->setCurrentText(currentMount);
627
628 // Dust Caps
629 values.clear();
630 auto dustcaps = INDIListener::devicesByInterface(INDI::BaseDevice::DUSTCAP_INTERFACE);
631 for (auto &oneCap : dustcaps)
632 values << oneCap->getDeviceName();
633 changed |= !values.empty() && values != m_DustCapNames;
634 m_DustCapNames = values;
635 auto currentCap = dustCapComboBox->currentText();
636 dustCapComboBox->clear();
637 dustCapComboBox->addItems(QStringList() << "--" << values);
638 dustCapComboBox->setCurrentText(currentCap);
639
640 // Light Boxes
641 values.clear();
642 auto lightboxes = INDIListener::devicesByInterface(INDI::BaseDevice::LIGHTBOX_INTERFACE);
643 for (auto &oneBox : lightboxes)
644 values << oneBox->getDeviceName();
645 changed |= !values.empty() && values != m_LightBoxNames;
646 auto currentLightBox = lightBoxComboBox->currentText();
647 m_LightBoxNames = values;
648 lightBoxComboBox->clear();
649 lightBoxComboBox->addItems(QStringList() << "--" << values);
650 lightBoxComboBox->setCurrentText(currentLightBox);
651
652 // Scopes
653 values = KStars::Instance()->data()->userdb()->getOpticalElementNames();
654 changed |= !values.empty() && values != m_ScopeNames;
655 m_ScopeNames = values;
656 auto currentScope = scopeComboBox->currentText();
657 scopeComboBox->clear();
658 scopeComboBox->addItems(QStringList() << "--" << values);
659 scopeComboBox->setCurrentText(currentScope);
660
661 // Rotators
662 values.clear();
663 auto rotators = INDIListener::devicesByInterface(INDI::BaseDevice::ROTATOR_INTERFACE);
664 for (auto &oneRotator : rotators)
665 values << oneRotator->getDeviceName();
666 changed |= !values.empty() && values != m_RotatorNames;
667 m_RotatorNames = values;
668 auto currentRotator = rotatorComboBox->currentText();
669 rotatorComboBox->clear();
670 rotatorComboBox->addItems(QStringList() << "--" << values);
671 rotatorComboBox->setCurrentText(currentRotator);
672
673 // Focusers
674 values.clear();
675 auto focusers = INDIListener::devicesByInterface(INDI::BaseDevice::FOCUSER_INTERFACE);
676 for (auto &oneFocuser : focusers)
677 values << oneFocuser->getDeviceName();
678 changed |= !values.empty() && values != m_FocuserNames;
679 m_FocuserNames = values;
680 auto currentFocuser = focusComboBox->currentText();
681 focusComboBox->clear();
682 focusComboBox->addItems(QStringList() << "--" << values);
683 focusComboBox->setCurrentText(currentFocuser);
684
685 // Filter Wheels
686 values.clear();
687 auto filterwheels = INDIListener::devicesByInterface(INDI::BaseDevice::FILTER_INTERFACE);
688 for (auto &oneFilterWheel : filterwheels)
689 values << oneFilterWheel->getDeviceName();
690 changed |= !values.empty() && values != m_FilterWheelNames;
691 m_FilterWheelNames = values;
692 auto currentFilter = filterComboBox->currentText();
693 filterComboBox->clear();
694 filterComboBox->addItems(QStringList() << "--" << values);
695 filterComboBox->setCurrentText(currentFilter);
696
697 // Cameras
698 values.clear();
699 auto cameras = INDIListener::devicesByInterface(INDI::BaseDevice::CCD_INTERFACE);
700 for (auto &oneCamera : cameras)
701 values << oneCamera->getDeviceName();
702 changed |= !values.empty() && values != m_CameraNames;
703 m_CameraNames = values;
704 auto currentCamera = cameraComboBox->currentText();
705 cameraComboBox->clear();
706 cameraComboBox->addItems(QStringList() << "--" << values);
707 cameraComboBox->setCurrentText(currentCamera);
708
709 // Guiders
710 values.clear();
711 auto guiders = INDIListener::devicesByInterface(INDI::BaseDevice::GUIDER_INTERFACE);
712 for (auto &oneGuider : guiders)
713 values << oneGuider->getDeviceName();
714 changed |= !values.empty() && values != m_GuiderNames;
715 m_GuiderNames = values;
716 auto currentGuider = guiderComboBox->currentText();
717 guiderComboBox->clear();
718 guiderComboBox->addItems(QStringList() << "--" << values);
719 guiderComboBox->setCurrentText(currentGuider);
720
721 // Restore all signals
722 for (auto &oneWidget : findChildren<QComboBox*>())
723 oneWidget->blockSignals(false);
724
725 return changed;
726}
727
728////////////////////////////////////////////////////////////////////////////
729///
730////////////////////////////////////////////////////////////////////////////
731QString OpticalTrainManager::uniqueTrainName(QString name)
732{
733 QString result = name;
734 int nr = 1;
735 while (m_TrainNames.contains(result))
736 result = QString("%1 (%2)").arg(name).arg(nr++);
737
738 return result;
739}
740
741////////////////////////////////////////////////////////////////////////////
742///
743////////////////////////////////////////////////////////////////////////////
744bool OpticalTrainManager::selectOpticalTrain(QListWidgetItem *item)
745{
746 if (item != nullptr && selectOpticalTrain(item->text()))
747 {
748 item->setFlags(item->flags() | Qt::ItemIsEditable);
749 return true;
750 }
751 return false;
752}
753
754////////////////////////////////////////////////////////////////////////////
755///
756////////////////////////////////////////////////////////////////////////////
757QString OpticalTrainManager::findTrainContainingDevice(const QString &name, Role role)
758{
759 for (auto &oneTrain : m_OpticalTrains)
760 {
761 auto train = oneTrain["name"].toString();
762
763 switch (role)
764 {
765 case Mount:
766 if (oneTrain["mount"].toString() == name)
767 return train;
768 break;
769 case Camera:
770 if (oneTrain["camera"].toString() == name)
771 return train;
772 break;
773 case Rotator:
774 if (oneTrain["rotator"].toString() == name)
775 return train;
776 break;
777 case GuideVia:
778 if (oneTrain["guider"].toString() == name)
779 return train;
780 break;
781 case DustCap:
782 if (oneTrain["dustcap"].toString() == name)
783 return train;
784 break;
785 case Scope:
786 if (oneTrain["scope"].toString() == name)
787 return train;
788 break;
789 case FilterWheel:
790 if (oneTrain["filterwheel"].toString() == name)
791 return train;
792 break;
793 case Focuser:
794 if (oneTrain["focuser"].toString() == name)
795 return train;
796 break;
797 case Reducer:
798 if (oneTrain["reducer"].toString() == name)
799 return train;
800 break;
801 case LightBox:
802 if (oneTrain["lightbox"].toString() == name)
803 return train;
804 break;
805 case Dome:
806 case Weather:
807 case GPS:
808 // for those not part of an image train: do nothing
809 break;
810 }
811
812 }
813
814 return QString();
815}
816
817////////////////////////////////////////////////////////////////////////////
818///
819////////////////////////////////////////////////////////////////////////////
820bool OpticalTrainManager::selectOpticalTrain(const QString &name)
821{
822 for (auto &oneTrain : m_OpticalTrains)
823 {
824 if (oneTrain["name"].toString() == name)
825 {
826 m_Persistent = false;
827 m_CurrentOpticalTrain = &oneTrain;
828 mountComboBox->setCurrentText(oneTrain["mount"].toString());
829 dustCapComboBox->setCurrentText(oneTrain["dustcap"].toString());
830 lightBoxComboBox->setCurrentText(oneTrain["lightbox"].toString());
831 scopeComboBox->setCurrentText(oneTrain["scope"].toString());
832 reducerSpinBox->setValue(oneTrain["reducer"].toDouble());
833 rotatorComboBox->setCurrentText(oneTrain["rotator"].toString());
834 focusComboBox->setCurrentText(oneTrain["focuser"].toString());
835 filterComboBox->setCurrentText(oneTrain["filterwheel"].toString());
836 cameraComboBox->setCurrentText(oneTrain["camera"].toString());
837 guiderComboBox->setCurrentText(oneTrain["guider"].toString());
838 removeB->setEnabled(m_OpticalTrains.length() > 1);
839 trainConfigBox->setEnabled(true);
840 m_Persistent = true;
841 return true;
842 }
843 }
844
845 // none found
846 m_Persistent = false;
847 m_CurrentOpticalTrain = nullptr;
848 mountComboBox->setCurrentText("--");
849 dustCapComboBox->setCurrentText("--");
850 lightBoxComboBox->setCurrentText("--");
851 scopeComboBox->setCurrentText("--");
852 reducerSpinBox->setValue(1.0);
853 rotatorComboBox->setCurrentText("--");
854 focusComboBox->setCurrentText("--");
855 filterComboBox->setCurrentText("--");
856 cameraComboBox->setCurrentText("--");
857 guiderComboBox->setCurrentText("--");
858 removeB->setEnabled(false);
859 trainConfigBox->setEnabled(false);
860 m_Persistent = true;
861 return false;
862}
863
864////////////////////////////////////////////////////////////////////////////
865///
866////////////////////////////////////////////////////////////////////////////
867void OpticalTrainManager::openEditor(const QString &name)
868{
869 selectOpticalTrain(name);
870 QList<QListWidgetItem*> matches = trainNamesList->findItems(name, Qt::MatchExactly);
871 if (matches.count() > 0)
872 trainNamesList->setCurrentItem(matches.first());
873 emit configurationRequested(true);
874 show();
875}
876
877////////////////////////////////////////////////////////////////////////////
878///
879////////////////////////////////////////////////////////////////////////////
880bool OpticalTrainManager::getGenericDevice(const QString &train, Role role, QSharedPointer<ISD::GenericDevice> &generic)
881{
882 for (auto &oneTrain : m_OpticalTrains)
883 {
884 if (oneTrain["name"].toString() == train)
885 {
886 switch (role)
887 {
888 case Mount:
889 return INDIListener::findDevice(oneTrain["mount"].toString(), generic);
890 case Camera:
891 return INDIListener::findDevice(oneTrain["camera"].toString(), generic);
892 case Rotator:
893 return INDIListener::findDevice(oneTrain["rotator"].toString(), generic);
894 case GuideVia:
895 return INDIListener::findDevice(oneTrain["guider"].toString(), generic);
896 case DustCap:
897 return INDIListener::findDevice(oneTrain["dustcap"].toString(), generic);
898 case FilterWheel:
899 return INDIListener::findDevice(oneTrain["filterwheel"].toString(), generic);
900 case Focuser:
901 return INDIListener::findDevice(oneTrain["focuser"].toString(), generic);
902 case LightBox:
903 return INDIListener::findDevice(oneTrain["lightbox"].toString(), generic);
904 case Dome:
905 {
906 auto devices = INDIListener::devicesByInterface(INDI::BaseDevice::DOME_INTERFACE);
907 if (!devices.empty())
908 {
909 generic = devices[0];
910 return true;
911 }
912 else
913 return false;
914 }
915 break;
916 case Weather:
917 {
918 auto devices = INDIListener::devicesByInterface(INDI::BaseDevice::WEATHER_INTERFACE);
919 if (!devices.empty())
920 {
921 generic = devices[0];
922 return true;
923 }
924 else
925 return false;
926 }
927 break;
928 case GPS:
929 {
930 auto devices = INDIListener::devicesByInterface(INDI::BaseDevice::GPS_INTERFACE);
931 if (!devices.empty())
932 {
933 generic = devices[0];
934 return true;
935 }
936 else
937 return false;
938 }
939 default:
940 break;
941 }
942 }
943 }
944
945 return false;
946}
947
948////////////////////////////////////////////////////////////////////////////
949///
950////////////////////////////////////////////////////////////////////////////
951ISD::Mount *OpticalTrainManager::getMount(const QString &name)
952{
953 for (auto &oneTrain : m_OpticalTrains)
954 {
955 if (oneTrain["name"].toString() == name)
956 {
958 if (INDIListener::findDevice(oneTrain["mount"].toString(), generic))
959 return generic->getMount();
960 }
961 }
962
963 return nullptr;
964}
965
966////////////////////////////////////////////////////////////////////////////
967///
968////////////////////////////////////////////////////////////////////////////
969ISD::DustCap *OpticalTrainManager::getDustCap(const QString &name)
970{
971 for (auto &oneTrain : m_OpticalTrains)
972 {
973 if (oneTrain["name"].toString() == name)
974 {
976 if (INDIListener::findDevice(oneTrain["dustcap"].toString(), generic))
977 return generic->getDustCap();
978 }
979 }
980
981 return nullptr;
982}
983
984////////////////////////////////////////////////////////////////////////////
985///
986////////////////////////////////////////////////////////////////////////////
987ISD::LightBox *OpticalTrainManager::getLightBox(const QString &name)
988{
989 for (auto &oneTrain : m_OpticalTrains)
990 {
991 if (oneTrain["name"].toString() == name)
992 {
994 if (INDIListener::findDevice(oneTrain["lightbox"].toString(), generic))
995 return generic->getLightBox();
996 }
997 }
998
999 return nullptr;
1000}
1001
1002////////////////////////////////////////////////////////////////////////////
1003///
1004////////////////////////////////////////////////////////////////////////////
1005QJsonObject OpticalTrainManager::getScope(const QString &name)
1006{
1007 QJsonObject oneOpticalElement;
1008 for (auto &oneTrain : m_OpticalTrains)
1009 {
1010 if (oneTrain["name"].toString() == name)
1011 {
1012 if (KStars::Instance()->data()->userdb()->getOpticalElementByName(oneTrain["scope"].toString(), oneOpticalElement))
1013 return oneOpticalElement;
1014 }
1015 }
1016
1017 return oneOpticalElement;
1018}
1019
1020////////////////////////////////////////////////////////////////////////////
1021///
1022////////////////////////////////////////////////////////////////////////////
1023double OpticalTrainManager::getReducer(const QString &name)
1024{
1025 for (auto &oneTrain : m_OpticalTrains)
1026 {
1027 if (oneTrain["name"].toString() == name)
1028 return oneTrain["reducer"].toDouble();
1029 }
1030
1031 return 1;
1032}
1033
1034////////////////////////////////////////////////////////////////////////////
1035///
1036////////////////////////////////////////////////////////////////////////////
1037ISD::Rotator *OpticalTrainManager::getRotator(const QString &name)
1038{
1039 for (auto &oneTrain : m_OpticalTrains)
1040 {
1041 if (oneTrain["name"].toString() == name)
1042 {
1044 if (INDIListener::findDevice(oneTrain["rotator"].toString(), generic))
1045 return generic->getRotator();
1046 }
1047 }
1048
1049 return nullptr;
1050}
1051
1052////////////////////////////////////////////////////////////////////////////
1053///
1054////////////////////////////////////////////////////////////////////////////
1055ISD::Focuser *OpticalTrainManager::getFocuser(const QString &name)
1056{
1057 for (auto &oneTrain : m_OpticalTrains)
1058 {
1059 if (oneTrain["name"].toString() == name)
1060 {
1062 if (INDIListener::findDevice(oneTrain["focuser"].toString(), generic))
1063 return generic->getFocuser();
1064 }
1065 }
1066
1067 return nullptr;
1068}
1069
1070////////////////////////////////////////////////////////////////////////////
1071///
1072////////////////////////////////////////////////////////////////////////////
1073ISD::FilterWheel *OpticalTrainManager::getFilterWheel(const QString &name)
1074{
1075 for (auto &oneTrain : m_OpticalTrains)
1076 {
1077 if (oneTrain["name"].toString() == name)
1078 {
1080 if (INDIListener::findDevice(oneTrain["filterwheel"].toString(), generic))
1081 return generic->getFilterWheel();
1082 }
1083 }
1084
1085 return nullptr;
1086}
1087
1088////////////////////////////////////////////////////////////////////////////
1089///
1090////////////////////////////////////////////////////////////////////////////
1091ISD::Camera *OpticalTrainManager::getCamera(const QString &name)
1092{
1093 for (auto &oneTrain : m_OpticalTrains)
1094 {
1095 if (oneTrain["name"].toString() == name)
1096 {
1098 if (INDIListener::findDevice(oneTrain["camera"].toString(), generic))
1099 return generic->getCamera();
1100 }
1101 }
1102
1103 return nullptr;
1104}
1105
1106////////////////////////////////////////////////////////////////////////////
1107///
1108////////////////////////////////////////////////////////////////////////////
1109ISD::Guider *OpticalTrainManager::getGuider(const QString &name)
1110{
1111 for (auto &oneTrain : m_OpticalTrains)
1112 {
1113 if (oneTrain["name"].toString() == name)
1114 {
1116 if (INDIListener::findDevice(oneTrain["guider"].toString(), generic))
1117 return generic->getGuider();
1118 }
1119 }
1120
1121 return nullptr;
1122}
1123
1124////////////////////////////////////////////////////////////////////////////
1125///
1126////////////////////////////////////////////////////////////////////////////
1127ISD::AdaptiveOptics *OpticalTrainManager::getAdaptiveOptics(const QString &name)
1128{
1129 // FIXME not implmeneted yet.
1130 // Need to add to database later
1131 for (auto &oneTrain : m_OpticalTrains)
1132 {
1133 if (oneTrain["name"].toString() == name)
1134 {
1136 if (INDIListener::findDevice(oneTrain["adaptiveoptics"].toString(), generic))
1137 return generic->getAdaptiveOptics();
1138 }
1139 }
1140
1141 return nullptr;
1142}
1143
1144////////////////////////////////////////////////////////////////////////////
1145///
1146////////////////////////////////////////////////////////////////////////////
1147const QVariantMap OpticalTrainManager::getOpticalTrain(uint8_t id) const
1148{
1149 for (auto &oneTrain : m_OpticalTrains)
1150 {
1151 if (oneTrain["id"].toInt() == id)
1152 return oneTrain;
1153 }
1154
1155 return QVariantMap();
1156}
1157
1158////////////////////////////////////////////////////////////////////////////
1159///
1160////////////////////////////////////////////////////////////////////////////
1161bool OpticalTrainManager::exists(uint8_t id) const
1162{
1163 for (auto &oneTrain : m_OpticalTrains)
1164 {
1165 if (oneTrain["id"].toInt() == id)
1166 return true;
1167 }
1168
1169 return false;
1170}
1171
1172////////////////////////////////////////////////////////////////////////////
1173///
1174////////////////////////////////////////////////////////////////////////////
1175const QVariantMap OpticalTrainManager::getOpticalTrain(const QString &name) const
1176{
1177 for (auto &oneTrain : m_OpticalTrains)
1178 {
1179 if (oneTrain["name"].toString() == name)
1180 return oneTrain;
1181 }
1182
1183 return QVariantMap();
1184}
1185
1186////////////////////////////////////////////////////////////////////////////
1187///
1188////////////////////////////////////////////////////////////////////////////
1189void OpticalTrainManager::refreshTrains()
1190{
1191 refreshModel();
1192 emit updated();
1193}
1194
1195////////////////////////////////////////////////////////////////////////////
1196///
1197////////////////////////////////////////////////////////////////////////////
1198void OpticalTrainManager::refreshOpticalElements()
1199{
1200 m_ScopeNames = KStars::Instance()->data()->userdb()->getOpticalElementNames();
1201 syncDelegatesToDevices();
1202}
1203
1204////////////////////////////////////////////////////////////////////////////
1205///
1206////////////////////////////////////////////////////////////////////////////
1207int OpticalTrainManager::id(const QString &name) const
1208{
1209 for (auto &oneTrain : m_OpticalTrains)
1210 {
1211 if (oneTrain["name"].toString() == name)
1212 return oneTrain["id"].toUInt();
1213 }
1214
1215 return -1;
1216}
1217
1218////////////////////////////////////////////////////////////////////////////
1219///
1220////////////////////////////////////////////////////////////////////////////
1221QString OpticalTrainManager::name(int id) const
1222{
1223 for (auto &oneTrain : m_OpticalTrains)
1224 {
1225 if (oneTrain["id"].toInt() == id)
1226 return oneTrain["name"].toString();
1227 }
1228
1229 return QString();
1230}
1231
1232////////////////////////////////////////////////////////////////////////////
1233///
1234////////////////////////////////////////////////////////////////////////////
1235void OpticalTrainManager::checkMissingDevices()
1236{
1237 // Double check the sanity of the train. If devices are added or missing, then we need to show it to alert the user.
1238 auto devices = getMissingDevices();
1239 if (!devices.empty())
1240 {
1241 if (devices.count() == 1)
1242 {
1243 KSNotification::event(QLatin1String("IndiServerMessage"),
1244 i18n("Missing device detected (%1). Please reconfigure the optical trains before proceeding any further.",
1245 devices.first()),
1246 KSNotification::General, KSNotification::Warn);
1247 }
1248 else
1249 {
1250 KSNotification::event(QLatin1String("IndiServerMessage"),
1251 i18n("Missing devices detected (%1). Please reconfigure the optical trains before proceeding any further.",
1252 devices.join(", ")),
1253 KSNotification::General, KSNotification::Warn);
1254 }
1255 show();
1256 raise();
1257 emit configurationRequested(true);
1258 }
1259}
1260
1261////////////////////////////////////////////////////////////////////////////
1262///
1263////////////////////////////////////////////////////////////////////////////
1264QStringList OpticalTrainManager::getMissingDevices() const
1265{
1266 auto missing = QStringList();
1267 for (auto &oneTrain : m_OpticalTrains)
1268 {
1269 auto mount = oneTrain["mount"].toString();
1270 if (mount != "--" && m_MountNames.contains(mount) == false)
1271 missing << mount;
1272
1273 auto camera = oneTrain["camera"].toString();
1274 if (camera != "--" && m_CameraNames.contains(camera) == false)
1275 missing << camera;
1276
1277 auto dustcap = oneTrain["dustcap"].toString();
1278 if (dustcap != "--" && m_DustCapNames.contains(dustcap) == false)
1279 missing << dustcap;
1280
1281 auto lightbox = oneTrain["lightbox"].toString();
1282 if (lightbox != "--" && m_LightBoxNames.contains(lightbox) == false)
1283 missing << lightbox;
1284
1285 auto focuser = oneTrain["focuser"].toString();
1286 if (focuser != "--" && m_FocuserNames.contains(focuser) == false)
1287 missing << focuser;
1288
1289 auto filterwheel = oneTrain["filterwheel"].toString();
1290 if (filterwheel != "--" && m_FilterWheelNames.contains(filterwheel) == false)
1291 missing << filterwheel;
1292
1293 auto guider = oneTrain["guider"].toString();
1294 if (guider != "--" && m_GuiderNames.contains(guider) == false)
1295 missing << guider;
1296
1297 }
1298
1299 return missing;
1300}
1301
1302////////////////////////////////////////////////////////////////////////////
1303///
1304////////////////////////////////////////////////////////////////////////////
1305void Ekos::OpticalTrainManager::updateOpticalTrainValue(QComboBox *cb, const QString &element)
1306{
1307 if (trainNamesList->currentItem() != nullptr && m_Persistent == true)
1308 setOpticalTrainValue(trainNamesList->currentItem()->text(), element, cb->currentText());
1309}
1310
1311////////////////////////////////////////////////////////////////////////////
1312///
1313////////////////////////////////////////////////////////////////////////////
1314void OpticalTrainManager::updateOpticalTrainValue(double value, const QString &element)
1315{
1316 if (trainNamesList->currentItem() != nullptr && m_Persistent == true)
1317 setOpticalTrainValue(trainNamesList->currentItem()->text(), element, value);
1318
1319}
1320
1321////////////////////////////////////////////////////////////////////////////
1322/// Reset optical train to default values.
1323////////////////////////////////////////////////////////////////////////////
1324void OpticalTrainManager::reset()
1325{
1326 if (m_CurrentOpticalTrain != nullptr)
1327 {
1328 auto id = m_CurrentOpticalTrain->value("id");
1329 auto name = m_CurrentOpticalTrain->value("name");
1330 int row = trainNamesList->currentRow();
1331 m_CurrentOpticalTrain->clear();
1332
1333 m_CurrentOpticalTrain->insert("id", id);
1334 m_CurrentOpticalTrain->insert("name", name);
1335 m_CurrentOpticalTrain->insert("mount", "--");
1336 m_CurrentOpticalTrain->insert("camera", "--");
1337 m_CurrentOpticalTrain->insert("rotator", "--");
1338 m_CurrentOpticalTrain->insert("guider", "--");
1339 m_CurrentOpticalTrain->insert("dustcap", "--");
1340 m_CurrentOpticalTrain->insert("scope", "--");
1341 m_CurrentOpticalTrain->insert("filterwheel", "--");
1342 m_CurrentOpticalTrain->insert("focuser", "--");
1343 m_CurrentOpticalTrain->insert("reducer", 1);
1344 m_CurrentOpticalTrain->insert("lightbox", "--");
1345
1346 KStarsData::Instance()->userdb()->UpdateOpticalTrain(*m_CurrentOpticalTrain, id.toInt());
1347 refreshTrains();
1348 selectOpticalTrain(name.toString());
1349 trainNamesList->setCurrentRow(row);
1350 }
1351}
1352
1353}
AdaptiveOptics class handles control of INDI AdaptiveOptics devices.
Camera class controls an INDI Camera device.
Definition indicamera.h:45
Handles operation of a remotely controlled dust cover cap.
Definition indidustcap.h:25
Focuser class handles control of INDI focuser devices.
Definition indifocuser.h:21
Handles operation of a remotely controlled light box.
device handle controlling Mounts.
Definition indimount.h:29
Rotator class handles control of INDI Rotator devices.
Definition indirotator.h:20
bool GetOpticalTrains(uint32_t profileID, QList< QVariantMap > &opticalTrains)
Populate the reference passed with all optical trains.
bool UpdateOpticalTrain(const QVariantMap &oneTrain, int id)
Update an existing optical train.
KSUserDB * userdb()
Definition kstarsdata.h:223
static KStars * Instance()
Definition kstars.h:122
KStarsData * data() const
Definition kstars.h:134
Information on telescope used in observation.
Definition scope.h:18
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)
QString name(const QVariant &location)
void clicked(bool checked)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
void currentIndexChanged(int index)
void finished(int result)
void valueChanged(double d)
QVariantMap toVariantMap() const const
void append(QList< T > &&value)
iterator begin()
void clear()
qsizetype count() const const
bool empty() const const
iterator end()
iterator erase(const_iterator begin, const_iterator end)
T & first()
bool isEmpty() const const
void currentRowChanged(int currentRow)
void itemChanged(QListWidgetItem *item)
void itemClicked(QListWidgetItem *item)
Qt::ItemFlags flags() const const
void setFlags(Qt::ItemFlags flags)
QString text() const const
QSqlDatabase database(const QString &connectionName, bool open)
int count() const const
QString fieldName(int index) const const
QVariant value(const QString &name) const const
QString arg(Args &&... args) const const
void clear()
bool isEmpty() const const
ItemIsEditable
MatchExactly
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 21 2025 11:54:27 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.