From 1fe6fb6ee5584485689a33f28d24c4dbc618d378 Mon Sep 17 00:00:00 2001 From: Malin E Date: Mon, 7 Mar 2022 14:37:38 +0100 Subject: [PATCH] Address comments --- .../launcher/include/profile/horizonsdialog.h | 20 +- .../ext/launcher/resources/qss/launcher.qss | 10 +- .../launcher/src/profile/horizonsdialog.cpp | 280 +++++++++++++----- 3 files changed, 224 insertions(+), 86 deletions(-) diff --git a/apps/OpenSpace/ext/launcher/include/profile/horizonsdialog.h b/apps/OpenSpace/ext/launcher/include/profile/horizonsdialog.h index 860351217c..f6880a4a23 100644 --- a/apps/OpenSpace/ext/launcher/include/profile/horizonsdialog.h +++ b/apps/OpenSpace/ext/launcher/include/profile/horizonsdialog.h @@ -41,6 +41,7 @@ class QNetworkAccessManager; class QNetworkReply; class QLineEdit; class QPlainTextEdit; +class QProgressBar; using json = nlohmann::json; @@ -56,8 +57,10 @@ public: std::filesystem::path file() const; private slots: - void openDirectory(); + void openSaveAs(); void typeOnChange(int index); + void downloadProgress(qint64 ist, qint64 max); + void importTimeRange(); void approved(); private: @@ -77,6 +80,8 @@ private: }; void createWidgets(); + void cleanAllWidgets(); + void styleLabel(QLabel* label, bool isDirty); bool handleRequest(); bool isValidInput(); std::string constructUrl(); @@ -90,21 +95,30 @@ private: openspace::HorizonsFile _horizonsFile; QNetworkAccessManager* _manager; + QLabel* _typeLabel = nullptr; QComboBox* _typeCombo = nullptr; - QLineEdit* _directoryEdit = nullptr; - QLineEdit* _nameEdit = nullptr; + QLabel* _fileLabel = nullptr; + QLineEdit* _fileEdit = nullptr; + QLabel* _targetLabel = nullptr; QLineEdit* _targetEdit = nullptr; QComboBox* _chooseTargetCombo = nullptr; std::string _targetName; + QLabel* _centerLabel = nullptr; QLineEdit* _centerEdit = nullptr; QComboBox* _chooseObserverCombo = nullptr; std::string _observerName; + QLabel* _startLabel = nullptr; QDateTimeEdit* _startEdit = nullptr; std::string _startTime; + QLabel* _endLabel = nullptr; QDateTimeEdit* _endEdit = nullptr; std::string _endTime; + std::pair _validTimeRange; + QPushButton* _importTimeButton = nullptr; + QLabel* _stepLabel = nullptr; QLineEdit* _stepEdit = nullptr; QComboBox* _timeTypeCombo = nullptr; + QProgressBar* _downloadProgress = nullptr; QLabel* _downloadLabel = nullptr; QPlainTextEdit* _log = nullptr; diff --git a/apps/OpenSpace/ext/launcher/resources/qss/launcher.qss b/apps/OpenSpace/ext/launcher/resources/qss/launcher.qss index fe255458f5..b62ffde2a6 100644 --- a/apps/OpenSpace/ext/launcher/resources/qss/launcher.qss +++ b/apps/OpenSpace/ext/launcher/resources/qss/launcher.qss @@ -156,6 +156,14 @@ QPlainTextEdit#log { font-family: Courier; } -QLabel#url { +QLabel#thin { font-weight: normal; } + +QLabel#error { + color: rgb(221, 17, 17); +} + +QLabel#normal { + color: rgb(0, 0, 0); +} diff --git a/apps/OpenSpace/ext/launcher/src/profile/horizonsdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/horizonsdialog.cpp index 6215c39f33..d84cb7eeb1 100644 --- a/apps/OpenSpace/ext/launcher/src/profile/horizonsdialog.cpp +++ b/apps/OpenSpace/ext/launcher/src/profile/horizonsdialog.cpp @@ -34,10 +34,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -131,7 +133,6 @@ std::filesystem::path HorizonsDialog::file() const { } void HorizonsDialog::createWidgets() { - QBoxLayout* wholeLayout = new QHBoxLayout(this); QBoxLayout* layout = new QVBoxLayout(this); { QLabel* generateLabel = new QLabel("Generate a new Horizons file", this); @@ -144,14 +145,15 @@ void HorizonsDialog::createWidgets() { this ); infoLabel->setWordWrap(true); - infoLabel->setObjectName("url"); + infoLabel->setObjectName("thin"); infoLabel->setOpenExternalLinks(true); layout->addWidget(infoLabel); } { QBoxLayout* container = new QHBoxLayout(this); - QLabel* typeLabel = new QLabel("Horizons data type:", this); - container->addWidget(typeLabel); + _typeLabel = new QLabel("Horizons data type:", this); + _typeLabel->setToolTip("Choose Horizons data type"); + container->addWidget(_typeLabel); _typeCombo = new QComboBox(this); _typeCombo->setToolTip("Choose Horizons data type"); @@ -171,44 +173,41 @@ void HorizonsDialog::createWidgets() { } { QBoxLayout* container = new QHBoxLayout(this); - QLabel* nameLabel = new QLabel("Filename:", this); - container->addWidget(nameLabel); - - _nameEdit = new QLineEdit(QString::fromStdString("horizons.dat"), this); - _nameEdit->setToolTip( - "Name of the generated Horizons file. Must end with '.dat'" + _fileLabel = new QLabel("File Path:", this); + _fileLabel->setToolTip( + "Where the generated Horizons file is saved" ); - container->addWidget(_nameEdit); + container->addWidget(_fileLabel); - layout->addLayout(container); - } - { - QBoxLayout* container = new QHBoxLayout(this); - QLabel* directoryLabel = new QLabel("Save directory:", this); - container->addWidget(directoryLabel); - - _directoryEdit = new QLineEdit(this); - _directoryEdit->setToolTip( - "Directory where the generated Horizons file is saved" + _fileEdit = new QLineEdit(this); + _fileEdit->setToolTip( + "Where the generated Horizons file is saved" ); - container->addWidget(_directoryEdit); + container->addWidget(_fileEdit); QPushButton* directoryButton = new QPushButton("Browse", this); connect( directoryButton, &QPushButton::released, this, - &HorizonsDialog::openDirectory + &HorizonsDialog::openSaveAs ); directoryButton->setCursor(Qt::PointingHandCursor); + directoryButton->setToolTip( + "Browse for a file path where the generated Horizons file will be saved" + ); container->addWidget(directoryButton); layout->addLayout(container); } { QBoxLayout* container = new QHBoxLayout(this); - QLabel* targetLabel = new QLabel("Target Body:", this); - container->addWidget(targetLabel); + _targetLabel = new QLabel("Target Body:", this); + _targetLabel->setToolTip( + "Which target or body would you like Horizons trajectery data for?" + ); + _targetLabel->setObjectName("horizons-error"); + container->addWidget(_targetLabel); _targetEdit = new QLineEdit(QString::fromStdString("Mars Reconnaissance Orbiter"), this); @@ -228,8 +227,9 @@ void HorizonsDialog::createWidgets() { } { QBoxLayout* container = new QHBoxLayout(this); - QLabel* centerLabel = new QLabel("Observer Location:", this); - container->addWidget(centerLabel); + _centerLabel = new QLabel("Observer Location:", this); + _centerLabel->setToolTip("In which reference frame do you want the data in?"); + container->addWidget(_centerLabel); _centerEdit = new QLineEdit(QString::fromStdString("500@499"), this); _centerEdit->setToolTip("In which reference frame do you want the data in?"); @@ -246,8 +246,9 @@ void HorizonsDialog::createWidgets() { } { QBoxLayout* container = new QHBoxLayout(this); - QLabel* startLabel = new QLabel("Start Time:", this); - container->addWidget(startLabel); + _startLabel = new QLabel("Start Time:", this); + _startLabel->setToolTip("Enter the start date and time for the data"); + container->addWidget(_startLabel); _startEdit = new QDateTimeEdit(this); _startEdit->setDisplayFormat("yyyy-MM-dd T hh:mm"); _startEdit->setDate(QDate::currentDate().addDays(-1)); @@ -257,8 +258,9 @@ void HorizonsDialog::createWidgets() { } { QBoxLayout* container = new QHBoxLayout(this); - QLabel* endLabel = new QLabel("End Time:", this); - container->addWidget(endLabel); + _endLabel = new QLabel("End Time:", this); + _endLabel->setToolTip("Enter the end date and time for the data"); + container->addWidget(_endLabel); _endEdit = new QDateTimeEdit(this); _endEdit->setDisplayFormat("yyyy-MM-dd T hh:mm"); _endEdit->setDate(QDate::currentDate()); @@ -266,10 +268,26 @@ void HorizonsDialog::createWidgets() { container->addWidget(_endEdit); layout->addLayout(container); } + { + _importTimeButton = new QPushButton("Import timerange", this); + connect( + _importTimeButton, + &QPushButton::released, + this, + &HorizonsDialog::importTimeRange + ); + _importTimeButton->setCursor(Qt::PointingHandCursor); + _importTimeButton->setToolTip( + "Import parsed timerange for full data coverage" + ); + _importTimeButton->hide(); + layout->addWidget(_importTimeButton); + } { QBoxLayout* container = new QHBoxLayout(this); - QLabel* stepLabel = new QLabel("Step Size:", this); - container->addWidget(stepLabel); + _stepLabel = new QLabel("Step Size:", this); + _stepLabel->setToolTip("Enter the step size for the data"); + container->addWidget(_stepLabel); _stepEdit = new QLineEdit(this); _stepEdit->setValidator(new QIntValidator(this)); @@ -295,9 +313,33 @@ void HorizonsDialog::createWidgets() { } layout->addWidget(new Line); { + _log = new QPlainTextEdit(this); + _log->setReadOnly(true); + _log->setObjectName("log"); + + QPalette p = _log->palette(); + p.setColor(QPalette::All, QPalette::Base, Qt::black); + p.setColor(QPalette::All, QPalette::Text, Qt::white); + + _log->setPalette(p); + _log->setToolTip("Any error messages from the data request is displayed here"); + layout->addWidget(_log); + + appendLog("Horizons log messages:", HorizonsDialog::LogLevel::None); + } + layout->addWidget(new Line); + { + QBoxLayout* container = new QHBoxLayout(this); _downloadLabel = new QLabel("Downloading file...", this); + _downloadLabel->setObjectName("thin"); _downloadLabel->hide(); - layout->addWidget(_downloadLabel, 0, Qt::AlignRight); + + _downloadProgress = new QProgressBar(this); + _downloadProgress->hide(); + + container->addWidget(_downloadLabel); + container->addWidget(_downloadProgress); + layout->addLayout(container); } { QBoxLayout* footer = new QHBoxLayout(this); @@ -313,26 +355,43 @@ void HorizonsDialog::createWidgets() { footer->addWidget(buttons); layout->addLayout(footer); } - wholeLayout->addLayout(layout); - { - _log = new QPlainTextEdit(this); - _log->setReadOnly(true); - _log->setObjectName("log"); - - QPalette p = _log->palette(); - p.setColor(QPalette::All, QPalette::Base, Qt::black); - p.setColor(QPalette::All, QPalette::Text, Qt::white); - - _log->setPalette(p); - wholeLayout->addWidget(_log); - - appendLog("Horizons log messages:", HorizonsDialog::LogLevel::None); - } } -void HorizonsDialog::openDirectory() { - std::string directory = QFileDialog::getExistingDirectory(this).toStdString(); - _directoryEdit->setText(directory.c_str()); +void HorizonsDialog::cleanAllWidgets() { + styleLabel(_typeLabel, false); + styleLabel(_fileLabel, false); + styleLabel(_targetLabel, false); + styleLabel(_centerLabel, false); + styleLabel(_startLabel, false); + styleLabel(_endLabel, false); + styleLabel(_stepLabel, false); +} + +void HorizonsDialog::styleLabel(QLabel* label, bool isDirty) { + std::string newStyle; + if (isDirty) { + newStyle = "error"; + } + else { + newStyle = "normal"; + } + label->setObjectName(newStyle.c_str()); + label->style()->unpolish(label); + label->style()->polish(label); +} + +void HorizonsDialog::openSaveAs() { + std::string filename = QFileDialog::getSaveFileName( + this, + "Choose a file path where the generated Horizons file will be saved", + "", + "Horizons data file (*.dat);;(*.dat)", + nullptr +#ifdef __linux__ + , QFileDialog::DontUseNativeDialog +#endif + ).toStdString(); + _fileEdit->setText(filename.c_str()); } void HorizonsDialog::typeOnChange(int index) { @@ -346,14 +405,37 @@ void HorizonsDialog::typeOnChange(int index) { } else { _errorMsg->setText("Invalid Horizons type"); + styleLabel(_typeLabel, true); } } +void HorizonsDialog::downloadProgress(qint64 value, qint64 total) { + if (total < 0) { + _downloadProgress->setRange(0, 0); + return; + } + _downloadProgress->setRange(0, total); + _downloadProgress->setValue(value); +} + +void HorizonsDialog::importTimeRange() { + _startEdit->setDateTime( + QDateTime::fromString(_validTimeRange.first.c_str(), "yyyy-MMM-dd T hh:mm") + ); + _endEdit->setDateTime( + QDateTime::fromString(_validTimeRange.second.c_str(), "yyyy-MMM-dd T hh:mm") + ); + _importTimeButton->hide(); + _validTimeRange = std::pair(); +} + bool HorizonsDialog::handleRequest() { if (!isValidInput()) { return false; } _errorMsg->clear(); + cleanAllWidgets(); + _importTimeButton->hide(); std::string url = constructUrl(); @@ -384,21 +466,11 @@ bool HorizonsDialog::isValidInput() { bool isValid = true; std::string message; - // Request - // Name - if (_nameEdit->text().isEmpty()) { + // File + if (_fileEdit->text().isEmpty()) { isValid = false; - message = "Filename not selected"; - } - - // Directory - else if (_directoryEdit->text().isEmpty()) { - isValid = false; - message = "Directory not selected"; - } - else if (!std::filesystem::is_directory(_directoryEdit->text().toStdString())) { - isValid = false; - message = "The selected directory could not be found"; + message = "File path not selected"; + styleLabel(_fileLabel, true); } // Target field @@ -408,10 +480,12 @@ bool HorizonsDialog::isValidInput() { { isValid = false; message = "Target not selected"; + styleLabel(_targetLabel, true); } else if (_targetEdit->text().isEmpty() && _chooseTargetCombo->count() == 0) { isValid = false; message = "Target not selected"; + styleLabel(_targetLabel, true); } // Observer field @@ -421,16 +495,19 @@ bool HorizonsDialog::isValidInput() { { isValid = false; message = "Observer not selected"; + styleLabel(_centerLabel, true); } else if (_centerEdit->text().isEmpty() && _chooseObserverCombo->count() == 0) { isValid = false; message = "Observer not selected"; + styleLabel(_centerLabel, true); } // Step size else if (_stepEdit->text().isEmpty()) { isValid = false; message = "Step size not selected"; + styleLabel(_stepLabel, true); } // Check if input is numerical bool couldConvert = false; @@ -439,6 +516,7 @@ bool HorizonsDialog::isValidInput() { isValid = false; message = "Step size needs to be a number in range 1 to " + std::to_string(std::numeric_limits::max()); + styleLabel(_stepLabel, true); } else { // In the case of arcseconds range is different @@ -446,15 +524,17 @@ bool HorizonsDialog::isValidInput() { if (60 > step || step > 3600) { isValid = false; message = "Angular step size needs to be in range 60 to 3600"; + styleLabel(_stepLabel, true); } } - // Numbers only and in range 1 to 2147483647 (max of 32 bit int) + // Numbers only and in range 1 to 2147483647 (max of 32 bit int). // Horizons read the step size into a 32 bit int, but verifies the input on their // website as a uint32_t. If step size over 32 bit int is sent, this error // message is recived: Cannot read numeric value -- re-enter else if (1 > step || step > std::numeric_limits::max()) { isValid = false; message = "Step size is outside valid range 1 to " + std::to_string(std::numeric_limits::max()); + styleLabel(_stepLabel, true); } } @@ -475,6 +555,7 @@ std::string HorizonsDialog::constructUrl() { } else { _errorMsg->setText("Invalid Horizons type"); + styleLabel(_typeLabel, true); return ""; } @@ -536,6 +617,7 @@ std::string HorizonsDialog::constructUrl() { } else { _errorMsg->setText("Invalid time unit type"); + styleLabel(_stepLabel, true); return ""; } @@ -557,6 +639,10 @@ json HorizonsDialog::sendRequest(const std::string url) { request.setUrl(QUrl(url.c_str())); QNetworkReply* reply = _manager->get(request); + connect(reply, &QNetworkReply::downloadProgress, this, &HorizonsDialog::downloadProgress); + _downloadProgress->reset(); + _downloadProgress->show(); + QEventLoop loop; auto status = QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); if (!status) { @@ -573,6 +659,7 @@ json HorizonsDialog::sendRequest(const std::string url) { } json HorizonsDialog::handleReply(QNetworkReply* reply) { + _downloadProgress->hide(); if (reply->error() != QNetworkReply::NoError) { QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if (!checkHttpStatus(statusCode)) { @@ -675,11 +762,8 @@ openspace::HorizonsFile HorizonsDialog::handleAnswer(json& answer) { } // Create a text file and write reply to it - QString filePathQ = _directoryEdit->text(); - filePathQ.append(QDir::separator()); - filePathQ.append(_nameEdit->text()); - std::string filePath = filePathQ.toStdString(); - std::filesystem::path fullFilePath = std::filesystem::absolute(filePath); + std::filesystem::path filePath = + std::filesystem::absolute(_fileEdit->text().toStdString()); auto result = answer.find("result"); if (result == answer.end()) { @@ -691,13 +775,31 @@ openspace::HorizonsFile HorizonsDialog::handleAnswer(json& answer) { } // Check if the file already exists - if (std::filesystem::is_regular_file(fullFilePath)) { - _errorMsg->setText("File already exist, try another filename"); - return openspace::HorizonsFile(); + if (std::filesystem::is_regular_file(filePath)) { + QMessageBox msgBox; + msgBox.setText("File already exist."); + msgBox.setInformativeText("Do you want to overwrite it?"); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Yes); + int ret = msgBox.exec(); + switch (ret) { + case QMessageBox::Yes: + // If yes then continue as normal + break; + case QMessageBox::No: + case QMessageBox::Cancel: + _errorMsg->setText("File already exist, try another file path"); + styleLabel(_fileLabel, true); + return openspace::HorizonsFile(); + default: + _errorMsg->setText("Invalid answer"); + styleLabel(_fileLabel, true); + return openspace::HorizonsFile(); + } } // Return a new file with the result - return openspace::HorizonsFile(fullFilePath, *result); + return openspace::HorizonsFile(filePath, *result); } bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) { @@ -712,10 +814,13 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) { case openspace::HorizonsFile::ResultCode::ErrorSize: { std::string message = "Time range '" + _startTime + "' to '" + _endTime + "' with step size '" + _stepEdit->text().toStdString() + - "' " + _timeTypeCombo->currentText().toStdString() + - " is too big, try to increase the step size and/or decrease " + " " + _timeTypeCombo->currentText().toStdString() + + "' is too big, try to increase the step size and/or decrease " "the time range"; appendLog(message, HorizonsDialog::LogLevel::Error); + styleLabel(_startLabel, true); + styleLabel(_endLabel, true); + styleLabel(_stepLabel, true); break; } @@ -724,15 +829,18 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) { "Step size is too big, exceeds available time span for target", HorizonsDialog::LogLevel::Error ); + styleLabel(_stepLabel, true); break; case openspace::HorizonsFile::ResultCode::ErrorTimeRange: { appendLog("Time range is outside the valid range for target '" + _targetName + "'.", HorizonsDialog::LogLevel::Error); + styleLabel(_startLabel, true); + styleLabel(_endLabel, true); - std::pair validTimeRange = + _validTimeRange = _horizonsFile.parseValidTimeRange("Trajectory files", "************"); - if (validTimeRange.first.empty()) { + if (_validTimeRange.first.empty()) { appendLog( "Could not parse the valid time range from file", HorizonsDialog::LogLevel::Error @@ -740,8 +848,9 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) { break; } - appendLog("Valid time range is '" + validTimeRange.first + "' to '" + - validTimeRange.second + "'", HorizonsDialog::LogLevel::Info); + appendLog("Valid time range is '" + _validTimeRange.first + "' to '" + + _validTimeRange.second + "'", HorizonsDialog::LogLevel::Info); + _importTimeButton->show(); break; } @@ -750,6 +859,7 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) { "Use '@" + _observerName + "' as observer to list possible matches.", HorizonsDialog::LogLevel::Error ); + styleLabel(_centerLabel, true); break; case openspace::HorizonsFile::ResultCode::ErrorObserverTargetSame: @@ -757,6 +867,8 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) { "' are the same. Please use another observer for the current target.", HorizonsDialog::LogLevel::Error ); + styleLabel(_targetLabel, true); + styleLabel(_centerLabel, true); break; case openspace::HorizonsFile::ResultCode::ErrorNoData: @@ -777,6 +889,7 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) { "' as observer to search for alternatives.", HorizonsDialog::LogLevel::Info ); + styleLabel(_centerLabel, true); std::vector matchingstations = _horizonsFile.parseMatches( @@ -806,6 +919,7 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) { _observerName + "'", HorizonsDialog::LogLevel::Warning ); + styleLabel(_centerLabel, true); std::vector matchingObservers = _horizonsFile.parseMatches("Name", "matches"); @@ -831,6 +945,7 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) { appendLog("No match was found for target '" + _targetName + "'", HorizonsDialog::LogLevel::Error ); + styleLabel(_targetLabel, true); break; case openspace::HorizonsFile::ResultCode::MultipleTarget: { @@ -848,6 +963,7 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) { appendLog("Multiple matches were found for target '" + _targetName + "'", HorizonsDialog::LogLevel::Warning ); + styleLabel(_targetLabel, true); std::vector matchingTargets = _horizonsFile.parseMatches("Name", "matches");