From aa6b3e9d290717f3ff56caed3b1fab5a700c54fd 2017-12-21 16:46:36 From: Thibaud Rabillard Date: 2017-12-21 16:46:36 Subject: [PATCH] Merge branch 'feature/CatalogueGuiPart5' into develop --- diff --git a/app/src/MainWindow.cpp b/app/src/MainWindow.cpp index 02ff19c..8698463 100644 --- a/app/src/MainWindow.cpp +++ b/app/src/MainWindow.cpp @@ -386,8 +386,8 @@ bool MainWindow::MainWindowPrivate::checkDataToSave(QWidget *parentWidget) if (hasChanges) { // There are some unsaved changes switch (QMessageBox::question( - parentWidget, "Save changes", - tr("The catalogue controller unsaved changes.\nDo you want to save them ?"), + parentWidget, tr("Save changes"), + tr("The catalogue controller has unsaved changes.\nDo you want to save them ?"), QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::SaveAll)) { case QMessageBox::SaveAll: diff --git a/core/include/Data/DataSeriesType.h b/core/include/Data/DataSeriesType.h new file mode 100644 index 0000000..d019c83 --- /dev/null +++ b/core/include/Data/DataSeriesType.h @@ -0,0 +1,26 @@ +#ifndef SCIQLOP_DATASERIESTYPE_H +#define SCIQLOP_DATASERIESTYPE_H + +#include + +enum class DataSeriesType { SCALAR, SPECTROGRAM, VECTOR, UNKNOWN }; + +struct DataSeriesTypeUtils { + static DataSeriesType fromString(const QString &type) + { + if (type == QStringLiteral("scalar")) { + return DataSeriesType::SCALAR; + } + else if (type == QStringLiteral("spectrogram")) { + return DataSeriesType::SPECTROGRAM; + } + else if (type == QStringLiteral("vector")) { + return DataSeriesType::VECTOR; + } + else { + return DataSeriesType::UNKNOWN; + } + } +}; + +#endif // SCIQLOP_DATASERIESTYPE_H diff --git a/core/include/DataSource/DataSourceController.h b/core/include/DataSource/DataSourceController.h index cbf2d67..4e41b7e 100644 --- a/core/include/DataSource/DataSourceController.h +++ b/core/include/DataSource/DataSourceController.h @@ -75,6 +75,10 @@ public slots: void initialize(); void finalize(); + /// Request the creation of a variable from the ID_DATA_KEY of a product + void requestVariableFromProductIdKey(const QString &datasourceIdKey); + + /// Request the creation of a variable from metadata of a product void requestVariable(const QVariantHash &productData); signals: diff --git a/core/include/DataSource/DataSourceItem.h b/core/include/DataSource/DataSourceItem.h index b872ce6..a1edb5e 100644 --- a/core/include/DataSource/DataSourceItem.h +++ b/core/include/DataSource/DataSourceItem.h @@ -145,6 +145,14 @@ public: */ DataSourceItem *findItem(const QVariantHash &data, bool recursive); + /** + * @brief Searches the first child matching the specified \p ID_DATA_KEY in its metadata. + * @param id The id to search. + * @param recursive So the search recursively. + * @return the item matching the data or nullptr if it was not found. + */ + DataSourceItem *findItem(const QString &datasourceIdKey, bool recursive); + bool operator==(const DataSourceItem &other); bool operator!=(const DataSourceItem &other); diff --git a/core/include/Variable/Variable.h b/core/include/Variable/Variable.h index 88c5682..ea92956 100644 --- a/core/include/Variable/Variable.h +++ b/core/include/Variable/Variable.h @@ -4,6 +4,7 @@ #include "CoreGlobal.h" #include +#include #include #include @@ -54,6 +55,9 @@ public: /// @return the data of the variable, nullptr if there is no data std::shared_ptr dataSeries() const noexcept; + /// @return the type of data that the variable holds + DataSeriesType type() const noexcept; + QVariantHash metadata() const noexcept; bool contains(const SqpRange &range) const noexcept; @@ -76,6 +80,8 @@ public: signals: void updated(); + /// Signal emitted when when the data series of the variable is loaded for the first time + void dataInitialized(); private: class VariablePrivate; diff --git a/core/include/Variable/VariableController.h b/core/include/Variable/VariableController.h index 9c02286..9238f8a 100644 --- a/core/include/Variable/VariableController.h +++ b/core/include/Variable/VariableController.h @@ -48,26 +48,6 @@ public: */ std::shared_ptr cloneVariable(std::shared_ptr variable) noexcept; - /** - * Deletes from the controller the variable passed in parameter. - * - * Delete a variable includes: - * - the deletion of the various references to the variable in SciQlop - * - the deletion of the model variable - * - the deletion of the provider associated with the variable - * - removing the cache associated with the variable - * - * @param variable the variable to delete from the controller. - */ - void deleteVariable(std::shared_ptr variable) noexcept; - - /** - * Deletes from the controller the variables passed in parameter. - * @param variables the variables to delete from the controller. - * @sa deleteVariable() - */ - void deleteVariables(const QVector > &variables) noexcept; - /// Returns the MIME data associated to a list of variables QByteArray mimeDataForVariables(const QList > &variables) const; @@ -89,7 +69,29 @@ signals: /// validated, canceled, or failed) void acquisitionFinished(); + void variableAdded(const std::shared_ptr &variable); + public slots: + /** + * Deletes from the controller the variable passed in parameter. + * + * Delete a variable includes: + * - the deletion of the various references to the variable in SciQlop + * - the deletion of the model variable + * - the deletion of the provider associated with the variable + * - removing the cache associated with the variable + * + * @param variable the variable to delete from the controller. + */ + void deleteVariable(std::shared_ptr variable) noexcept; + + /** + * Deletes from the controller the variables passed in parameter. + * @param variables the variables to delete from the controller. + * @sa deleteVariable() + */ + void deleteVariables(const QVector > &variables) noexcept; + /// Request the data loading of the variable whithin range void onRequestDataLoading(QVector > variables, const SqpRange &range, bool synchronise); diff --git a/core/src/DataSource/DataSourceController.cpp b/core/src/DataSource/DataSourceController.cpp index a7bc3d2..b59016a 100644 --- a/core/src/DataSource/DataSourceController.cpp +++ b/core/src/DataSource/DataSourceController.cpp @@ -37,6 +37,20 @@ public: return sourceItem; } + + // Search for the first datasource item matching the specified ID_DATA_KEY + DataSourceItem *findDataSourceItem(const QString &datasourceIdKey) + { + DataSourceItem *sourceItem = nullptr; + for (const auto &item : m_DataSourceItems) { + sourceItem = item.second->findItem(datasourceIdKey, true); + if (sourceItem) { + break; + } + } + + return sourceItem; + } }; DataSourceController::DataSourceController(QObject *parent) @@ -149,6 +163,20 @@ void DataSourceController::finalize() impl->m_WorkingMutex.unlock(); } +void DataSourceController::requestVariableFromProductIdKey(const QString &datasourceIdKey) +{ + auto sourceItem = impl->findDataSourceItem(datasourceIdKey); + + if (sourceItem) { + auto sourceName = sourceItem->rootItem().name(); + auto sourceId = impl->m_DataSources.key(sourceName); + loadProductItem(sourceId, *sourceItem); + } + else { + qCWarning(LOG_DataSourceController()) << tr("requestVariable, product data not found"); + } +} + void DataSourceController::requestVariable(const QVariantHash &productData) { auto sourceItem = impl->findDataSourceItem(productData); diff --git a/core/src/DataSource/DataSourceItem.cpp b/core/src/DataSource/DataSourceItem.cpp index 86730d3..3d59332 100644 --- a/core/src/DataSource/DataSourceItem.cpp +++ b/core/src/DataSource/DataSourceItem.cpp @@ -165,6 +165,24 @@ DataSourceItem *DataSourceItem::findItem(const QVariantHash &data, bool recursiv return nullptr; } +DataSourceItem *DataSourceItem::findItem(const QString &datasourceIdKey, bool recursive) +{ + for (const auto &child : impl->m_Children) { + auto childId = child->impl->m_Data.value(ID_DATA_KEY); + if (childId == datasourceIdKey) { + return child.get(); + } + + if (recursive) { + if (auto foundItem = child->findItem(datasourceIdKey, true)) { + return foundItem; + } + } + } + + return nullptr; +} + bool DataSourceItem::operator==(const DataSourceItem &other) { // Compares items' attributes diff --git a/core/src/Variable/Variable.cpp b/core/src/Variable/Variable.cpp index 8d68fa0..d431fda 100644 --- a/core/src/Variable/Variable.cpp +++ b/core/src/Variable/Variable.cpp @@ -9,6 +9,29 @@ Q_LOGGING_CATEGORY(LOG_Variable, "Variable") +namespace { + +/** + * Searches in metadata for a value that can be converted to DataSeriesType + * @param metadata the metadata where to search + * @return the value converted to a DataSeriesType if it was found, UNKNOWN type otherwise + * @sa DataSeriesType + */ +DataSeriesType findDataSeriesType(const QVariantHash &metadata) +{ + auto dataSeriesType = DataSeriesType::UNKNOWN; + + // Go through the metadata and stop at the first value that could be converted to DataSeriesType + for (auto it = metadata.cbegin(), end = metadata.cend(); + it != end && dataSeriesType == DataSeriesType::UNKNOWN; ++it) { + dataSeriesType = DataSeriesTypeUtils::fromString(it.value().toString()); + } + + return dataSeriesType; +} + +} // namespace + struct Variable::VariablePrivate { explicit VariablePrivate(const QString &name, const QVariantHash &metadata) : m_Name{name}, @@ -17,7 +40,8 @@ struct Variable::VariablePrivate { m_Metadata{metadata}, m_DataSeries{nullptr}, m_RealRange{INVALID_RANGE}, - m_NbPoints{0} + m_NbPoints{0}, + m_Type{findDataSeriesType(m_Metadata)} { } @@ -28,7 +52,8 @@ struct Variable::VariablePrivate { m_Metadata{other.m_Metadata}, m_DataSeries{other.m_DataSeries != nullptr ? other.m_DataSeries->clone() : nullptr}, m_RealRange{other.m_RealRange}, - m_NbPoints{other.m_NbPoints} + m_NbPoints{other.m_NbPoints}, + m_Type{findDataSeriesType(m_Metadata)} { } @@ -75,6 +100,7 @@ struct Variable::VariablePrivate { std::shared_ptr m_DataSeries; SqpRange m_RealRange; int m_NbPoints; + DataSeriesType m_Type; QReadWriteLock m_Lock; }; @@ -161,16 +187,23 @@ void Variable::mergeDataSeries(std::shared_ptr dataSeries) noexcept return; } + auto dataInit = false; + // Add or merge the data impl->lockWrite(); if (!impl->m_DataSeries) { impl->m_DataSeries = dataSeries->clone(); + dataInit = true; } else { impl->m_DataSeries->merge(dataSeries.get()); } impl->purgeDataSeries(); impl->unlock(); + + if (dataInit) { + emit dataInitialized(); + } } @@ -183,6 +216,15 @@ std::shared_ptr Variable::dataSeries() const noexcept return dataSeries; } +DataSeriesType Variable::type() const noexcept +{ + impl->lockRead(); + auto type = impl->m_Type; + impl->unlock(); + + return type; +} + QVariantHash Variable::metadata() const noexcept { impl->lockRead(); diff --git a/core/src/Variable/VariableController.cpp b/core/src/Variable/VariableController.cpp index 25ae998..916d0cb 100644 --- a/core/src/Variable/VariableController.cpp +++ b/core/src/Variable/VariableController.cpp @@ -373,6 +373,8 @@ VariableController::createVariable(const QString &name, const QVariantHash &meta // impl->processRequest(newVariable, range, varRequestId); // impl->updateVariableRequest(varRequestId); + emit variableAdded(newVariable); + return newVariable; } diff --git a/gui/include/Catalogue/CatalogueEventsWidget.h b/gui/include/Catalogue/CatalogueEventsWidget.h index 0dee481..b40dfca 100644 --- a/gui/include/Catalogue/CatalogueEventsWidget.h +++ b/gui/include/Catalogue/CatalogueEventsWidget.h @@ -9,6 +9,7 @@ class DBCatalogue; class DBEvent; class DBEventProduct; class VisualizationWidget; +class VisualizationSelectionZoneItem; namespace Ui { class CatalogueEventsWidget; @@ -21,10 +22,13 @@ class CatalogueEventsWidget : public QWidget { signals: void eventsSelected(const QVector > &event); + void eventsRemoved(const QVector > &event); void eventProductsSelected( const QVector, std::shared_ptr > > &eventproducts); void selectionCleared(); + void selectionZoneAdded(const std::shared_ptr &event, const QString &productId, + VisualizationSelectionZoneItem *selectionZone); public: explicit CatalogueEventsWidget(QWidget *parent = 0); @@ -39,6 +43,8 @@ public: bool isAllEventsDisplayed() const; bool isEventDisplayed(const std::shared_ptr &event) const; + void refreshEvent(const std::shared_ptr &event); + public slots: void populateWithCatalogues(const QVector > &catalogues); void populateWithAllEvents(); diff --git a/gui/include/Catalogue/CatalogueExplorer.h b/gui/include/Catalogue/CatalogueExplorer.h index 37d3ad9..079fd15 100644 --- a/gui/include/Catalogue/CatalogueExplorer.h +++ b/gui/include/Catalogue/CatalogueExplorer.h @@ -12,6 +12,10 @@ class CatalogueEventsWidget; class CatalogueSideBarWidget; class VisualizationWidget; +class VisualizationSelectionZoneItem; + +class DBEvent; + class CatalogueExplorer : public QDialog { Q_OBJECT @@ -25,6 +29,10 @@ public: CatalogueEventsWidget &eventsWidget() const; CatalogueSideBarWidget &sideBarWidget() const; + void clearSelectionZones(); + void addSelectionZoneItem(const std::shared_ptr &event, const QString &productId, + VisualizationSelectionZoneItem *selectionZone); + private: Ui::CatalogueExplorer *ui; diff --git a/gui/include/Catalogue/CatalogueInspectorWidget.h b/gui/include/Catalogue/CatalogueInspectorWidget.h index 51c42b9..1bef099 100644 --- a/gui/include/Catalogue/CatalogueInspectorWidget.h +++ b/gui/include/Catalogue/CatalogueInspectorWidget.h @@ -36,6 +36,8 @@ public: const std::shared_ptr &eventProduct); void setCatalogue(const std::shared_ptr &catalogue); + void refresh(); + public slots: void showPage(Page page); diff --git a/gui/include/Visualization/AxisRenderingUtils.h b/gui/include/Visualization/AxisRenderingUtils.h index 29fc11e..dbf1709 100644 --- a/gui/include/Visualization/AxisRenderingUtils.h +++ b/gui/include/Visualization/AxisRenderingUtils.h @@ -12,6 +12,7 @@ class IDataSeries; class QCPAxis; class QCustomPlot; class SqpColorScale; +class Variable; /// Formats a data value according to the axis on which it is present QString formatValue(double value, const QCPAxis &axis); @@ -27,11 +28,17 @@ struct IAxisHelper { /// @param plot the plot for which to set axe properties /// @param colorScale the color scale for which to set properties virtual void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) = 0; + + /// Set the units of the plot's axes and the color scale associated to plot passed as + /// parameters + /// @param plot the plot for which to set axe units + /// @param colorScale the color scale for which to set unit + virtual void setUnits(QCustomPlot &plot, SqpColorScale &colorScale) = 0; }; struct IAxisHelperFactory { - /// Creates IAxisHelper according to a data series - static std::unique_ptr create(std::shared_ptr dataSeries) noexcept; + /// Creates IPlottablesHelper according to the type of data series a variable holds + static std::unique_ptr create(const Variable &variable) noexcept; }; #endif // SCIQLOP_AXISRENDERINGUTILS_H diff --git a/gui/include/Visualization/PlottablesRenderingUtils.h b/gui/include/Visualization/PlottablesRenderingUtils.h index 055a490..6c958d3 100644 --- a/gui/include/Visualization/PlottablesRenderingUtils.h +++ b/gui/include/Visualization/PlottablesRenderingUtils.h @@ -1,6 +1,8 @@ #ifndef SCIQLOP_PLOTTABLESRENDERINGUTILS_H #define SCIQLOP_PLOTTABLESRENDERINGUTILS_H +#include + #include #include @@ -9,9 +11,9 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_PlottablesRenderingUtils) -class IDataSeries; class QCPColorScale; class QCustomPlot; +class Variable; /** * Helper used to handle plottables rendering @@ -25,9 +27,8 @@ struct IPlottablesHelper { }; struct IPlottablesHelperFactory { - /// Creates IPlottablesHelper according to a data series - static std::unique_ptr - create(std::shared_ptr dataSeries) noexcept; + /// Creates IPlottablesHelper according to the type of data series a variable holds + static std::unique_ptr create(const Variable &variable) noexcept; }; #endif // SCIQLOP_PLOTTABLESRENDERINGUTILS_H diff --git a/gui/include/Visualization/VisualizationGraphHelper.h b/gui/include/Visualization/VisualizationGraphHelper.h index c0bf2cf..42eee9c 100644 --- a/gui/include/Visualization/VisualizationGraphHelper.h +++ b/gui/include/Visualization/VisualizationGraphHelper.h @@ -32,7 +32,7 @@ struct VisualizationGraphHelper { */ static PlottablesMap create(std::shared_ptr variable, QCustomPlot &plot) noexcept; - static void updateData(PlottablesMap &plottables, std::shared_ptr dataSeries, + static void updateData(PlottablesMap &plottables, std::shared_ptr variable, const SqpRange &dateTime); static void setYAxisRange(std::shared_ptr variable, QCustomPlot &plot) noexcept; diff --git a/gui/include/Visualization/VisualizationGraphRenderingDelegate.h b/gui/include/Visualization/VisualizationGraphRenderingDelegate.h index e59fd18..20fb5a4 100644 --- a/gui/include/Visualization/VisualizationGraphRenderingDelegate.h +++ b/gui/include/Visualization/VisualizationGraphRenderingDelegate.h @@ -9,6 +9,7 @@ class IDataSeries; class QCustomPlot; class QMouseEvent; class Unit; +class Variable; class VisualizationGraphWidget; class VisualizationGraphRenderingDelegate { @@ -23,13 +24,14 @@ public: /// Updates rendering when data of plot changed void onPlotUpdated() noexcept; - /// Sets properties of the plot's axes from the data series passed as parameter - void setAxesProperties(std::shared_ptr dataSeries) noexcept; + /// Sets units of the plot's axes according to the properties of the variable passed as + /// parameter + void setAxesUnits(const Variable &variable) noexcept; - /// Sets rendering properties of the plottables passed as parameter, from the data series that + /// Sets graph properties of the plottables passed as parameter, from the variable that /// generated these - void setPlottablesProperties(std::shared_ptr dataSeries, - PlottablesMap &plottables) noexcept; + void setGraphProperties(const Variable &variable, PlottablesMap &plottables) noexcept; + /// Shows or hides graph overlay (name, close button, etc.) void showGraphOverlay(bool show) noexcept; diff --git a/gui/include/Visualization/VisualizationGraphWidget.h b/gui/include/Visualization/VisualizationGraphWidget.h index efee1c9..c48e045 100644 --- a/gui/include/Visualization/VisualizationGraphWidget.h +++ b/gui/include/Visualization/VisualizationGraphWidget.h @@ -69,13 +69,16 @@ public: /// Sets the y-axis range based on the data of a variable void setYRange(std::shared_ptr variable); SqpRange graphRange() const noexcept; - void setGraphRange(const SqpRange &range); + void setGraphRange(const SqpRange &range, bool calibration = false); + void setAutoRangeOnVariableInitialization(bool value); // Zones /// Returns the ranges of all the selection zones on the graph QVector selectionZoneRanges() const; /// Adds new selection zones in the graph void addSelectionZones(const QVector &ranges); + /// Adds a new selection zone in the graph + VisualizationSelectionZoneItem *addSelectionZone(const QString &name, const SqpRange &range); /// Removes the specified selection zone void removeSelectionZone(VisualizationSelectionZoneItem *selectionZone); diff --git a/gui/include/Visualization/VisualizationSelectionZoneItem.h b/gui/include/Visualization/VisualizationSelectionZoneItem.h index d7b6e58..675981e 100644 --- a/gui/include/Visualization/VisualizationSelectionZoneItem.h +++ b/gui/include/Visualization/VisualizationSelectionZoneItem.h @@ -8,6 +8,11 @@ class VisualizationGraphWidget; class VisualizationSelectionZoneItem : public QCPItemRect { + Q_OBJECT + +signals: + /// Signal emitted when the zone range is edited manually + void rangeEdited(const SqpRange &range); public: VisualizationSelectionZoneItem(QCustomPlot *plot); diff --git a/gui/meson.build b/gui/meson.build index add3dca..1a81c55 100644 --- a/gui/meson.build +++ b/gui/meson.build @@ -20,6 +20,7 @@ gui_moc_headers = [ 'include/Visualization/VisualizationDragDropContainer.h', 'include/Visualization/VisualizationDragWidget.h', 'include/Visualization/ColorScaleEditor.h', + 'include/Visualization/VisualizationSelectionZoneItem.h', 'include/Actions/SelectionZoneAction.h', 'include/Visualization/VisualizationMultiZoneSelectionDialog.h', 'include/Catalogue/CatalogueExplorer.h', diff --git a/gui/src/Catalogue/CatalogueActionManager.cpp b/gui/src/Catalogue/CatalogueActionManager.cpp index 398c83d..0521df5 100644 --- a/gui/src/Catalogue/CatalogueActionManager.cpp +++ b/gui/src/Catalogue/CatalogueActionManager.cpp @@ -48,14 +48,18 @@ struct CatalogueActionManager::CatalogueActionManagerPrivate { auto eventProduct = std::make_shared(); eventProduct->setEvent(*event); + auto productId + = var->metadata().value(DataSourceItem::ID_DATA_KEY, "UnknownID").toString(); + auto zoneRange = zone->range(); eventProduct->setTStart(zoneRange.m_TStart); eventProduct->setTEnd(zoneRange.m_TEnd); - eventProduct->setProductId( - var->metadata().value(DataSourceItem::ID_DATA_KEY, "UnknownID").toString()); + eventProduct->setProductId(productId); productList.push_back(*eventProduct); + + m_CatalogueExplorer->addSelectionZoneItem(event, productId, zone); } } diff --git a/gui/src/Catalogue/CatalogueEventsWidget.cpp b/gui/src/Catalogue/CatalogueEventsWidget.cpp index d55ec11..882d212 100644 --- a/gui/src/Catalogue/CatalogueEventsWidget.cpp +++ b/gui/src/Catalogue/CatalogueEventsWidget.cpp @@ -6,7 +6,13 @@ #include #include #include +#include +#include +#include #include +#include +#include +#include #include #include #include @@ -21,6 +27,9 @@ Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget") /// Fixed size of the validation column const auto VALIDATION_COLUMN_SIZE = 35; +/// Percentage added to the range of a event when it is displayed +const auto EVENT_RANGE_MARGE = 30; // in % + struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate { CatalogueEventsModel *m_Model = nullptr; @@ -28,6 +37,7 @@ struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate { QString m_ZoneForGraphMode; QVector > m_DisplayedCatalogues; bool m_AllEventDisplayed = false; + QVector m_CustomGraphs; VisualizationWidget *m_VisualizationWidget = nullptr; @@ -178,31 +188,134 @@ struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate { } } - void updateForGraphMode(QTreeView *treeView) + QVector getGraphRanges(const std::shared_ptr &event) { - auto selectedRows = treeView->selectionModel()->selectedRows(); - - if (selectedRows.count() == 1) { - auto event = m_Model->getEvent(selectedRows.first()); - if (m_VisualizationWidget) { - if (auto tab = m_VisualizationWidget->currentTabWidget()) { - if (auto zone = tab->getZoneWithName(m_ZoneForGraphMode)) { - // TODO - } - } - else { - qCWarning(LOG_CatalogueEventsWidget()) - << "updateGraphMode: no tab found in the visualization"; - } - } - else { - qCWarning(LOG_CatalogueEventsWidget()) - << "updateGraphMode: visualization widget not found"; + // Retrieves the range of each product and the maximum size + QVector graphRanges; + double maxDt = 0; + for (auto eventProduct : event->getEventProducts()) { + SqpRange eventRange; + eventRange.m_TStart = eventProduct.getTStart(); + eventRange.m_TEnd = eventProduct.getTEnd(); + graphRanges << eventRange; + + auto dt = eventRange.m_TEnd - eventRange.m_TStart; + if (dt > maxDt) { + maxDt = dt; } } - else { + + // Adds the marge + maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0; + + // Corrects the graph ranges so that they all have the same size + QVector correctedGraphRanges; + for (auto range : graphRanges) { + auto dt = range.m_TEnd - range.m_TStart; + auto diff = qAbs((maxDt - dt) / 2.0); + + SqpRange correctedRange; + correctedRange.m_TStart = range.m_TStart - diff; + correctedRange.m_TEnd = range.m_TEnd + diff; + + correctedGraphRanges << correctedRange; + } + + return correctedGraphRanges; + } + + void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget) + { + auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows(); + if (selectedRows.count() != 1) { qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: not compatible with multiple events selected"; + return; + } + + if (!m_VisualizationWidget) { + qCWarning(LOG_CatalogueEventsWidget()) + << "updateGraphMode: visualization widget not found"; + return; + } + + auto event = m_Model->getEvent(selectedRows.first()); + if (!event) { + // A event product is probably selected + qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected"; + return; + } + + auto tab = m_VisualizationWidget->currentTabWidget(); + if (!tab) { + qCWarning(LOG_CatalogueEventsWidget()) + << "updateGraphMode: no tab found in the visualization"; + return; + } + + auto zone = tab->getZoneWithName(m_ZoneForGraphMode); + if (!zone) { + qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found"; + return; + } + + // Close the previous graph and delete the asociated variables + for (auto graph : m_CustomGraphs) { + graph->close(); + auto variables = graph->variables().toVector(); + + QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables", + Qt::QueuedConnection, + Q_ARG(QVector >, variables)); + } + m_CustomGraphs.clear(); + + // Calculates the range of each graph which will be created + auto graphRange = getGraphRanges(event); + + // Loops through the event products and create the graph + auto itRange = graphRange.cbegin(); + for (auto eventProduct : event->getEventProducts()) { + auto productId = eventProduct.getProductId(); + + auto range = *itRange; + ++itRange; + + SqpRange productRange; + productRange.m_TStart = eventProduct.getTStart(); + productRange.m_TEnd = eventProduct.getTEnd(); + + auto context = new QObject{catalogueEventWidget}; + QObject::connect( + &sqpApp->variableController(), &VariableController::variableAdded, context, + [this, catalogueEventWidget, zone, context, event, range, productRange, + productId](auto variable) { + + if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString() + == productId) { + auto graph = zone->createGraph(variable); + graph->setAutoRangeOnVariableInitialization(false); + + auto selectionZone + = graph->addSelectionZone(event->getName(), productRange); + emit catalogueEventWidget->selectionZoneAdded(event, productId, + selectionZone); + m_CustomGraphs << graph; + + graph->setGraphRange(range, true); + + // Removes the graph from the graph list if it is closed manually + QObject::connect(graph, &VisualizationGraphWidget::destroyed, + [this, graph]() { m_CustomGraphs.removeAll(graph); }); + + delete context; // removes the connection + } + }, + Qt::QueuedConnection); + + QMetaObject::invokeMethod(&sqpApp->dataSourceController(), + "requestVariableFromProductIdKey", Qt::QueuedConnection, + Q_ARG(QString, productId)); } } @@ -256,7 +369,7 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) this->mapToGlobal(ui->btnChart->frameGeometry().center())) .value(0); - impl->updateForGraphMode(ui->treeView); + impl->updateForGraphMode(this); } }); @@ -268,7 +381,7 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) if (!events.isEmpty() && eventProducts.isEmpty()) { if (QMessageBox::warning(this, tr("Remove Event(s)"), - tr("The selected event(s) will be completly removed " + tr("The selected event(s) will be permanently removed " "from the repository!\nAre you sure you want to continue?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) { @@ -277,6 +390,8 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) sqpApp->catalogueController().removeEvent(event); impl->removeEvent(event, ui->treeView); } + + emit this->eventsRemoved(events); } } }); @@ -295,7 +410,7 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) impl->updateForTimeMode(ui->treeView); } else if (isNotMultiSelection && ui->btnChart->isChecked()) { - impl->updateForGraphMode(ui->treeView); + impl->updateForGraphMode(this); } QVector > events; @@ -398,6 +513,11 @@ bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr &eve return impl->m_Model->indexOf(event).isValid(); } +void CatalogueEventsWidget::refreshEvent(const std::shared_ptr &event) +{ + impl->m_Model->refreshEvent(event, true); +} + void CatalogueEventsWidget::populateWithCatalogues( const QVector > &catalogues) { diff --git a/gui/src/Catalogue/CatalogueExplorer.cpp b/gui/src/Catalogue/CatalogueExplorer.cpp index e730041..3ce453a 100644 --- a/gui/src/Catalogue/CatalogueExplorer.cpp +++ b/gui/src/Catalogue/CatalogueExplorer.cpp @@ -4,13 +4,22 @@ #include #include #include +#include +#include #include #include #include +#include + +#include struct CatalogueExplorer::CatalogueExplorerPrivate { CatalogueActionManager m_ActionManager; + std::unordered_map, QVector > + m_SelectionZonesPerEvents; + + QMetaObject::Connection m_Conn; CatalogueExplorerPrivate(CatalogueExplorer *catalogueExplorer) : m_ActionManager(catalogueExplorer) @@ -27,6 +36,7 @@ CatalogueExplorer::CatalogueExplorer(QWidget *parent) impl->m_ActionManager.installSelectionZoneActions(); + // Updates events and inspector when something is selected in the catalogue widget connect(ui->catalogues, &CatalogueSideBarWidget::catalogueSelected, [this](auto catalogues) { if (catalogues.count() == 1) { ui->inspector->setCatalogue(catalogues.first()); @@ -66,6 +76,7 @@ CatalogueExplorer::CatalogueExplorer(QWidget *parent) ui->events->clear(); }); + // Updates the inspectot when something is selected in the events connect(ui->events, &CatalogueEventsWidget::eventsSelected, [this](auto events) { if (events.count() == 1) { ui->inspector->setEvent(events.first()); @@ -88,6 +99,27 @@ CatalogueExplorer::CatalogueExplorer(QWidget *parent) connect(ui->events, &CatalogueEventsWidget::selectionCleared, [this]() { ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); }); + // Manage Selection Zones associated to events + connect(ui->events, &CatalogueEventsWidget::selectionZoneAdded, + [this](auto event, auto productId, auto zone) { + this->addSelectionZoneItem(event, productId, zone); + }); + + connect(ui->events, &CatalogueEventsWidget::eventsRemoved, [this](auto events) { + for (auto event : events) { + auto associatedSelectionZonesIt = impl->m_SelectionZonesPerEvents.find(event); + if (associatedSelectionZonesIt != impl->m_SelectionZonesPerEvents.cend()) { + for (auto selectionZone : associatedSelectionZonesIt->second) { + auto parentGraph = selectionZone->parentGraphWidget(); + parentGraph->removeSelectionZone(selectionZone); + } + + impl->m_SelectionZonesPerEvents.erase(event); + } + } + }); + + // Updates changes from the inspector connect(ui->inspector, &CatalogueInspectorWidget::catalogueUpdated, [this](auto catalogue) { sqpApp->catalogueController().updateCatalogue(catalogue); ui->catalogues->setCatalogueChanges(catalogue, true); @@ -107,6 +139,7 @@ CatalogueExplorer::CatalogueExplorer(QWidget *parent) CatalogueExplorer::~CatalogueExplorer() { + disconnect(impl->m_Conn); delete ui; } @@ -124,3 +157,37 @@ CatalogueSideBarWidget &CatalogueExplorer::sideBarWidget() const { return *ui->catalogues; } + +void CatalogueExplorer::clearSelectionZones() +{ + impl->m_SelectionZonesPerEvents.clear(); +} + +void CatalogueExplorer::addSelectionZoneItem(const std::shared_ptr &event, + const QString &productId, + VisualizationSelectionZoneItem *selectionZone) +{ + impl->m_SelectionZonesPerEvents[event] << selectionZone; + connect(selectionZone, &VisualizationSelectionZoneItem::rangeEdited, + [event, productId, this](auto range) { + auto productList = event->getEventProducts(); + for (auto &product : productList) { + if (product.getProductId() == productId) { + product.setTStart(range.m_TStart); + product.setTEnd(range.m_TEnd); + } + } + event->setEventProducts(productList); + sqpApp->catalogueController().updateEvent(event); + ui->events->refreshEvent(event); + ui->events->setEventChanges(event, true); + ui->inspector->refresh(); + }); + + impl->m_Conn = connect(selectionZone, &VisualizationSelectionZoneItem::destroyed, + [event, selectionZone, this]() { + if (!impl->m_SelectionZonesPerEvents.empty()) { + impl->m_SelectionZonesPerEvents[event].removeAll(selectionZone); + } + }); +} diff --git a/gui/src/Catalogue/CatalogueInspectorWidget.cpp b/gui/src/Catalogue/CatalogueInspectorWidget.cpp index 6ab4aa8..02c8616 100644 --- a/gui/src/Catalogue/CatalogueInspectorWidget.cpp +++ b/gui/src/Catalogue/CatalogueInspectorWidget.cpp @@ -209,3 +209,19 @@ void CatalogueInspectorWidget::setCatalogue(const std::shared_ptr & blockSignals(false); } + +void CatalogueInspectorWidget::refresh() +{ + switch (static_cast(ui->stackedWidget->currentIndex())) { + case Page::CatalogueProperties: + setCatalogue(impl->m_DisplayedCatalogue); + break; + case Page::EventProperties: { + auto isEventShowed = ui->leEventName->isEnabled(); + setEvent(impl->m_DisplayedEvent); + if (!isEventShowed && impl->m_DisplayedEvent) { + setEventProduct(impl->m_DisplayedEvent, impl->m_DisplayedEventProduct); + } + } + } +} diff --git a/gui/src/DragAndDrop/DragDropGuiController.cpp b/gui/src/DragAndDrop/DragDropGuiController.cpp index 1546c22..22f058b 100644 --- a/gui/src/DragAndDrop/DragDropGuiController.cpp +++ b/gui/src/DragAndDrop/DragDropGuiController.cpp @@ -7,6 +7,7 @@ #include "Visualization/VisualizationWidget.h" #include "Visualization/operations/FindVariableOperation.h" +#include "DataSource/DataSourceController.h" #include "Variable/Variable.h" #include "Variable/VariableController.h" @@ -286,11 +287,22 @@ bool DragDropGuiController::checkMimeDataForVisualization( // result = false: cannot drop multiple variables in the visualisation } } + else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) { + auto productDataList = sqpApp->dataSourceController().productsDataForMimeData( + mimeData->data(MIME_TYPE_PRODUCT_LIST)); + if (productDataList.count() == 1) { + result = true; + } + else { + // result = false: cannot drop multiple products in the visualisation + } + } else { // Other MIME data // no special rules, accepted by default result = true; } + return result; } diff --git a/gui/src/Visualization/AxisRenderingUtils.cpp b/gui/src/Visualization/AxisRenderingUtils.cpp index 33f47a6..7fa33f0 100644 --- a/gui/src/Visualization/AxisRenderingUtils.cpp +++ b/gui/src/Visualization/AxisRenderingUtils.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include @@ -68,11 +70,17 @@ void setAxisProperties(QCPAxis &axis, const Unit &unit, */ template struct AxisSetter { - static void setProperties(T &, QCustomPlot &, SqpColorScale &) + static void setProperties(QCustomPlot &, SqpColorScale &) { // Default implementation does nothing qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis properties: unmanaged type of data"; } + + static void setUnits(T &, QCustomPlot &, SqpColorScale &) + { + // Default implementation does nothing + qCCritical(LOG_AxisRenderingUtils()) << "Can't set axis units: unmanaged type of data"; + } }; /** @@ -83,7 +91,12 @@ struct AxisSetter { template struct AxisSetter::value or std::is_base_of::value> > { - static void setProperties(T &dataSeries, QCustomPlot &plot, SqpColorScale &) + static void setProperties(QCustomPlot &, SqpColorScale &) + { + // Nothing to do + } + + static void setUnits(T &dataSeries, QCustomPlot &plot, SqpColorScale &) { dataSeries.lockRead(); auto xAxisUnit = dataSeries.xAxisUnit(); @@ -101,17 +114,8 @@ struct AxisSetter: */ template struct AxisSetter::value> > { - static void setProperties(T &dataSeries, QCustomPlot &plot, SqpColorScale &colorScale) + static void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) { - dataSeries.lockRead(); - auto xAxisUnit = dataSeries.xAxisUnit(); - auto yAxisUnit = dataSeries.yAxisUnit(); - auto valuesUnit = dataSeries.valuesUnit(); - dataSeries.unlock(); - - setAxisProperties(*plot.xAxis, xAxisUnit); - setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic); - // Displays color scale in plot plot.plotLayout()->insertRow(0); plot.plotLayout()->addElement(0, 0, colorScale.m_Scale); @@ -125,9 +129,21 @@ struct AxisSetteraxis(), valuesUnit, QCPAxis::stLogarithmic); colorScale.m_AutomaticThreshold = true; } + + static void setUnits(T &dataSeries, QCustomPlot &plot, SqpColorScale &colorScale) + { + dataSeries.lockRead(); + auto xAxisUnit = dataSeries.xAxisUnit(); + auto yAxisUnit = dataSeries.yAxisUnit(); + auto valuesUnit = dataSeries.valuesUnit(); + dataSeries.unlock(); + + setAxisProperties(*plot.xAxis, xAxisUnit); + setAxisProperties(*plot.yAxis, yAxisUnit, QCPAxis::stLogarithmic); + setAxisProperties(*colorScale.m_Scale->axis(), valuesUnit, QCPAxis::stLogarithmic); + } }; /** @@ -136,14 +152,25 @@ struct AxisSetter struct AxisHelper : public IAxisHelper { - explicit AxisHelper(T &dataSeries) : m_DataSeries{dataSeries} {} + explicit AxisHelper(std::shared_ptr dataSeries) : m_DataSeries{dataSeries} {} void setProperties(QCustomPlot &plot, SqpColorScale &colorScale) override { - AxisSetter::setProperties(m_DataSeries, plot, colorScale); + AxisSetter::setProperties(plot, colorScale); } - T &m_DataSeries; + void setUnits(QCustomPlot &plot, SqpColorScale &colorScale) override + { + if (m_DataSeries) { + AxisSetter::setUnits(*m_DataSeries, plot, colorScale); + } + else { + qCCritical(LOG_AxisRenderingUtils()) << "Can't set units: inconsistency between the " + "type of data series and the type supposed"; + } + } + + std::shared_ptr m_DataSeries; }; } // namespace @@ -159,19 +186,22 @@ QString formatValue(double value, const QCPAxis &axis) } } -std::unique_ptr -IAxisHelperFactory::create(std::shared_ptr dataSeries) noexcept +std::unique_ptr IAxisHelperFactory::create(const Variable &variable) noexcept { - if (auto scalarSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*scalarSeries); - } - else if (auto spectrogramSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*spectrogramSeries); - } - else if (auto vectorSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*vectorSeries); - } - else { - return std::make_unique >(*dataSeries); + switch (variable.type()) { + case DataSeriesType::SCALAR: + return std::make_unique >( + std::dynamic_pointer_cast(variable.dataSeries())); + case DataSeriesType::SPECTROGRAM: + return std::make_unique >( + std::dynamic_pointer_cast(variable.dataSeries())); + case DataSeriesType::VECTOR: + return std::make_unique >( + std::dynamic_pointer_cast(variable.dataSeries())); + default: + // Creates default helper + break; } + + return std::make_unique >(nullptr); } diff --git a/gui/src/Visualization/PlottablesRenderingUtils.cpp b/gui/src/Visualization/PlottablesRenderingUtils.cpp index e326eb1..b73fa91 100644 --- a/gui/src/Visualization/PlottablesRenderingUtils.cpp +++ b/gui/src/Visualization/PlottablesRenderingUtils.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include Q_LOGGING_CATEGORY(LOG_PlottablesRenderingUtils, "PlottablesRenderingUtils") @@ -17,7 +19,7 @@ namespace { */ template struct PlottablesSetter { - static void setProperties(T &, PlottablesMap &) + static void setProperties(PlottablesMap &) { // Default implementation does nothing qCCritical(LOG_PlottablesRenderingUtils()) @@ -33,20 +35,25 @@ struct PlottablesSetter { template struct PlottablesSetter::value or std::is_base_of::value> > { - static void setProperties(T &dataSeries, PlottablesMap &plottables) + static void setProperties(PlottablesMap &plottables) { - // Gets the number of components of the data series - dataSeries.lockRead(); - auto componentCount = dataSeries.valuesData()->componentCount(); - dataSeries.unlock(); + // Finds the plottable with the highest index to determine the number of colors to generate + auto end = plottables.cend(); + auto maxPlottableIndexIt + = std::max_element(plottables.cbegin(), end, [](const auto &it1, const auto &it2) { + return it1.first < it2.first; + }); + auto componentCount = maxPlottableIndexIt != end ? maxPlottableIndexIt->first + 1 : 0; // Generates colors for each component auto colors = ColorUtils::colors(Qt::blue, Qt::red, componentCount); // For each component of the data series, creates a QCPGraph to add to the plot for (auto i = 0; i < componentCount; ++i) { - auto graph = plottables.at(i); - graph->setPen(QPen{colors.at(i)}); + auto graphIt = plottables.find(i); + if (graphIt != end) { + graphIt->second->setPen(QPen{colors.at(i)}); + } } } }; @@ -58,7 +65,7 @@ struct PlottablesSetter struct PlottablesSetter::value> > { - static void setProperties(T &, PlottablesMap &plottables) + static void setProperties(PlottablesMap &plottables) { // Checks that for a spectrogram there is only one plottable, that is a colormap if (plottables.size() != 1) { @@ -92,31 +99,28 @@ struct PlottablesSetter struct PlottablesHelper : public IPlottablesHelper { - explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {} - void setProperties(PlottablesMap &plottables) override { - PlottablesSetter::setProperties(m_DataSeries, plottables); + PlottablesSetter::setProperties(plottables); } - - T &m_DataSeries; }; } // namespace std::unique_ptr -IPlottablesHelperFactory::create(std::shared_ptr dataSeries) noexcept +IPlottablesHelperFactory::create(const Variable &variable) noexcept { - if (auto scalarSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*scalarSeries); - } - else if (auto spectrogramSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*spectrogramSeries); - } - else if (auto vectorSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*vectorSeries); - } - else { - return std::make_unique >(*dataSeries); + switch (variable.type()) { + case DataSeriesType::SCALAR: + return std::make_unique >(); + case DataSeriesType::SPECTROGRAM: + return std::make_unique >(); + case DataSeriesType::VECTOR: + return std::make_unique >(); + default: + // Returns default helper + break; } + + return std::make_unique >(); } diff --git a/gui/src/Visualization/VisualizationGraphHelper.cpp b/gui/src/Visualization/VisualizationGraphHelper.cpp index 363a7c0..bd0c047 100644 --- a/gui/src/Visualization/VisualizationGraphHelper.cpp +++ b/gui/src/Visualization/VisualizationGraphHelper.cpp @@ -25,7 +25,7 @@ public: */ template struct PlottablesCreator { - static PlottablesMap createPlottables(T &, QCustomPlot &) + static PlottablesMap createPlottables(QCustomPlot &) { qCCritical(LOG_DataSeries()) << QObject::tr("Can't create plottables: unmanaged data series type"); @@ -33,34 +33,37 @@ struct PlottablesCreator { } }; +PlottablesMap createGraphs(QCustomPlot &plot, int nbGraphs) +{ + PlottablesMap result{}; + + // Creates {nbGraphs} QCPGraph to add to the plot + for (auto i = 0; i < nbGraphs; ++i) { + auto graph = plot.addGraph(); + result.insert({i, graph}); + } + + plot.replot(); + + return result; +} + /** - * Specialization of PlottablesCreator for scalars and vectors + * Specialization of PlottablesCreator for scalars * @sa ScalarSeries - * @sa VectorSeries */ template -struct PlottablesCreator::value - or std::is_base_of::value> > { - static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot) - { - PlottablesMap result{}; - - // Gets the number of components of the data series - dataSeries.lockRead(); - auto componentCount = dataSeries.valuesData()->componentCount(); - dataSeries.unlock(); - - // For each component of the data series, creates a QCPGraph to add to the plot - for (auto i = 0; i < componentCount; ++i) { - auto graph = plot.addGraph(); - result.insert({i, graph}); - } - - plot.replot(); +struct PlottablesCreator::value> > { + static PlottablesMap createPlottables(QCustomPlot &plot) { return createGraphs(plot, 1); } +}; - return result; - } +/** + * Specialization of PlottablesCreator for vectors + * @sa VectorSeries + */ +template +struct PlottablesCreator::value> > { + static PlottablesMap createPlottables(QCustomPlot &plot) { return createGraphs(plot, 3); } }; /** @@ -70,7 +73,7 @@ struct PlottablesCreator struct PlottablesCreator::value> > { - static PlottablesMap createPlottables(T &dataSeries, QCustomPlot &plot) + static PlottablesMap createPlottables(QCustomPlot &plot) { PlottablesMap result{}; result.insert({0, new QCPColorMap{plot.xAxis, plot.yAxis}}); @@ -264,41 +267,59 @@ struct IPlottablesHelper { */ template struct PlottablesHelper : public IPlottablesHelper { - explicit PlottablesHelper(T &dataSeries) : m_DataSeries{dataSeries} {} + explicit PlottablesHelper(std::shared_ptr dataSeries) : m_DataSeries{dataSeries} {} PlottablesMap create(QCustomPlot &plot) const override { - return PlottablesCreator::createPlottables(m_DataSeries, plot); + return PlottablesCreator::createPlottables(plot); } void update(PlottablesMap &plottables, const SqpRange &range, bool rescaleAxes) const override { - PlottablesUpdater::updatePlottables(m_DataSeries, plottables, range, rescaleAxes); + if (m_DataSeries) { + PlottablesUpdater::updatePlottables(*m_DataSeries, plottables, range, rescaleAxes); + } + else { + qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency " + "between the type of data series and the " + "type supposed"; + } } void setYAxisRange(const SqpRange &xAxisRange, QCustomPlot &plot) const override { - return PlottablesUpdater::setPlotYAxisRange(m_DataSeries, xAxisRange, plot); + if (m_DataSeries) { + PlottablesUpdater::setPlotYAxisRange(*m_DataSeries, xAxisRange, plot); + } + else { + qCCritical(LOG_VisualizationGraphHelper()) << "Can't update plottables: inconsistency " + "between the type of data series and the " + "type supposed"; + } } - T &m_DataSeries; + std::shared_ptr m_DataSeries; }; -/// Creates IPlottablesHelper according to a data series -std::unique_ptr createHelper(std::shared_ptr dataSeries) noexcept +/// Creates IPlottablesHelper according to the type of data series a variable holds +std::unique_ptr createHelper(std::shared_ptr variable) noexcept { - if (auto scalarSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*scalarSeries); - } - else if (auto spectrogramSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*spectrogramSeries); - } - else if (auto vectorSeries = std::dynamic_pointer_cast(dataSeries)) { - return std::make_unique >(*vectorSeries); - } - else { - return std::make_unique >(*dataSeries); + switch (variable->type()) { + case DataSeriesType::SCALAR: + return std::make_unique >( + std::dynamic_pointer_cast(variable->dataSeries())); + case DataSeriesType::SPECTROGRAM: + return std::make_unique >( + std::dynamic_pointer_cast(variable->dataSeries())); + case DataSeriesType::VECTOR: + return std::make_unique >( + std::dynamic_pointer_cast(variable->dataSeries())); + default: + // Creates default helper + break; } + + return std::make_unique >(nullptr); } } // namespace @@ -307,7 +328,7 @@ PlottablesMap VisualizationGraphHelper::create(std::shared_ptr variabl QCustomPlot &plot) noexcept { if (variable) { - auto helper = createHelper(variable->dataSeries()); + auto helper = createHelper(variable); auto plottables = helper->create(plot); return plottables; } @@ -322,7 +343,7 @@ void VisualizationGraphHelper::setYAxisRange(std::shared_ptr variable, QCustomPlot &plot) noexcept { if (variable) { - auto helper = createHelper(variable->dataSeries()); + auto helper = createHelper(variable); helper->setYAxisRange(variable->range(), plot); } else { @@ -332,9 +353,9 @@ void VisualizationGraphHelper::setYAxisRange(std::shared_ptr variable, } void VisualizationGraphHelper::updateData(PlottablesMap &plottables, - std::shared_ptr dataSeries, + std::shared_ptr variable, const SqpRange &dateTime) { - auto helper = createHelper(dataSeries); + auto helper = createHelper(variable); helper->update(plottables, dateTime); } diff --git a/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp b/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp index 30747c3..7764aa2 100644 --- a/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp +++ b/gui/src/Visualization/VisualizationGraphRenderingDelegate.cpp @@ -9,6 +9,7 @@ #include #include +#include #include @@ -302,14 +303,14 @@ void VisualizationGraphRenderingDelegate::onPlotUpdated() noexcept impl->m_Plot.replot(); } -void VisualizationGraphRenderingDelegate::setAxesProperties( - std::shared_ptr dataSeries) noexcept +void VisualizationGraphRenderingDelegate::setAxesUnits(const Variable &variable) noexcept { - // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected - impl->m_XAxisLabel = dataSeries->xAxisUnit().m_Name; - auto axisHelper = IAxisHelperFactory::create(dataSeries); - axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale); + auto axisHelper = IAxisHelperFactory::create(variable); + axisHelper->setUnits(impl->m_Plot, impl->m_ColorScale); + + // Stores x-axis label to be able to retrieve it when x-axis pixmap is unselected + impl->m_XAxisLabel = impl->m_Plot.xAxis->label(); // Updates x-axis state impl->updateXAxisState(); @@ -317,10 +318,15 @@ void VisualizationGraphRenderingDelegate::setAxesProperties( impl->m_Plot.layer(AXES_LAYER)->replot(); } -void VisualizationGraphRenderingDelegate::setPlottablesProperties( - std::shared_ptr dataSeries, PlottablesMap &plottables) noexcept +void VisualizationGraphRenderingDelegate::setGraphProperties(const Variable &variable, + PlottablesMap &plottables) noexcept { - auto plottablesHelper = IPlottablesHelperFactory::create(dataSeries); + // Axes' properties + auto axisHelper = IAxisHelperFactory::create(variable); + axisHelper->setProperties(impl->m_Plot, impl->m_ColorScale); + + // Plottables' properties + auto plottablesHelper = IPlottablesHelperFactory::create(variable); plottablesHelper->setProperties(plottables); } diff --git a/gui/src/Visualization/VisualizationGraphWidget.cpp b/gui/src/Visualization/VisualizationGraphWidget.cpp index 85c81ff..204224e 100644 --- a/gui/src/Visualization/VisualizationGraphWidget.cpp +++ b/gui/src/Visualization/VisualizationGraphWidget.cpp @@ -65,10 +65,10 @@ struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { { } - void updateData(PlottablesMap &plottables, std::shared_ptr dataSeries, + void updateData(PlottablesMap &plottables, std::shared_ptr variable, const SqpRange &range) { - VisualizationGraphHelper::updateData(plottables, dataSeries, range); + VisualizationGraphHelper::updateData(plottables, variable, range); // Prevents that data has changed to update rendering m_RenderingDelegate->onPlotUpdated(); @@ -94,6 +94,8 @@ struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate { bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even + bool m_VariableAutoRangeOnInit = true; + void startDrawingRect(const QPoint &pos, QCustomPlot &plot) { removeDrawingRect(plot); @@ -294,28 +296,48 @@ void VisualizationGraphWidget::setFlags(GraphFlags flags) void VisualizationGraphWidget::addVariable(std::shared_ptr variable, SqpRange range) { - // Uses delegate to create the qcpplot components according to the variable - auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget); - - if (auto dataSeries = variable->dataSeries()) { - // Set axes properties according to the units of the data series - impl->m_RenderingDelegate->setAxesProperties(dataSeries); + /// Lambda used to set graph's units and range according to the variable passed in parameter + auto loadRange = [this](std::shared_ptr variable, const SqpRange &range) { + impl->m_RenderingDelegate->setAxesUnits(*variable); - // Sets rendering properties for the new plottables - // Warning: this method must be called after setAxesProperties(), as it can access to some - // axes properties that have to be initialized - impl->m_RenderingDelegate->setPlottablesProperties(dataSeries, createdPlottables); - } + this->setFlags(GraphFlag::DisableAll); + setGraphRange(range); + this->setFlags(GraphFlag::EnableAll); - impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)}); + emit requestDataLoading({variable}, range, false); + }; connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated())); - this->setFlags(GraphFlag::DisableAll); - this->setGraphRange(range); - this->setFlags(GraphFlag::EnableAll); + // Calls update of graph's range and units when the data of the variable have been initialized. + // Note: we use QueuedConnection here as the update event must be called in the UI thread + connect(variable.get(), &Variable::dataInitialized, this, + [ varW = std::weak_ptr{variable}, range, loadRange, this ]() { + if (auto var = varW.lock()) { + // If the variable is the first added in the graph, we load its range + auto firstVariableInGraph = range == INVALID_RANGE; + auto loadedRange = graphRange(); + if (impl->m_VariableAutoRangeOnInit) { + loadedRange = firstVariableInGraph ? var->range() : range; + } + loadRange(var, loadedRange); + setYRange(var); + } + }, + Qt::QueuedConnection); + + // Uses delegate to create the qcpplot components according to the variable + auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget); + + // Sets graph properties + impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables); + + impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)}); - emit requestDataLoading(QVector >() << variable, range, false); + // If the variable already has its data loaded, load its units and its range in the graph + if (variable->dataSeries() != nullptr) { + loadRange(variable, range); + } emit variableAdded(variable); } @@ -371,14 +393,29 @@ SqpRange VisualizationGraphWidget::graphRange() const noexcept return SqpRange{graphRange.lower, graphRange.upper}; } -void VisualizationGraphWidget::setGraphRange(const SqpRange &range) +void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration) { qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START"); + + if (calibration) { + impl->m_IsCalibration = true; + } + ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd); ui->widget->replot(); + + if (calibration) { + impl->m_IsCalibration = false; + } + qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END"); } +void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value) +{ + impl->m_VariableAutoRangeOnInit = value; +} + QVector VisualizationGraphWidget::selectionZoneRanges() const { QVector ranges; @@ -401,6 +438,20 @@ void VisualizationGraphWidget::addSelectionZones(const QVector &ranges plot().replot(QCustomPlot::rpQueuedReplot); } +VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name, + const SqpRange &range) +{ + // note: ownership is transfered to QCustomPlot + auto zone = new VisualizationSelectionZoneItem(&plot()); + zone->setName(name); + zone->setRange(range.m_TStart, range.m_TEnd); + impl->addSelectionZone(zone); + + plot().replot(QCustomPlot::rpQueuedReplot); + + return zone; +} + void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone) { parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false); @@ -989,7 +1040,7 @@ void VisualizationGraphWidget::onDataCacheVariableUpdated() qCDebug(LOG_VisualizationGraphWidget()) << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime; if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) { - impl->updateData(variableEntry.second, variable->dataSeries(), variable->range()); + impl->updateData(variableEntry.second, variable, variable->range()); } } } @@ -999,6 +1050,6 @@ void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr v { auto it = impl->m_VariableToPlotMultiMap.find(variable); if (it != impl->m_VariableToPlotMultiMap.end()) { - impl->updateData(it->second, variable->dataSeries(), range); + impl->updateData(it->second, variable, range); } } diff --git a/gui/src/Visualization/VisualizationSelectionZoneItem.cpp b/gui/src/Visualization/VisualizationSelectionZoneItem.cpp index 2e5c5b2..4e51bd9 100644 --- a/gui/src/Visualization/VisualizationSelectionZoneItem.cpp +++ b/gui/src/Visualization/VisualizationSelectionZoneItem.cpp @@ -388,8 +388,11 @@ void VisualizationSelectionZoneItem::mouseMoveEvent(QMouseEvent *event, const QP break; } + emit rangeEdited(range()); + for (auto associatedZone : impl->m_AssociatedEditedZones) { associatedZone->parentPlot()->replot(); + emit associatedZone->rangeEdited(associatedZone->range()); } } else { diff --git a/gui/src/Visualization/VisualizationTabWidget.cpp b/gui/src/Visualization/VisualizationTabWidget.cpp index 957de0e..383f384 100644 --- a/gui/src/Visualization/VisualizationTabWidget.cpp +++ b/gui/src/Visualization/VisualizationTabWidget.cpp @@ -7,6 +7,7 @@ #include "Visualization/MacScrollBarStyle.h" +#include "DataSource/DataSourceController.h" #include "Variable/VariableController.h" #include "Common/MimeTypesDef.h" @@ -69,6 +70,8 @@ struct VisualizationTabWidget::VisualizationTabWidgetPrivate { void dropZone(int index, VisualizationTabWidget *tabWidget); void dropVariables(const QList > &variables, int index, VisualizationTabWidget *tabWidget); + void dropProducts(const QVariantList &productsMetaData, int index, + VisualizationTabWidget *tabWidget); }; VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *parent) @@ -91,6 +94,8 @@ VisualizationTabWidget::VisualizationTabWidget(const QString &name, QWidget *par VisualizationDragDropContainer::DropBehavior::Inserted); ui->dragDropContainer->setMimeType(MIME_TYPE_VARIABLE_LIST, VisualizationDragDropContainer::DropBehavior::Inserted); + ui->dragDropContainer->setMimeType(MIME_TYPE_PRODUCT_LIST, + VisualizationDragDropContainer::DropBehavior::Inserted); ui->dragDropContainer->setAcceptMimeDataFunction([this](auto mimeData) { return sqpApp->dragDropGuiController().checkMimeDataForVisualization(mimeData, @@ -229,6 +234,11 @@ void VisualizationTabWidget::dropMimeData(int index, const QMimeData *mimeData) mimeData->data(MIME_TYPE_VARIABLE_LIST)); impl->dropVariables(variables, index, this); } + else if (mimeData->hasFormat(MIME_TYPE_PRODUCT_LIST)) { + auto productsData = sqpApp->dataSourceController().productsDataForMimeData( + mimeData->data(MIME_TYPE_PRODUCT_LIST)); + impl->dropProducts(productsData, index, this); + } else { qCWarning(LOG_VisualizationZoneWidget()) << tr("VisualizationTabWidget::dropMimeData, unknown MIME data received."); @@ -352,3 +362,28 @@ void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropVariables( tabWidget->createZone(variables, index); } + +void VisualizationTabWidget::VisualizationTabWidgetPrivate::dropProducts( + const QVariantList &productsMetaData, int index, VisualizationTabWidget *tabWidget) +{ + // Note: the AcceptMimeDataFunction (set on the drop container) ensure there is a single and + // compatible variable here + if (productsMetaData.count() != 1) { + qCWarning(LOG_VisualizationZoneWidget()) + << tr("VisualizationTabWidget::dropProducts, dropping multiple products, operation " + "aborted."); + return; + } + + auto context = new QObject{tabWidget}; + connect(&sqpApp->variableController(), &VariableController::variableAdded, context, + [this, index, tabWidget, context](auto variable) { + tabWidget->createZone({variable}, index); + delete context; // removes the connection + }, + Qt::QueuedConnection); + + auto productData = productsMetaData.first().toHash(); + QMetaObject::invokeMethod(&sqpApp->dataSourceController(), "requestVariable", + Qt::QueuedConnection, Q_ARG(QVariantHash, productData)); +} diff --git a/gui/src/Visualization/VisualizationZoneWidget.cpp b/gui/src/Visualization/VisualizationZoneWidget.cpp index eb1222c..e7229cf 100644 --- a/gui/src/Visualization/VisualizationZoneWidget.cpp +++ b/gui/src/Visualization/VisualizationZoneWidget.cpp @@ -10,6 +10,7 @@ #include "Common/VisualizationDef.h" #include +#include #include