diff --git a/kstars/ekos/ekoslive/message.cpp b/kstars/ekos/ekoslive/message.cpp index 6920ec6bd..6c41fb582 100644 --- a/kstars/ekos/ekoslive/message.cpp +++ b/kstars/ekos/ekoslive/message.cpp @@ -1,1037 +1,1040 @@ /* Ekos Live Message Copyright (C) 2018 Jasem Mutlaq Message Channel This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include "message.h" #include "commands.h" #include "profileinfo.h" #include "indi/drivermanager.h" #include "kstars.h" #include "kstarsdata.h" #include "ekos_debug.h" #include #include #include namespace EkosLive { Message::Message(Ekos::Manager *manager): m_Manager(manager) { connect(&m_WebSocket, &QWebSocket::connected, this, &Message::onConnected); connect(&m_WebSocket, &QWebSocket::disconnected, this, &Message::onDisconnected); connect(&m_WebSocket, static_cast(&QWebSocket::error), this, &Message::onError); } void Message::connectServer() { QUrl requestURL(m_URL); QUrlQuery query; query.addQueryItem("username", m_AuthResponse["username"].toString()); query.addQueryItem("token", m_AuthResponse["token"].toString()); if (m_AuthResponse.contains("remoteToken")) query.addQueryItem("remoteToken", m_AuthResponse["remoteToken"].toString()); query.addQueryItem("email", m_AuthResponse["email"].toString()); query.addQueryItem("from_date", m_AuthResponse["from_date"].toString()); query.addQueryItem("to_date", m_AuthResponse["to_date"].toString()); query.addQueryItem("plan_id", m_AuthResponse["plan_id"].toString()); query.addQueryItem("type", m_AuthResponse["type"].toString()); requestURL.setPath("/message/ekos"); requestURL.setQuery(query); m_WebSocket.open(requestURL); qCInfo(KSTARS_EKOS) << "Connecting to Websocket server at" << requestURL.toDisplayString(); } void Message::disconnectServer() { m_WebSocket.close(); } void Message::onConnected() { qCInfo(KSTARS_EKOS) << "Connected to Message Websocket server at" << m_URL.toDisplayString(); m_isConnected = true; m_ReconnectTries = 0; connect(&m_WebSocket, &QWebSocket::textMessageReceived, this, &Message::onTextReceived); sendConnection(); sendProfiles(); emit connected(); } void Message::onDisconnected() { qCInfo(KSTARS_EKOS) << "Disonnected from Message Websocket server."; m_isConnected = false; disconnect(&m_WebSocket, &QWebSocket::textMessageReceived, this, &Message::onTextReceived); emit disconnected(); } void Message::onError(QAbstractSocket::SocketError error) { qCritical(KSTARS_EKOS) << "Websocket connection error" << m_WebSocket.errorString(); if (error == QAbstractSocket::RemoteHostClosedError || error == QAbstractSocket::ConnectionRefusedError) { if (m_ReconnectTries++ < RECONNECT_MAX_TRIES) QTimer::singleShot(RECONNECT_INTERVAL, this, SLOT(connectServer())); } } void Message::onTextReceived(const QString &message) { qCInfo(KSTARS_EKOS) << "Websocket Message" << message; QJsonParseError error; auto serverMessage = QJsonDocument::fromJson(message.toLatin1(), &error); if (error.error != QJsonParseError::NoError) { qCWarning(KSTARS_EKOS) << "Ekos Live Parsing Error" << error.errorString(); return; } // TODO add check to verify token! // const QString serverToken = serverMessage.object().value("token").toString(); // if (serverToken != token) // { // qCWarning(KSTARS_EKOS) << "Invalid token received from server!" << serverToken; // return; // } const QJsonObject msgObj = serverMessage.object(); const QString command = msgObj["type"].toString(); const QJsonObject payload = msgObj["payload"].toObject(); if (command == commands[GET_CONNECTION]) { sendConnection(); } else if (command == commands[LOGOUT]) { emit expired(); return; } else if (command == commands[SET_CLIENT_STATE]) { // If client is connected, make sure clock is ticking if (payload["state"].toBool(false)) { qCInfo(KSTARS_EKOS) << "EkosLive client is connected."; // If the clock is PAUSED, run it now and sync time as well. if (KStarsData::Instance()->clock()->isActive() == false) { qCInfo(KSTARS_EKOS) << "Resuming and syncing clock."; KStarsData::Instance()->clock()->start(); QAction *a = KStars::Instance()->actionCollection()->action("time_to_now"); if (a) a->trigger(); } } // Otherwise, if KStars was started in PAUSED state // then we pause here as well to save power. else { qCInfo(KSTARS_EKOS) << "EkosLive client is disconnected."; // It was started with paused state, so let's pause IF Ekos is not running if (KStars::Instance()->isStartedWithClockRunning() == false && m_Manager->ekosStatus() == Ekos::CommunicationStatus::Idle) { qCInfo(KSTARS_EKOS) << "Stopping the clock."; KStarsData::Instance()->clock()->stop(); } } } else if (command == commands[GET_DRIVERS]) sendDrivers(); else if (command == commands[GET_PROFILES]) sendProfiles(); else if (command == commands[GET_SCOPES]) sendScopes(); else if (command.startsWith("scope_")) processScopeCommands(command, payload); else if (command.startsWith("profile_")) processProfileCommands(command, payload); if (m_Manager->getEkosStartingStatus() != Ekos::Success) return; if (command == commands[GET_STATES]) sendStates(); else if (command == commands[GET_CAMERAS]) { sendCameras(); // Try to trigger any signals based on current camera list if (m_Manager->captureModule()) m_Manager->captureModule()->checkCCD(); } else if (command == commands[GET_MOUNTS]) sendMounts(); else if (command == commands[GET_FILTER_WHEELS]) sendFilterWheels(); else if (command == commands[GET_DOMES]) sendDomes(); else if (command == commands[GET_CAPS]) sendCaps(); else if (command.startsWith("capture_")) processCaptureCommands(command, payload); else if (command.startsWith("mount_")) processMountCommands(command, payload); else if (command.startsWith("focus_")) processFocusCommands(command, payload); else if (command.startsWith("guide_")) processGuideCommands(command, payload); else if (command.startsWith("align_")) processAlignCommands(command, payload); else if (command.startsWith("polar_")) processPolarCommands(command, payload); else if (command.startsWith("dome_")) processDomeCommands(command, payload); else if (command.startsWith("cap_")) processCapCommands(command, payload); else if (command.startsWith("option_")) processOptionsCommands(command, payload); else if (command.startsWith("dslr_")) processDSLRCommands(command, payload); } void Message::sendCameras() { if (m_isConnected == false) return; QJsonArray cameraList; for(ISD::GDInterface *gd : m_Manager->findDevices(KSTARS_CCD)) { ISD::CCD *oneCCD = dynamic_cast(gd); connect(oneCCD, &ISD::CCD::newTemperatureValue, this, &Message::sendTemperature, Qt::UniqueConnection); ISD::CCDChip *primaryChip = oneCCD->getChip(ISD::CCDChip::PRIMARY_CCD); double temperature = Ekos::INVALID_VALUE, gain = Ekos::INVALID_VALUE; oneCCD->getTemperature(&temperature); oneCCD->getGain(&gain); QJsonObject oneCamera = { {"name", oneCCD->getDeviceName()}, {"canBin", primaryChip->canBin()}, {"hasTemperature", oneCCD->hasCooler()}, {"temperature", temperature}, {"canCool", oneCCD->canCool()}, {"isoList", QJsonArray::fromStringList(oneCCD->getChip(ISD::CCDChip::PRIMARY_CCD)->getISOList())}, {"iso", oneCCD->getChip(ISD::CCDChip::PRIMARY_CCD)->getISOIndex()}, {"hasVideo", oneCCD->hasVideoStream()}, {"gain", gain} }; cameraList.append(oneCamera); } sendResponse(commands[GET_CAMERAS], cameraList); } void Message::sendMounts() { if (m_isConnected == false || m_Manager->getEkosStartingStatus() != Ekos::Success) return; QJsonArray mountList; for(ISD::GDInterface *gd : m_Manager->findDevices(KSTARS_TELESCOPE)) { ISD::Telescope *oneTelescope = dynamic_cast(gd); QJsonObject oneMount = { {"name", oneTelescope->getDeviceName()}, {"canPark", oneTelescope->canPark()}, {"canSync", oneTelescope->canSync()}, {"canControlTrack", oneTelescope->canControlTrack()}, {"hasSlewRates", oneTelescope->hasSlewRates()}, {"slewRates", QJsonArray::fromStringList(oneTelescope->slewRates())}, }; mountList.append(oneMount); } sendResponse(commands[GET_MOUNTS], mountList); // Also send initial slew rate for(ISD::GDInterface *gd : m_Manager->findDevices(KSTARS_TELESCOPE)) { ISD::Telescope *oneTelescope = dynamic_cast(gd); QJsonObject slewRate = {{"slewRate", oneTelescope->getSlewRate() }}; sendResponse(commands[NEW_MOUNT_STATE], slewRate); } } void Message::sendDomes() { if (m_isConnected == false || m_Manager->getEkosStartingStatus() != Ekos::Success) return; QJsonArray domeList; for(ISD::GDInterface *gd : m_Manager->findDevices(KSTARS_DOME)) { ISD::Dome *dome = dynamic_cast(gd); QJsonObject oneDome = { {"name", dome->getDeviceName()}, {"canPark", dome->canPark()}, {"canGoto", dome->canAbsMove()}, {"canAbort", dome->canAbort()}, }; domeList.append(oneDome); } sendResponse(commands[GET_DOMES], domeList); // Also send initial azimuth for(ISD::GDInterface *gd : m_Manager->findDevices(KSTARS_DOME)) { ISD::Dome *oneDome = dynamic_cast(gd); if (oneDome->canAbsMove()) { QJsonObject status = {{"az", oneDome->azimuthPosition() }}; sendResponse(commands[NEW_DOME_STATE], status); break; } } } void Message::sendCaps() { if (m_isConnected == false || m_Manager->getEkosStartingStatus() != Ekos::Success) return; QJsonArray capList; for(ISD::GDInterface *gd : m_Manager->findDevices(KSTARS_AUXILIARY)) { if (gd->getDriverInterface() & INDI::BaseDevice::DUSTCAP_INTERFACE) { ISD::DustCap *dustCap = dynamic_cast(gd); QJsonObject oneCap = { {"name", dustCap->getDeviceName()}, {"canPark", dustCap->canPark()}, {"hasLight", dustCap->hasLight()}, }; capList.append(oneCap); } } sendResponse(commands[GET_CAPS], capList); } void Message::sendDrivers() { if (m_isConnected == false) return; sendResponse(commands[GET_DRIVERS], DriverManager::Instance()->getDriverList()); } void Message::sendScopes() { if (m_isConnected == false) return; QJsonArray scopeList; QList allScopes; KStarsData::Instance()->userdb()->GetAllScopes(allScopes); for (auto &scope : allScopes) scopeList.append(scope->toJson()); sendResponse(commands[GET_SCOPES], scopeList); } void Message::sendTemperature(double value) { ISD::CCD *oneCCD = dynamic_cast(sender()); QJsonObject temperature = { {"name", oneCCD->getDeviceName()}, {"value", value} }; sendResponse(commands[NEW_TEMPERATURE], temperature); } void Message::sendFilterWheels() { if (m_isConnected == false || m_Manager->getEkosStartingStatus() != Ekos::Success) return; QJsonArray filterList; for(ISD::GDInterface *gd : m_Manager->findDevices(KSTARS_FILTER)) { INDI::Property *prop = gd->getProperty("FILTER_NAME"); if (prop == nullptr) break; ITextVectorProperty *filterNames = prop->getText(); if (filterNames == nullptr) break; QJsonArray filters; for (int i = 0; i < filterNames->ntp; i++) filters.append(filterNames->tp[i].text); QJsonObject oneFilter = { {"name", gd->getDeviceName()}, {"filters", filters} }; filterList.append(oneFilter); } sendResponse(commands[GET_FILTER_WHEELS], filterList); } void Message::setCaptureSettings(const QJsonObject &settings) { m_Manager->captureModule()->setSettings(settings); } void Message::processCaptureCommands(const QString &command, const QJsonObject &payload) { Ekos::Capture *capture = m_Manager->captureModule(); if (command == commands[CAPTURE_PREVIEW]) { setCaptureSettings(payload); capture->captureOne(); } else if (command == commands[CAPTURE_TOGGLE_VIDEO]) { capture->toggleVideo(payload["enabled"].toBool()); } else if (command == commands[CAPTURE_START]) capture->start(); else if (command == commands[CAPTURE_STOP]) capture->stop(); else if (command == commands[CAPTURE_LOOP]) capture->startFraming(); else if (command == commands[CAPTURE_GET_SEQUENCES]) { sendCaptureSequence(capture->getSequence()); } else if (command == commands[CAPTURE_ADD_SEQUENCE]) { // Set capture settings first setCaptureSettings(payload); // Then sequence settings capture->setCount(static_cast(payload["count"].toInt())); capture->setDelay(static_cast(payload["delay"].toInt())); capture->setPrefix(payload["prefix"].toString()); // Now add job capture->addJob(); } else if (command == commands[CAPTURE_REMOVE_SEQUENCE]) { capture->removeJob(payload["index"].toInt()); } } void Message::sendCaptureSequence(const QJsonArray &sequenceArray) { sendResponse(commands[CAPTURE_GET_SEQUENCES], sequenceArray); } void Message::sendCaptureSettings(const QJsonObject &settings) { sendResponse(commands[CAPTURE_SET_SETTINGS], settings); } void Message::sendAlignSettings(const QJsonObject &settings) { sendResponse(commands[ALIGN_SET_SETTINGS], settings); } void Message::processGuideCommands(const QString &command, const QJsonObject &payload) { Ekos::Guide *guide = m_Manager->guideModule(); Q_UNUSED(payload); if (command == commands[GUIDE_START]) { guide->guide(); } else if (command == commands[GUIDE_STOP]) guide->abort(); else if (command == commands[GUIDE_CLEAR]) guide->clearCalibration(); } void Message::processFocusCommands(const QString &command, const QJsonObject &payload) { Ekos::Focus *focus = m_Manager->focusModule(); Q_UNUSED(payload); if (command == commands[FOCUS_START]) focus->start(); else if (command == commands[FOCUS_STOP]) focus->abort(); else if (command == commands[FOCUS_RESET]) focus->resetFrame(); else if (command == commands[FOCUS_IN]) focus->focusIn(payload["steps"].toInt()); else if (command == commands[FOCUS_OUT]) focus->focusOut(payload["steps"].toInt()); else if (command == commands[FOCUS_LOOP]) focus->startFraming(); } void Message::processMountCommands(const QString &command, const QJsonObject &payload) { Ekos::Mount *mount = m_Manager->mountModule(); if (command == commands[MOUNT_ABORT]) mount->abort(); else if (command == commands[MOUNT_PARK]) mount->park(); else if (command == commands[MOUNT_UNPARK]) mount->unpark(); else if (command == commands[MOUNT_SET_TRACKING]) mount->setTrackEnabled(payload["enabled"].toBool()); else if (command == commands[MOUNT_SYNC_RADE]) { mount->setJ2000Enabled(payload["isJ2000"].toBool()); mount->sync(payload["ra"].toString(), payload["de"].toString()); } else if (command == commands[MOUNT_SYNC_TARGET]) { mount->syncTarget(payload["target"].toString()); } else if (command == commands[MOUNT_GOTO_RADE]) { mount->setJ2000Enabled(payload["isJ2000"].toBool()); mount->slew(payload["ra"].toString(), payload["de"].toString()); } else if (command == commands[MOUNT_GOTO_TARGET]) { mount->gotoTarget(payload["target"].toString()); } else if (command == commands[MOUNT_SET_SLEW_RATE]) { int rate = payload["rate"].toInt(-1); if (rate >= 0) mount->setSlewRate(rate); } else if (command == commands[MOUNT_SET_MOTION]) { QString direction = payload["direction"].toString(); ISD::Telescope::TelescopeMotionCommand action = payload["action"].toBool(false) ? ISD::Telescope::MOTION_START : ISD::Telescope::MOTION_STOP; if (direction == "N") mount->motionCommand(action, ISD::Telescope::MOTION_NORTH, -1); else if (direction == "S") mount->motionCommand(action, ISD::Telescope::MOTION_SOUTH, -1); else if (direction == "E") mount->motionCommand(action, -1, ISD::Telescope::MOTION_EAST); else if (direction == "W") mount->motionCommand(action, -1, ISD::Telescope::MOTION_WEST); } } void Message::processDomeCommands(const QString &command, const QJsonObject &payload) { Ekos::Dome *dome = m_Manager->domeModule(); Q_ASSERT_X(dome != nullptr, __FUNCTION__, "Dome module is not valid"); if (command == commands[DOME_PARK]) dome->park(); else if (command == commands[DOME_UNPARK]) dome->unpark(); else if (command == commands[DOME_STOP]) dome->abort(); else if (command == commands[DOME_GOTO]) dome->setAzimuthPosition(payload["az"].toDouble()); } void Message::processCapCommands(const QString &command, const QJsonObject &payload) { Ekos::DustCap *cap = m_Manager->capModule(); Q_ASSERT_X(cap != nullptr, __FUNCTION__, "Dust cap module is not valid"); if (command == commands[CAP_PARK]) cap->park(); else if (command == commands[CAP_UNPARK]) cap->unpark(); else if (command == commands[CAP_SET_LIGHT]) cap->setLightEnabled(payload["enabled"].toBool()); } void Message::processAlignCommands(const QString &command, const QJsonObject &payload) { Ekos::Align *align = m_Manager->alignModule(); if (command == commands[ALIGN_SOLVE]) align->captureAndSolve(); else if (command == commands[ALIGN_SET_SETTINGS]) align->setSettings(payload); else if (command == commands[ALIGN_STOP]) align->abort(); else if (command == commands[ALIGN_LOAD_AND_SLEW]) { QTemporaryFile file; file.open(); file.write(QByteArray::fromBase64(payload["data"].toString().toLatin1())); file.close(); align->loadAndSlew(file.fileName()); } } void Message::setAlignStatus(Ekos::AlignState newState) { if (m_isConnected == false || m_Manager->getEkosStartingStatus() != Ekos::Success) return; QJsonObject alignState = { {"status", Ekos::alignStates[newState]} }; sendResponse(commands[NEW_ALIGN_STATE], alignState); } void Message::setAlignSolution(const QVariantMap &solution) { if (m_isConnected == false || m_Manager->getEkosStartingStatus() != Ekos::Success) return; QJsonObject alignState = { {"solution", QJsonObject::fromVariantMap(solution)}, }; sendResponse(commands[NEW_ALIGN_STATE], alignState); } void Message::processPolarCommands(const QString &command, const QJsonObject &payload) { Ekos::Align *align = m_Manager->alignModule(); if (command == commands[PAH_START]) { align->startPAHProcess(); } if (command == commands[PAH_STOP]) { align->stopPAHProcess(); } if (command == commands[PAH_SET_SETTINGS]) { align->setPAHSettings(payload); } else if (command == commands[PAH_REFRESH]) { align->setPAHRefreshDuration(payload["value"].toDouble(1)); align->startPAHRefreshProcess(); } else if (command == commands[PAH_RESET_VIEW]) { emit resetPolarView(); } else if (command == commands[PAH_SET_CROSSHAIR]) { double x = payload["x"].toDouble(); double y = payload["y"].toDouble(); if (boundingRect.isNull() == false) { // #1 Find actual dimension inside the bounding rectangle // since if we have bounding rectable then x,y fractions are INSIDE it double boundX = x * boundingRect.width(); double boundY = y * boundingRect.height(); // #2 Find fraction of the dimensions above the full image size // Add to it the bounding rect top left offsets x = (boundX + boundingRect.x()) / viewSize.width(); y = (boundY + boundingRect.y()) / viewSize.height(); } align->setPAHCorrectionOffsetPercentage(x, y); } else if (command == commands[PAH_SELECT_STAR_DONE]) { align->setPAHCorrectionSelectionComplete(); } else if (command == commands[PAH_REFRESHING_DONE]) { align->setPAHRefreshComplete(); } } void Message::setPAHStage(Ekos::Align::PAHStage stage) { if (m_isConnected == false || m_Manager->getEkosStartingStatus() != Ekos::Success) return; Q_UNUSED(stage); Ekos::Align *align = m_Manager->alignModule(); QJsonObject polarState = { {"stage", align->getPAHStage()} }; // Increase size when select star if (stage == Ekos::Align::PAH_STAR_SELECT) align->zoomAlignView(); sendResponse(commands[NEW_POLAR_STATE], polarState); } void Message::setPAHMessage(const QString &message) { if (m_isConnected == false || m_Manager->getEkosStartingStatus() != Ekos::Success) return; QTextDocument doc; doc.setHtml(message); QJsonObject polarState = { {"message", doc.toPlainText()} }; sendResponse(commands[NEW_POLAR_STATE], polarState); } void Message::setPolarResults(QLineF correctionVector, QString polarError) { if (m_isConnected == false || m_Manager->getEkosStartingStatus() != Ekos::Success) return; this->correctionVector = correctionVector; QPointF center = 0.5 * correctionVector.p1() + 0.5 * correctionVector.p2(); QJsonObject vector = { {"center_x", center.x()}, {"center_y", center.y()}, {"mag", correctionVector.length()}, {"pa", correctionVector.angle()}, {"error", polarError} }; QJsonObject polarState = { {"vector", vector} }; sendResponse(commands[NEW_POLAR_STATE], polarState); } void Message::setPAHEnabled(bool enabled) { if (m_isConnected == false || m_Manager->getEkosStartingStatus() != Ekos::Success) return; QJsonObject polarState = { {"enabled", enabled} }; sendResponse(commands[NEW_POLAR_STATE], polarState); } void Message::processProfileCommands(const QString &command, const QJsonObject &payload) { if (command == commands[START_PROFILE]) { m_Manager->setProfile(payload["name"].toString()); m_Manager->start(); } else if (command == commands[STOP_PROFILE]) { m_Manager->stop(); + + // Close all FITS Viewers + KStars::Instance()->clearAllViewers(); } else if (command == commands[ADD_PROFILE]) { m_Manager->addNamedProfile(payload); sendProfiles(); } else if (command == commands[UPDATE_PROFILE]) { m_Manager->editNamedProfile(payload); sendProfiles(); } else if (command == commands[GET_PROFILE]) { m_Manager->getNamedProfile(payload["name"].toString()); } else if (command == commands[DELETE_PROFILE]) { m_Manager->deleteNamedProfile(payload["name"].toString()); sendProfiles(); } } void Message::sendProfiles() { QJsonArray profileArray; for (const auto &oneProfile : m_Manager->profiles) profileArray.append(oneProfile->toJson()); QJsonObject profiles = { {"selectedProfile", m_Manager->getCurrentProfile()->name}, {"profiles", profileArray} }; sendResponse(commands[GET_PROFILES], profiles); } void Message::setEkosStatingStatus(Ekos::CommunicationStatus status) { if (status == Ekos::Pending) return; QJsonObject connectionState = { {"connected", true}, {"online", status == Ekos::Success} }; sendResponse(commands[NEW_CONNECTION_STATE], connectionState); } void Message::processOptionsCommands(const QString &command, const QJsonObject &payload) { if (command == commands[OPTION_SET_HIGH_BANDWIDTH]) m_Options[OPTION_SET_HIGH_BANDWIDTH] = payload["value"].toBool(true); else if (command == commands[OPTION_SET_IMAGE_TRANSFER]) m_Options[OPTION_SET_IMAGE_TRANSFER] = payload["value"].toBool(true); else if (command == commands[OPTION_SET_NOTIFICATIONS]) m_Options[OPTION_SET_NOTIFICATIONS] = payload["value"].toBool(true); else if (command == commands[OPTION_SET_CLOUD_STORAGE]) m_Options[OPTION_SET_CLOUD_STORAGE] = payload["value"].toBool(false); emit optionsChanged(m_Options); } void Message::processScopeCommands(const QString &command, const QJsonObject &payload) { if (command == commands[ADD_SCOPE]) { KStarsData::Instance()->userdb()->AddScope(payload["model"].toString(), payload["vendor"].toString(), payload["driver"].toString(), payload["type"].toString(), payload["focal_length"].toDouble(), payload["aperture"].toDouble()); } else if (command == commands[UPDATE_SCOPE]) { KStarsData::Instance()->userdb()->AddScope(payload["model"].toString(), payload["vendor"].toString(), payload["driver"].toString(), payload["type"].toString(), payload["focal_length"].toDouble(), payload["aperture"].toDouble(), payload["id"].toString()); } else if (command == commands[DELETE_SCOPE]) { KStarsData::Instance()->userdb()->DeleteEquipment("telescope", payload["id"].toInt()); } sendScopes(); } void Message::processDSLRCommands(const QString &command, const QJsonObject &payload) { if (command == commands[DSLR_SET_INFO]) { if (m_Manager->captureModule()) m_Manager->captureModule()->addDSLRInfo( payload["model"].toString(), payload["width"].toInt(), payload["height"].toInt(), payload["pixelw"].toDouble(), payload["pixelw"].toDouble()); } } void Message::requestDSLRInfo(const QString &cameraName) { m_WebSocket.sendTextMessage(QJsonDocument({{"type", commands[DSLR_GET_INFO]}, {"payload", cameraName}}).toJson(QJsonDocument::Compact)); } void Message::sendResponse(const QString &command, const QJsonObject &payload) { m_WebSocket.sendTextMessage(QJsonDocument({{"type", command}, {"payload", payload}}).toJson(QJsonDocument::Compact)); } void Message::sendResponse(const QString &command, const QJsonArray &payload) { m_WebSocket.sendTextMessage(QJsonDocument({{"type", command}, {"payload", payload}}).toJson(QJsonDocument::Compact)); } void Message::updateMountStatus(const QJsonObject &status) { if (m_isConnected == false) return; sendResponse(commands[NEW_MOUNT_STATE], status); } void Message::updateCaptureStatus(const QJsonObject &status) { if (m_isConnected == false) return; sendResponse(commands[NEW_CAPTURE_STATE], status); } void Message::updateFocusStatus(const QJsonObject &status) { if (m_isConnected == false) return; sendResponse(commands[NEW_FOCUS_STATE], status); } void Message::updateGuideStatus(const QJsonObject &status) { if (m_isConnected == false) return; sendResponse(commands[NEW_GUIDE_STATE], status); } void Message::updateDomeStatus(const QJsonObject &status) { if (m_isConnected == false) return; sendResponse(commands[NEW_DOME_STATE], status); } void Message::updateCapStatus(const QJsonObject &status) { if (m_isConnected == false) return; sendResponse(commands[NEW_CAP_STATE], status); } void Message::sendConnection() { if (m_isConnected == false) return; QJsonObject connectionState = { {"connected", true}, {"online", m_Manager->getEkosStartingStatus() == Ekos::Success} }; sendResponse(commands[NEW_CONNECTION_STATE], connectionState); } void Message::sendStates() { if (m_isConnected == false) return; QJsonObject captureState = {{ "status", m_Manager->captureStatus->text()}}; sendResponse(commands[NEW_CAPTURE_STATE], captureState); // Send capture sequence if one exists if (m_Manager->captureModule()) sendCaptureSequence(m_Manager->captureModule()->getSequence()); if (m_Manager->mountModule()) { QJsonObject mountState = { {"status", m_Manager->mountStatus->text()}, {"target", m_Manager->mountTarget->text()}, {"slewRate", m_Manager->mountModule()->slewRate()} }; sendResponse(commands[NEW_MOUNT_STATE], mountState); } QJsonObject focusState = {{ "status", m_Manager->focusStatus->text()}}; sendResponse(commands[NEW_FOCUS_STATE], focusState); QJsonObject guideState = {{ "status", m_Manager->guideStatus->text()}}; sendResponse(commands[NEW_GUIDE_STATE], guideState); if (m_Manager->alignModule()) { // Align State QJsonObject alignState = { {"status", Ekos::alignStates[m_Manager->alignModule()->status()]}, {"solvers", QJsonArray::fromStringList(m_Manager->alignModule()->getActiveSolvers())} }; sendResponse(commands[NEW_ALIGN_STATE], alignState); // Align settings sendResponse(commands[ALIGN_SET_SETTINGS], m_Manager->alignModule()->getSettings()); // Polar State QTextDocument doc; doc.setHtml(m_Manager->alignModule()->getPAHMessage()); QJsonObject polarState = { {"stage", m_Manager->alignModule()->getPAHStage()}, {"enabled", m_Manager->alignModule()->isPAHEnabled()}, {"message", doc.toPlainText()}, }; sendResponse(commands[NEW_POLAR_STATE], polarState); // Polar settings sendResponse(commands[PAH_SET_SETTINGS], m_Manager->alignModule()->getPAHSettings()); } } void Message::sendEvent(const QString &message, KSNotification::EventType event) { if (m_isConnected == false || m_Options[OPTION_SET_NOTIFICATIONS] == false) return; QJsonObject newEvent = {{ "severity", event}, {"message", message}, {"uuid", QUuid::createUuid().toString()}}; sendResponse(commands[NEW_NOTIFICATION], newEvent); } void Message::setBoundingRect(QRect rect, QSize view) { boundingRect = rect; viewSize = view; } } diff --git a/kstars/kstars.cpp b/kstars/kstars.cpp index 21ee5926e..5a5519308 100644 --- a/kstars/kstars.cpp +++ b/kstars/kstars.cpp @@ -1,668 +1,674 @@ /*************************************************************************** kstars.cpp - K Desktop Planetarium ------------------- begin : Mon Feb 5 01:11:45 PST 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kstars.h" #include "config-kstars.h" #include "version.h" #include "fov.h" #include "kactionmenu.h" #include "kstarsadaptor.h" #include "kstarsdata.h" #include "kstarssplash.h" #include "observinglist.h" #include "Options.h" #include "skymap.h" #include "skyqpainter.h" #include "texturemanager.h" #include "dialogs/finddialog.h" #include "dialogs/exportimagedialog.h" #include "skycomponents/starblockfactory.h" #ifdef HAVE_INDI #include "ekos/manager.h" #include "indi/drivermanager.h" #include "indi/guimanager.h" #endif #ifdef HAVE_CFITSIO #include "fitsviewer/fitsviewer.h" #endif #include #include #ifdef Q_OS_WIN #include #endif #include #include #include KStars *KStars::pinstance = nullptr; bool KStars::Closing = false; KStars::KStars(bool doSplash, bool clockrun, const QString &startdate) : KXmlGuiWindow(), StartClockRunning(clockrun), StartDateString(startdate) { // FIXME Hack to set RTL direction for Arabic // This is not a solution. It seems qtbase_ar.qm needs to take care of this? // qttranslations5-l10n does not contain qtbase_ar.qm // It seems qtbase_ar.ts does not exist for Qt 5.9 at all and needs to be translated. // https://wiki.qt.io/Qt_Localization if (i18n("Sky") == "السماء") qApp->setLayoutDirection(Qt::RightToLeft); setWindowTitle(i18n("KStars")); // Set thread stack size to 32MB #if QT_VERSION >= QT_VERSION_CHECK(5,10,0) QThreadPool::globalInstance()->setStackSize(33554432); #endif // Initialize logging settings if (Options::disableLogging()) KSUtils::Logging::Disable(); else if (Options::logToFile()) KSUtils::Logging::UseFile(); else KSUtils::Logging::UseDefault(); KSUtils::Logging::SyncFilterRules(); qCInfo(KSTARS) << "Welcome to KStars" << KSTARS_VERSION; qCInfo(KSTARS) << "Build:" << KSTARS_BUILD_TS; qCInfo(KSTARS) << "OS:" << QSysInfo::productType(); qCInfo(KSTARS) << "API:" << QSysInfo::buildAbi(); qCInfo(KSTARS) << "Arch:" << QSysInfo::currentCpuArchitecture(); qCInfo(KSTARS) << "Kernel Type:" << QSysInfo::kernelType(); qCInfo(KSTARS) << "Kernel Version:" << QSysInfo::kernelVersion(); qCInfo(KSTARS) << "Qt Version:" << QT_VERSION_STR; new KstarsAdaptor( this); // NOTE the weird case convention, which cannot be changed as the file is generated by the moc. #ifdef Q_OS_OSX QString vlcPlugins = QDir(QCoreApplication::applicationDirPath() + "/../PlugIns/vlc").absolutePath(); qputenv("VLC_PLUGIN_PATH", vlcPlugins.toLatin1()); QString phonon_backend_path = QDir(QCoreApplication::applicationDirPath() + "/../PlugIns/phonon4qt5_backend/phonon_vlc.so").absolutePath(); qputenv("PHONON_BACKEND", phonon_backend_path.toLatin1()); QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QString path = env.value("PATH", ""); env.insert("PATH", "/usr/bin:/usr/local/bin:\"" + QCoreApplication::applicationDirPath() + "\":" + path); QProcess dbusCheck; dbusCheck.setProcessEnvironment(env); QString pluginsDir = QDir(QCoreApplication::applicationDirPath() + "/../PlugIns").absolutePath(); QString dbusPlist = pluginsDir + "/dbus/org.freedesktop.dbus-kstars.plist"; QFile file(dbusPlist); if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); QString pListText = in.readAll(); file.close(); int programArgsLeft = pListText.indexOf("ProgramArguments"); int programArgsRight = pListText.indexOf("", programArgsLeft) + 8 - programArgsLeft; QString currentProgramArgs = pListText.mid(programArgsLeft, programArgsRight); QString newProgramArguments = "" "ProgramArguments\n" " \n" " " + QCoreApplication::applicationDirPath() + "/dbus-daemon\n" " --nofork\n" " --config-file=" + pluginsDir + "/dbus/kstars.conf\n" " "; pListText.replace(currentProgramArgs, newProgramArguments); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream << pListText; file.close(); dbusCheck.start("chmod 775 " + dbusPlist); dbusCheck.waitForFinished(); dbusCheck.start("launchctl load -w \"" + dbusPlist + "\""); dbusCheck.waitForFinished(); qDebug("Starting DBus"); } else { qDebug("DBus File Write Error"); } } else { qDebug("DBus File Read Error"); } #endif QDBusConnection::sessionBus().registerObject("/KStars", this); QDBusConnection::sessionBus().registerService("org.kde.kstars"); #ifdef HAVE_CFITSIO m_GenericFITSViewer.clear(); #endif #ifdef HAVE_INDI m_EkosManager.clear(); #endif // Set pinstance to yourself pinstance = this; connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(slotAboutToQuit())); //Initialize QActionGroups projectionGroup = new QActionGroup(this); cschemeGroup = new QActionGroup(this); hipsGroup = new QActionGroup(this); telescopeGroup = new QActionGroup(this); telescopeGroup->setExclusive(false); domeGroup = new QActionGroup(this); domeGroup->setExclusive(false); m_KStarsData = KStarsData::Create(); Q_ASSERT(m_KStarsData); //Set Geographic Location from Options m_KStarsData->setLocationFromOptions(); //Initialize Time and Date bool datetimeSet = false; if (StartDateString.isEmpty() == false) { KStarsDateTime startDate = KStarsDateTime::fromString(StartDateString); if (startDate.isValid()) data()->changeDateTime(data()->geo()->LTtoUT(startDate)); else data()->changeDateTime(KStarsDateTime::currentDateTimeUtc()); datetimeSet = true; } // JM 2016-11-15: Not need to set it again as it was initialized in the ctor of SimClock /* else data()->changeDateTime( KStarsDateTime::currentDateTimeUtc() ); */ // Initialize clock. If --paused is not in the command line, look in options if (clockrun) StartClockRunning = Options::runClock(); // If we are starting paused, we need to change datetime in data if (StartClockRunning == false) { qCInfo(KSTARS) << "KStars is started in paused state."; if (datetimeSet == false) data()->changeDateTime(KStarsDateTime::currentDateTimeUtc()); } // Setup splash screen KStarsSplash *splash = nullptr; if (doSplash) { splash = new KStarsSplash(nullptr); connect(m_KStarsData, SIGNAL(progressText(QString)), splash, SLOT(setMessage(QString))); splash->show(); } else { connect(m_KStarsData, SIGNAL(progressText(QString)), m_KStarsData, SLOT(slotConsoleMessage(QString))); } /* //set up Dark color scheme for application windows DarkPalette = QPalette(QColor("black"), QColor("black")); DarkPalette.setColor(QPalette::Inactive, QPalette::WindowText, QColor("red")); DarkPalette.setColor(QPalette::Normal, QPalette::WindowText, QColor("red")); DarkPalette.setColor(QPalette::Normal, QPalette::Base, QColor("black")); DarkPalette.setColor(QPalette::Normal, QPalette::Text, QColor(238, 0, 0)); DarkPalette.setColor(QPalette::Normal, QPalette::Highlight, QColor(238, 0, 0)); DarkPalette.setColor(QPalette::Normal, QPalette::HighlightedText, QColor("black")); DarkPalette.setColor(QPalette::Inactive, QPalette::Text, QColor(238, 0, 0)); DarkPalette.setColor(QPalette::Inactive, QPalette::Base, QColor(30, 10, 10)); //store original color scheme OriginalPalette = QApplication::palette(); */ //Initialize data. When initialization is complete, it will run dataInitFinished() if (!m_KStarsData->initialize()) return; delete splash; datainitFinished(); #if (__GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 && !defined(__UCLIBC__)) qDebug() << "glibc >= 2.1 detected. Using GNU extension sincos()"; #else qDebug() << "Did not find glibc >= 2.1. Will use ANSI-compliant sin()/cos() functions."; #endif } KStars *KStars::createInstance(bool doSplash, bool clockrun, const QString &startdate) { delete pinstance; // pinstance is set directly in constructor. new KStars(doSplash, clockrun, startdate); Q_ASSERT(pinstance && "pinstance must be non NULL"); return pinstance; } KStars::~KStars() { releaseResources(); Q_ASSERT(pinstance); pinstance = nullptr; #ifdef PROFILE_COORDINATE_CONVERSION qDebug() << "Spent " << SkyPoint::cpuTime_EqToHz << " seconds in " << SkyPoint::eqToHzCalls << " calls to SkyPoint::EquatorialToHorizontal, for an average of " << 1000. * (SkyPoint::cpuTime_EqToHz / SkyPoint::eqToHzCalls) << " ms per call"; #endif #ifdef COUNT_DMS_SINCOS_CALLS qDebug() << "Constructed " << dms::dms_constructor_calls << " dms objects, of which " << dms::dms_with_sincos_called << " had trigonometric functions called on them = " << (float(dms::dms_with_sincos_called) / float(dms::dms_constructor_calls)) * 100. << "%"; qDebug() << "Of the " << dms::trig_function_calls << " calls to sin/cos/sincos on dms objects, " << dms::redundant_trig_function_calls << " were redundant = " << ((float(dms::redundant_trig_function_calls) / float(dms::trig_function_calls)) * 100.) << "%"; qDebug() << "We had " << CachingDms::cachingdms_bad_uses << " bad uses of CachingDms in all, compared to " << CachingDms::cachingdms_constructor_calls << " constructed CachingDms objects = " << (float(CachingDms::cachingdms_bad_uses) / float(CachingDms::cachingdms_constructor_calls)) * 100. << "% bad uses"; #endif /* BUG 366596: Some KDE applications processes remain as background (zombie) processes after closing * No solution to this bug so far using Qt 5.8 as of 2016-11-24 * Therefore, the only way to solve this on Windows is to explicitly kill kstars.exe * Hopefully we do not need this hack once the above bug is resolved. */ #ifdef Q_OS_WIN QProcess::execute("taskkill /im kstars.exe /f"); #endif } void KStars::releaseResources() { delete m_KStarsData; m_KStarsData = nullptr; delete StarBlockFactory::Instance(); TextureManager::Release(); SkyQPainter::releaseImageCache(); FOVManager::releaseCache(); #ifdef HAVE_INDI GUIManager::release(); delete m_EkosManager; m_EkosManager = nullptr; // GUIManager::Instance()->close(); #endif #ifdef HAVE_CFITSIO qDeleteAll(m_FITSViewers); #endif QSqlDatabase::removeDatabase("userdb"); QSqlDatabase::removeDatabase("skydb"); } void KStars::clearCachedFindDialog() { #if 0 if (m_FindDialog) // dialog is cached { /** Delete findDialog only if it is not opened */ if (m_FindDialog->isHidden()) { delete m_FindDialog; m_FindDialog = nullptr; DialogIsObsolete = false; } else DialogIsObsolete = true; // dialog was opened so it could not deleted } #endif } void KStars::applyConfig(bool doApplyFocus) { if (Options::isTracking()) { actionCollection()->action("track_object")->setText(i18n("Stop &Tracking")); actionCollection() ->action("track_object") ->setIcon(QIcon::fromTheme("document-encrypt")); } actionCollection() ->action("coordsys") ->setText(Options::useAltAz() ? i18n("Switch to star globe view (Equatorial &Coordinates)") : i18n("Switch to horizonal view (Horizontal &Coordinates)")); actionCollection()->action("show_time_box")->setChecked(Options::showTimeBox()); actionCollection()->action("show_location_box")->setChecked(Options::showGeoBox()); actionCollection()->action("show_focus_box")->setChecked(Options::showFocusBox()); actionCollection()->action("show_statusBar")->setChecked(Options::showStatusBar()); actionCollection()->action("show_sbAzAlt")->setChecked(Options::showAltAzField()); actionCollection()->action("show_sbRADec")->setChecked(Options::showRADecField()); actionCollection()->action("show_sbJ2000RADec")->setChecked(Options::showJ2000RADecField()); actionCollection()->action("show_stars")->setChecked(Options::showStars()); actionCollection()->action("show_deepsky")->setChecked(Options::showDeepSky()); actionCollection()->action("show_planets")->setChecked(Options::showSolarSystem()); actionCollection()->action("show_clines")->setChecked(Options::showCLines()); actionCollection()->action("show_constellationart")->setChecked(Options::showConstellationArt()); actionCollection()->action("show_cnames")->setChecked(Options::showCNames()); actionCollection()->action("show_cbounds")->setChecked(Options::showCBounds()); actionCollection()->action("show_mw")->setChecked(Options::showMilkyWay()); actionCollection()->action("show_equatorial_grid")->setChecked(Options::showEquatorialGrid()); actionCollection()->action("show_horizontal_grid")->setChecked(Options::showHorizontalGrid()); actionCollection()->action("show_horizon")->setChecked(Options::showGround()); actionCollection()->action("show_flags")->setChecked(Options::showFlags()); actionCollection()->action("show_supernovae")->setChecked(Options::showSupernovae()); actionCollection()->action("show_satellites")->setChecked(Options::showSatellites()); statusBar()->setVisible(Options::showStatusBar()); //color scheme m_KStarsData->colorScheme()->loadFromConfig(); //QApplication::setPalette(Options::darkAppColors() ? DarkPalette : OriginalPalette); /** //Note: This uses style sheets to set the dark colors, this should be cross platform. Palettes have a different behavior on OS X and Windows as opposed to Linux. //It might be a good idea to use stylesheets in the future instead of palettes but this will work for now for OS X. //This is also in KStarsDbus.cpp. If you change it, change it in BOTH places. @code #ifdef Q_OS_OSX if (Options::darkAppColors()) qApp->setStyleSheet( "QWidget { background-color: black; color:red; " "selection-background-color:rgb(30,30,30);selection-color:white}" "QToolBar { border:none }" "QTabBar::tab:selected { background-color:rgb(50,50,50) }" "QTabBar::tab:!selected { background-color:rgb(30,30,30) }" "QPushButton { background-color:rgb(50,50,50);border-width:1px; border-style:solid;border-color:black}" "QPushButton::disabled { background-color:rgb(10,10,10);border-width:1px; " "border-style:solid;border-color:black }" "QToolButton:Checked { background-color:rgb(30,30,30); border:none }" "QComboBox { background-color:rgb(30,30,30); }" "QComboBox::disabled { background-color:rgb(10,10,10) }" "QScrollBar::handle { background: rgb(30,30,30) }" "QSpinBox { border-width: 1px; border-style:solid; border-color:rgb(30,30,30) }" "QDoubleSpinBox { border-width:1px; border-style:solid; border-color:rgb(30,30,30) }" "QLineEdit { border-width: 1px; border-style: solid; border-color:rgb(30,30,30) }" "QCheckBox::indicator:unchecked { background-color:rgb(30,30,30);border-width:1px; " "border-style:solid;border-color:black }" "QCheckBox::indicator:checked { background-color:red;border-width:1px; " "border-style:solid;border-color:black }" "QRadioButton::indicator:unchecked { background-color:rgb(30,30,30) }" "QRadioButton::indicator:checked { background-color:red }" "QRoundProgressBar { alternate-background-color:black }" "QDateTimeEdit {background-color:rgb(30,30,30); border-width: 1px; border-style:solid; " "border-color:rgb(30,30,30) }" "QHeaderView { color:red;background-color:black }" "QHeaderView::Section { background-color:rgb(30,30,30) }" "QTableCornerButton::section{ background-color:rgb(30,30,30) }" ""); else qApp->setStyleSheet(""); #endif @endcode **/ //Set toolbar options from config file toolBar("kstarsToolBar")->applySettings(KSharedConfig::openConfig()->group("MainToolBar")); toolBar("viewToolBar")->applySettings(KSharedConfig::openConfig()->group("ViewToolBar")); //Geographic location data()->setLocationFromOptions(); //Focus if (doApplyFocus) { SkyObject *fo = data()->objectNamed(Options::focusObject()); if (fo && fo != map()->focusObject()) { map()->setClickedObject(fo); map()->setClickedPoint(fo); map()->slotCenter(); } if (!fo) { SkyPoint fp(Options::focusRA(), Options::focusDec()); if (fp.ra().Degrees() != map()->focus()->ra().Degrees() || fp.dec().Degrees() != map()->focus()->dec().Degrees()) { map()->setClickedPoint(&fp); map()->slotCenter(); } } } } void KStars::showImgExportDialog() { if (m_ExportImageDialog) m_ExportImageDialog->show(); } void KStars::syncFOVActions() { foreach (QAction *action, fovActionMenu->menu()->actions()) { if (action->text().isEmpty()) { continue; } if (Options::fOVNames().contains(action->text().remove(0, 1))) { action->setChecked(true); } else { action->setChecked(false); } } } void KStars::hideAllFovExceptFirst() { // When there is only one visible FOV symbol, we don't need to do anything // Also, don't do anything if there are no available FOV symbols. if (data()->visibleFOVs.size() == 1 || data()->availFOVs.isEmpty()) { return; } else { // If there are no visible FOVs, select first available if (data()->visibleFOVs.isEmpty()) { Q_ASSERT(!data()->availFOVs.isEmpty()); Options::setFOVNames(QStringList(data()->availFOVs.first()->name())); } else { Options::setFOVNames(QStringList(data()->visibleFOVs.first()->name())); } // Sync FOV and update skymap data()->syncFOV(); syncFOVActions(); map()->update(); // SkyMap::forceUpdate() is not required, as FOVs are drawn as overlays } } void KStars::selectNextFov() { if (data()->getVisibleFOVs().isEmpty()) return; Q_ASSERT(!data() ->getAvailableFOVs() .isEmpty()); // The available FOVs had better not be empty if the visible ones are not. FOV *currentFov = data()->getVisibleFOVs().first(); int currentIdx = data()->availFOVs.indexOf(currentFov); // If current FOV is not the available FOV list or there is only 1 FOV available, then return if (currentIdx == -1 || data()->availFOVs.size() < 2) { return; } QStringList nextFovName; if (currentIdx == data()->availFOVs.size() - 1) { nextFovName << data()->availFOVs.first()->name(); } else { nextFovName << data()->availFOVs.at(currentIdx + 1)->name(); } Options::setFOVNames(nextFovName); data()->syncFOV(); syncFOVActions(); map()->update(); } void KStars::selectPreviousFov() { if (data()->getVisibleFOVs().isEmpty()) return; Q_ASSERT(!data() ->getAvailableFOVs() .isEmpty()); // The available FOVs had better not be empty if the visible ones are not. FOV *currentFov = data()->getVisibleFOVs().first(); int currentIdx = data()->availFOVs.indexOf(currentFov); // If current FOV is not the available FOV list or there is only 1 FOV available, then return if (currentIdx == -1 || data()->availFOVs.size() < 2) { return; } QStringList prevFovName; if (currentIdx == 0) { prevFovName << data()->availFOVs.last()->name(); } else { prevFovName << data()->availFOVs.at(currentIdx - 1)->name(); } Options::setFOVNames(prevFovName); data()->syncFOV(); syncFOVActions(); map()->update(); } //FIXME Port to QML2 //#if 0 void KStars::showWISettingsUI() { slotWISettings(); } //#endif void KStars::updateTime(const bool automaticDSTchange) { // Due to frequently use of this function save data and map pointers for speedup. // Save options and geo() to a pointer would not speedup because most of time options // and geo will accessed only one time. KStarsData *Data = data(); // dms oldLST( Data->lst()->Degrees() ); Data->updateTime(Data->geo(), automaticDSTchange); //We do this outside of kstarsdata just to get the coordinates //displayed in the infobox to update every second. // if ( !Options::isTracking() && LST()->Degrees() > oldLST.Degrees() ) { // int nSec = int( 3600.*( LST()->Hours() - oldLST.Hours() ) ); // Map->focus()->setRA( Map->focus()->ra().Hours() + double( nSec )/3600. ); // if ( Options::useAltAz() ) Map->focus()->EquatorialToHorizontal( LST(), geo()->lat() ); // Map->showFocusCoords(); // } //If time is accelerated beyond slewTimescale, then the clock's timer is stopped, //so that it can be ticked manually after each update, in order to make each time //step exactly equal to the timeScale setting. //Wrap the call to manualTick() in a singleshot timer so that it doesn't get called until //the skymap has been completely updated. if (Data->clock()->isManualMode() && Data->clock()->isActive()) { // Jasem 2017-11-13: Time for each update varies. // Ideally we want to advance the simulation clock by // the current clock scale (e.g. 1 hour) every 1 second // of real time. However, the sky map update, depending on calculations and // drawing of objects, takes variable time to complete. //QTimer::singleShot(0, Data->clock(), SLOT(manualTick())); QTimer::singleShot(1000, Data->clock(), SLOT(manualTick())); } } #ifdef HAVE_CFITSIO QPointer KStars::genericFITSViewer() { if (m_GenericFITSViewer.isNull()) { m_GenericFITSViewer = new FITSViewer(Options::independentWindowFITS() ? nullptr : this); m_GenericFITSViewer->setAttribute(Qt::WA_DeleteOnClose); m_FITSViewers.append(m_GenericFITSViewer); } return m_GenericFITSViewer; } #endif #ifdef HAVE_CFITSIO void KStars::addFITSViewer(QPointer fv) { m_FITSViewers.append(fv); connect(fv.data(), &FITSViewer::destroyed, [ &, fv]() { m_FITSViewers.removeOne(fv); }); } #endif #ifdef HAVE_INDI Ekos::Manager *KStars::ekosManager() { if (m_EkosManager.isNull()) m_EkosManager = new Ekos::Manager(Options::independentWindowEkos() ? nullptr : this); return m_EkosManager; } #endif void KStars::closeEvent(QCloseEvent *event) { KStars::Closing = true; QWidget::closeEvent(event); } +void KStars::clearAllViewers() +{ + qDeleteAll(m_FITSViewers); + QList viewers = findChildren(); + qDeleteAll(viewers); +} diff --git a/kstars/kstars.h b/kstars/kstars.h index 59fd24f56..bb3f59847 100644 --- a/kstars/kstars.h +++ b/kstars/kstars.h @@ -1,872 +1,874 @@ /** ************************************************************************* kstars.h - K Desktop Planetarium ------------------- begin : Mon Feb 5 01:11:45 PST 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /** ************************************************************************* * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #pragma once #include "config-kstars.h" #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) #include #else #include #endif #ifdef HAVE_CFITSIO #include #endif // forward declaration is enough. We only need pointers class QActionGroup; class QDockWidget; class QPalette; class KActionMenu; class KStarsData; class SkyPoint; class SkyMap; class GeoLocation; class FindDialog; class TimeStepBox; class ImageExporter; class AltVsTime; class WUTDialog; class WIView; class WILPSettings; class WIEquipSettings; class ObsConditions; class AstroCalc; class SkyCalendar; class ScriptBuilder; class PlanetViewer; //class JMoonTool; class MoonPhaseTool; class FlagManager; class Execute; class ExportImageDialog; class PrintingWizard; class HorizonManager; class EyepieceField; class AddDeepSkyObject; class OpsCatalog; class OpsGuides; class OpsSolarSystem; class OpsSatellites; class OpsSupernovae; class OpsColors; class OpsAdvanced; class OpsINDI; class OpsEkos; class OpsFITS; class OpsXplanet; namespace Ekos { class Manager; } #ifdef HAVE_CFITSIO class FITSViewer; #endif /** *@class KStars *@short This is the main window for KStars. *In addition to the GUI elements, the class contains the program clock, KStarsData, and SkyMap objects. It also contains functions for the \ref DBusInterface D-Bus interface. KStars is now a singleton class. Use KStars::createInstance() to create an instance and KStars::Instance() to get a pointer to the instance *@author Jason Harris, Jasem Mutlaq *@version 1.1 */ class KStars : public KXmlGuiWindow { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kstars") private: /** * @short Constructor. * @param doSplash should the splash panel be displayed during * initialization. * @param startClockRunning should the clock be running on startup? * @param startDateString date (in string representation) to start running from. * * @todo Refer to documentation on date format. */ explicit KStars(bool doSplash, bool startClockRunning = true, const QString &startDateString = QString()); public: /** * @short Create an instance of this class. Destroy any previous instance * @param doSplash * @param clockrunning * @param startDateString * @note See KStars::KStars for details on parameters * @return a pointer to the instance */ static KStars *createInstance(bool doSplash, bool clockrunning = true, const QString &startDateString = QString()); /** @return a pointer to the instance of this class */ inline static KStars *Instance() { return pinstance; } /** Destructor. */ ~KStars() override; /** Syncs config file. Deletes objects. */ void releaseResources(); /** @return pointer to KStarsData object which contains application data. */ inline KStarsData *data() const { return m_KStarsData; } /** @return pointer to SkyMap object which is the sky display widget. */ inline SkyMap *map() const { return m_SkyMap; } inline FlagManager *flagManager() const { return m_FlagManager; } inline PrintingWizard *printingWizard() const { return m_PrintingWizard; } #ifdef HAVE_CFITSIO QPointer genericFITSViewer(); void addFITSViewer(QPointer fv); #endif #ifdef HAVE_INDI Ekos::Manager *ekosManager(); #endif /** Add an item to the color-scheme action manu * @param name The name to use in the menu * @param actionName The internal name for the action (derived from filename) */ void addColorMenuItem(const QString &name, const QString &actionName); /** Remove an item from the color-scheme action manu * @param actionName The internal name of the action (derived from filename) */ void removeColorMenuItem(const QString &actionName); /** @short Apply config options throughout the program. * In most cases, options are set in the "Options" object directly, * but for some things we have to manually react to config changes. * @param doApplyFocus If true, then focus posiiton will be set * from config file */ void applyConfig(bool doApplyFocus = true); void showImgExportDialog(); void syncFOVActions(); void hideAllFovExceptFirst(); void selectNextFov(); void selectPreviousFov(); void showWISettingsUI(); + void clearAllViewers(); + void showWI(ObsConditions *obs); /** Load HIPS information and repopulate menu. */ void repopulateHIPS(); WIEquipSettings *getWIEquipSettings() { return m_WIEquipmentSettings; } public Q_SLOTS: /** @defgroup DBusInterface DBus Interface KStars provides powerful scripting functionality via DBus. The most common DBus functions can be constructed and executed within the ScriptBuilder tool. Any 3rd party language or tool with support for DBus can access several interfaces provided by KStars:
  • KStars: Provides functions to manipulate the skymap including zoom, pan, and motion to selected objects. Add and remove object trails and labels. Wait for user input before running further actions.
  • SimClock: Provides functions to start and stop time, set a different date and time, and to set the clock scale.
  • Ekos: Provides functions to start and stop Ekos Manager, set Ekos connection mode, and access to Ekos modules:
    • Capture: Provides functions to capture images, load sequence queues, control filter wheel, and obtain information on job progress.
    • Focus: Provides functions to focus control in manual and automated mode. Start and stop focusing procedures and set autofocus options.
    • Guide: Provides functions to start and stop calibration and autoguiding procedures. Set calibration and autoguide options.
    • Align: Provides functions to solve images use online or offline astrometry.net solver.
*/ /*@{*/ /** DBUS interface function. * Set focus to given Ra/Dec coordinates * @param ra the Right Ascension coordinate for the focus (in Hours) * @param dec the Declination coordinate for the focus (in Degrees) */ Q_SCRIPTABLE Q_NOREPLY void setRaDec(double ra, double dec); /** DBUS interface function. * Set focus to given Alt/Az coordinates. * @param alt the Altitude coordinate for the focus (in Degrees) * @param az the Azimuth coordinate for the focus (in Degrees) */ Q_SCRIPTABLE Q_NOREPLY void setAltAz(double alt, double az); /** DBUS interface function. * Point in the direction described by the string argument. * @param direction either an object name, a compass direction (e.g., "north"), or "zenith" */ Q_SCRIPTABLE Q_NOREPLY void lookTowards(const QString &direction); /** DBUS interface function. * Add a name label to the named object * @param name the name of the object to which the label will be attached */ Q_SCRIPTABLE Q_NOREPLY void addLabel(const QString &name); /** DBUS interface function. * Remove a name label from the named object * @param name the name of the object from which the label will be removed */ Q_SCRIPTABLE Q_NOREPLY void removeLabel(const QString &name); /** DBUS interface function. * Add a trail to the named solar system body * @param name the name of the body to which the trail will be attached */ Q_SCRIPTABLE Q_NOREPLY void addTrail(const QString &name); /** DBUS interface function. * Remove a trail from the named solar system body * @param name the name of the object from which the trail will be removed */ Q_SCRIPTABLE Q_NOREPLY void removeTrail(const QString &name); /** DBUS interface function. Zoom in one step. */ Q_SCRIPTABLE Q_NOREPLY void zoomIn(); /** DBUS interface function. Zoom out one step. */ Q_SCRIPTABLE Q_NOREPLY void zoomOut(); /** DBUS interface function. reset to the default zoom level. */ Q_SCRIPTABLE Q_NOREPLY void defaultZoom(); /** DBUS interface function. Set zoom level to specified value. * @param z the zoom level. Units are pixels per radian. */ Q_SCRIPTABLE Q_NOREPLY void zoom(double z); /** DBUS interface function. Set local time and date. * @param yr year of date * @param mth month of date * @param day day of date * @param hr hour of time * @param min minute of time * @param sec second of time */ Q_SCRIPTABLE Q_NOREPLY void setLocalTime(int yr, int mth, int day, int hr, int min, int sec); /** DBUS interface function. Set local time and date to present values acc. system clock * @note Just a proxy for slotSetTimeToNow(), but it is better to * keep the DBus interface separate from the internal methods. */ Q_SCRIPTABLE Q_NOREPLY void setTimeToNow(); /** DBUS interface function. Delay further execution of DBUS commands. * @param t number of seconds to delay */ Q_SCRIPTABLE Q_NOREPLY void waitFor(double t); /** DBUS interface function. Pause further DBUS execution until a key is pressed. * @param k the key which will resume DBUS execution */ Q_SCRIPTABLE Q_NOREPLY void waitForKey(const QString &k); /** DBUS interface function. Toggle tracking. * @param track engage tracking if true; else disengage tracking */ Q_SCRIPTABLE Q_NOREPLY void setTracking(bool track); /** DBUS interface function. modify a view option. * @param option the name of the option to be modified * @param value the option's new value */ Q_SCRIPTABLE Q_NOREPLY void changeViewOption(const QString &option, const QString &value); /** DBUS interface function. * @param name the name of the option to query * @return the current value of the named option */ Q_SCRIPTABLE QString getOption(const QString &name); /** DBUS interface function. Read config file. * This function is useful for restoring the user settings from the config file, * after having modified the settings in memory. * @sa writeConfig() */ Q_SCRIPTABLE Q_NOREPLY void readConfig(); /** DBUS interface function. Write current settings to config file. * This function is useful for storing user settings before modifying them with a DBUS * script. The original settings can be restored with readConfig(). * @sa readConfig() */ Q_SCRIPTABLE Q_NOREPLY void writeConfig(); /** DBUS interface function. Show text message in a popup window. * @note Not Yet Implemented * @param x x-coordinate for message window * @param y y-coordinate for message window * @param message the text to display in the message window */ Q_SCRIPTABLE Q_NOREPLY void popupMessage(int x, int y, const QString &message); /** DBUS interface function. Draw a line on the sky map. * @note Not Yet Implemented * @param x1 starting x-coordinate of line * @param y1 starting y-coordinate of line * @param x2 ending x-coordinate of line * @param y2 ending y-coordinate of line * @param speed speed at which line should appear from start to end points (in pixels per second) */ Q_SCRIPTABLE Q_NOREPLY void drawLine(int x1, int y1, int x2, int y2, int speed); /** DBUS interface function. Set the geographic location. * @param city the city name of the location * @param province the province name of the location * @param country the country name of the location * @return True if geographic location is found and set, false otherwise. */ Q_SCRIPTABLE bool setGeoLocation(const QString &city, const QString &province, const QString &country); /** DBUS interface function. Set the GPS geographic location. * @param longitude longitude in degrees (-180 West to +180 East) * @param latitude latitude in degrees (-90 South to +90 North) * @param elevation site elevation in meters * @param tz0 Time zone offset WITHOUT daylight saving time. * @return True if geographic location is set, false otherwise. */ Q_SCRIPTABLE bool setGPSLocation(double longitude, double latitude, double elevation, double tz0); /** DBUS interface function. Modify a color. * @param colorName the name of the color to be modified (e.g., "SkyColor") * @param value the new color to use */ Q_SCRIPTABLE Q_NOREPLY void setColor(const QString &colorName, const QString &value); /** DBUS interface function. Load a color scheme. * @param name the name of the color scheme to load (e.g., "Moonless Night") */ Q_SCRIPTABLE Q_NOREPLY void loadColorScheme(const QString &name); /** DBUS interface function. Export the sky image to a file. * @param filename the filename for the exported image * @param width the width for the exported image. Map's width will be used if nothing or an invalid value is supplied. * @param height the height for the exported image. Map's height will be used if nothing or an invalid value is supplied. * @param includeLegend should we include a legend? */ Q_SCRIPTABLE Q_NOREPLY void exportImage(const QString &filename, int width = -1, int height = -1, bool includeLegend = false); /** DBUS interface function. Return a URL to retrieve Digitized Sky Survey image. * @param objectName name of the object. * @note If the object is note found, the string "ERROR" is returned. */ Q_SCRIPTABLE QString getDSSURL(const QString &objectName); /** DBUS interface function. Return a URL to retrieve Digitized Sky Survey image. * @param RA_J2000 J2000.0 RA * @param Dec_J2000 J2000.0 Declination * @param width width of the image, in arcminutes (default = 15) * @param height height of the image, in arcminutes (default = 15) */ Q_SCRIPTABLE QString getDSSURL(double RA_J2000, double Dec_J2000, float width = 15, float height = 15); /** DBUS interface function. Return XML containing information about a sky object * @param objectName name of the object. * @note If the object was not found, the XML is empty. */ Q_SCRIPTABLE QString getObjectDataXML(const QString &objectName); /** DBUS interface function. Return XML containing position info about a sky object * @param objectName name of the object. * @note If the object was not found, the XML is empty. */ Q_SCRIPTABLE QString getObjectPositionInfo(const QString &objectName); /** DBUS interface function. Render eyepiece view and save it in the file(s) specified * @note See EyepieceField::renderEyepieceView() for more info. This is a DBus proxy that calls that method, and then writes the resulting image(s) to file(s). * @note Important: If imagePath is empty, but overlay is true, or destPathImage is supplied, this method will make a blocking DSS download. */ Q_SCRIPTABLE Q_NOREPLY void renderEyepieceView(const QString &objectName, const QString &destPathChart, const double fovWidth = -1.0, const double fovHeight = -1.0, const double rotation = 0.0, const double scale = 1.0, const bool flip = false, const bool invert = false, QString imagePath = QString(), const QString &destPathImage = QString(), const bool overlay = false, const bool invertColors = false); /** DBUS interface function. Set the approx field-of-view * @param FOV_Degrees field of view in degrees */ Q_SCRIPTABLE Q_NOREPLY void setApproxFOV(double FOV_Degrees); /** DBUS interface function. Get the dimensions of the Sky Map. * @return a string containing widthxheight in pixels. */ Q_SCRIPTABLE QString getSkyMapDimensions(); /** DBUS interface function. Return a newline-separated list of objects in the observing wishlist. * @note Unfortunately, unnamed objects are troublesome. Hopefully, we don't have them on the observing list. */ Q_SCRIPTABLE QString getObservingWishListObjectNames(); /** DBUS interface function. Return a newline-separated list of objects in the observing session plan. * @note Unfortunately, unnamed objects are troublesome. Hopefully, we don't have them on the observing list. */ Q_SCRIPTABLE QString getObservingSessionPlanObjectNames(); /** DBUS interface function. Print the sky image. * @param usePrintDialog if true, the KDE print dialog will be shown; otherwise, default parameters will be used * @param useChartColors if true, the "Star Chart" color scheme will be used for the printout, which will save ink. */ Q_SCRIPTABLE Q_NOREPLY void printImage(bool usePrintDialog, bool useChartColors); /** DBUS interface function. Open FITS image. * @param imageUrl URL of FITS image to load. For a local file the prefix must be file:// For example * if the file is located at /home/john/m42.fits then the full URL is file:///home/john/m42.fits */ Q_SCRIPTABLE Q_NOREPLY void openFITS(const QUrl &imageUrl); /** @}*/ /** * Update time-dependent data and (possibly) repaint the sky map. * @param automaticDSTchange change DST status automatically? */ void updateTime(const bool automaticDSTchange = true); /** action slot: sync kstars clock to system time */ void slotSetTimeToNow(); /** Apply new settings and redraw skymap */ void slotApplyConfigChanges(); /** Apply new settings for WI */ void slotApplyWIConfigChanges(); /** Called when zoom level is changed. Enables/disables zoom * actions and updates status bar. */ void slotZoomChanged(); /** action slot: Allow user to specify a field-of-view angle for the display window in degrees, * and set the zoom level accordingly. */ void slotSetZoom(); /** action slot: Toggle whether kstars is tracking current position */ void slotTrack(); /** action slot: open dialog for selecting a new geographic location */ void slotGeoLocator(); /** * @brief slotSetTelescopeEnabled call when telescope comes online or goes offline. * @param enable True if telescope is online and connected, false otherwise. */ void slotSetTelescopeEnabled(bool enable); /** * @brief slotSetDomeEnabled call when dome comes online or goes offline. * @param enable True if dome is online and connected, false otherwise. */ void slotSetDomeEnabled(bool enable); /** Delete FindDialog because ObjNames list has changed in KStarsData due to * reloading star data. So list in FindDialog must be new filled with current data. */ void clearCachedFindDialog(); /** Remove all trails which may have been added to solar system bodies */ void slotClearAllTrails(); /** Display position in the status bar. */ void slotShowPositionBar(SkyPoint *); /** action slot: open Flag Manager */ void slotFlagManager(); /** Show the eyepiece view tool */ void slotEyepieceView(SkyPoint *sp, const QString &imagePath = QString()); /** Show the add deep-sky object dialog */ void slotAddDeepSkyObject(); /** action slot: open KStars startup wizard */ void slotWizard(); void updateLocationFromWizard(const GeoLocation &geo); WIView *wiView() { return m_WIView; } bool isWIVisible() { if (!m_WIView) return false; if (!m_wiDock) return false; return m_wiDock->isVisible(); } //FIXME Port to QML2 //#if 0 /** action slot: open What's Interesting settings window */ void slotWISettings(); /** action slot: toggle What's Interesting window */ void slotToggleWIView(); //#endif private slots: /** action slot: open a dialog for setting the time and date */ void slotSetTime(); /** action slot: toggle whether kstars clock is running or not */ void slotToggleTimer(); /** action slot: advance one step forward in time */ void slotStepForward(); /** action slot: advance one step backward in time */ void slotStepBackward(); /** action slot: open dialog for finding a named object */ void slotFind(); /** action slot: open KNewStuff window to download extra data. */ void slotDownload(); /** action slot: open KStars calculator to compute astronomical ephemeris */ void slotCalculator(); /** action slot: open Elevation vs. Time tool */ void slotAVT(); /** action slot: open What's up tonight dialog */ void slotWUT(); /** action slot: open Sky Calendar tool */ void slotCalendar(); /** action slot: open the glossary */ void slotGlossary(); /** action slot: open ScriptBuilder dialog */ void slotScriptBuilder(); /** action slot: open Solar system viewer */ void slotSolarSystem(); /** action slot: open Jupiter Moons tool */ // void slotJMoonTool(); /** action slot: open Moon Phase Calendar tool */ void slotMoonPhaseTool(); #if 0 /** action slot: open Telescope wizard */ void slotTelescopeWizard(); #endif /** action slot: open INDI driver panel */ void slotINDIDriver(); /** action slot: open INDI control panel */ void slotINDIPanel(); /** action slot: open Ekos panel */ void slotEkos(); /** action slot: Track with the telescope (INDI) */ void slotINDITelescopeTrack(); /** * Action slot: Slew with the telescope (INDI) * * @param focused_object Slew to the focused object or the mouse pointer if false. * */ void slotINDITelescopeSlew(bool focused_object = true); void slotINDITelescopeSlewMousePointer(); /** * Action slot: Sync the telescope (INDI) * * @param focused_object Sync the position of the focused object or the mouse pointer if false. * */ void slotINDITelescopeSync(bool focused_object = true); void slotINDITelescopeSyncMousePointer(); /** action slot: Abort any telescope motion (INDI) */ void slotINDITelescopeAbort(); /** action slot: Park the telescope (INDI) */ void slotINDITelescopePark(); /** action slot: Unpark the telescope (INDI) */ void slotINDITelescopeUnpark(); /** action slot: Park the dome (INDI) */ void slotINDIDomePark(); /** action slot: UnPark the dome (INDI) */ void slotINDIDomeUnpark(); /** action slot: open dialog for setting the view options */ void slotViewOps(); /** finish setting up after the kstarsData has finished */ void datainitFinished(); /** Open FITS image. */ void slotOpenFITS(); /** Action slot to save the sky image to a file.*/ void slotExportImage(); /** Action slot to select a DBUS script and run it.*/ void slotRunScript(); /** Action slot to print skymap. */ void slotPrint(); /** Action slot to start Printing Wizard. */ void slotPrintingWizard(); /** Action slot to show tip-of-the-day window. */ void slotTipOfDay(); /** Action slot to set focus coordinates manually (opens FocusDialog). */ void slotManualFocus(); /** Meta-slot to point the focus at special points (zenith, N, S, E, W). * Uses the name of the Action which sent the Signal to identify the * desired direction. */ void slotPointFocus(); /** Meta-slot to set the color scheme according to the name of the * Action which sent the activating signal. */ void slotColorScheme(); /** * @brief slotThemeChanged save theme name in options */ void slotThemeChanged(); /** Select the Target symbol (a.k.a. field-of-view indicator) */ void slotTargetSymbol(bool flag); /** Select the HIPS Source catalog. */ void slotHIPSSource(); /** Invoke the Field-of-View symbol editor window */ void slotFOVEdit(); /** Toggle between Equatorial and Ecliptic coordinate systems */ void slotCoordSys(); /** Set the map projection according to the menu selection */ void slotMapProjection(); /** Toggle display of the observing list tool*/ void slotObsList(); /** Meta-slot to handle display toggles for all of the viewtoolbar buttons. * uses the name of the sender to identify the item to change. */ void slotViewToolBar(); /** Meta-slot to handle display toggles for all of the INDItoolbar buttons. * uses the name of the sender to identify the item to change. */ void slotINDIToolBar(); /** Meta-slot to handle toggling display of GUI elements (toolbars and infoboxes) * uses name of the sender action to identify the widget to hide/show. */ void slotShowGUIItem(bool); /** Toggle to and from full screen mode */ void slotFullScreen(); /** Save data to config file before exiting.*/ void slotAboutToQuit(); void slotEquipmentWriter(); void slotObserverManager(); void slotHorizonManager(); void slotExecute(); void slotPolarisHourAngle(); /** Update comets orbital elements*/ void slotUpdateComets(bool isAutoUpdate = false); /** Update asteroids orbital elements*/ void slotUpdateAsteroids(bool isAutoUpdate = false); /** Update list of recent supernovae*/ void slotUpdateSupernovae(); /** Update satellites orbital elements*/ void slotUpdateSatellites(); /** Configure Notifications */ void slotConfigureNotifications(); private: /** Load FOV information and repopulate menu. */ void repopulateFOV(); /** * @brief populateThemes Populate application themes */ void populateThemes(); /** Initialize Menu bar, toolbars and all Actions. */ void initActions(); /** Initialize Status bar. */ void initStatusBar(); /** Initialize focus position */ void initFocus(); /** Build the KStars main window */ void buildGUI(); void closeEvent(QCloseEvent *event) override; public: /** Check if the KStars main window is shown */ bool isGUIReady() { return m_SkyMap != nullptr; } /** Was KStars started with the clock running, or paused? */ bool isStartedWithClockRunning() { return StartClockRunning; } /// Set to true when the application is being closed static bool Closing; private: /// Pointer to an instance of KStars static KStars *pinstance; KActionMenu *colorActionMenu { nullptr }; KActionMenu *fovActionMenu { nullptr }; KActionMenu *hipsActionMenu { nullptr }; KStarsData *m_KStarsData { nullptr }; SkyMap *m_SkyMap { nullptr }; // Widgets TimeStepBox *m_TimeStepBox { nullptr }; // Dialogs & Tools // File Menu ExportImageDialog *m_ExportImageDialog { nullptr }; PrintingWizard *m_PrintingWizard { nullptr }; // Tool Menu AstroCalc *m_AstroCalc { nullptr }; AltVsTime *m_AltVsTime { nullptr }; SkyCalendar *m_SkyCalendar { nullptr }; ScriptBuilder *m_ScriptBuilder { nullptr }; PlanetViewer *m_PlanetViewer { nullptr }; WUTDialog *m_WUTDialog { nullptr }; // JMoonTool *m_JMoonTool { nullptr }; FlagManager *m_FlagManager { nullptr }; HorizonManager *m_HorizonManager { nullptr }; EyepieceField *m_EyepieceView { nullptr }; #ifdef HAVE_CFITSIO QPointer m_GenericFITSViewer; QList> m_FITSViewers; #endif #ifdef HAVE_INDI QPointer m_EkosManager; #endif AddDeepSkyObject *m_addDSODialog { nullptr }; // FIXME Port to QML2 //#if 0 WIView *m_WIView { nullptr }; WILPSettings *m_WISettings { nullptr }; WIEquipSettings *m_WIEquipmentSettings { nullptr }; ObsConditions *m_ObsConditions { nullptr }; QDockWidget *m_wiDock { nullptr }; //#endif QActionGroup *projectionGroup { nullptr }; QActionGroup *cschemeGroup { nullptr }; QActionGroup *hipsGroup { nullptr }; QActionGroup *telescopeGroup { nullptr }; QActionGroup *domeGroup { nullptr }; bool DialogIsObsolete { false }; bool StartClockRunning { false }; QString StartDateString; QLabel AltAzField, RADecField, J2000RADecField; //QPalette OriginalPalette, DarkPalette; OpsCatalog *opcatalog { nullptr }; OpsGuides *opguides { nullptr }; OpsSolarSystem *opsolsys { nullptr }; OpsSatellites *opssatellites { nullptr }; OpsSupernovae *opssupernovae { nullptr }; OpsColors *opcolors { nullptr }; OpsAdvanced *opadvanced { nullptr }; OpsINDI *opsindi { nullptr }; OpsEkos *opsekos { nullptr }; OpsFITS *opsfits { nullptr }; OpsXplanet *opsxplanet { nullptr }; };