From 63482592b526a01fde51c27875a037fdbb3ec6aa Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 17 Jan 2025 14:57:09 +0000 Subject: [PATCH 01/24] Add plot as a submodule. --- .gitmodules | 3 +++ frontend/CMakeLists.txt | 6 ++++-- frontend/plot | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .gitmodules create mode 160000 frontend/plot diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..fb25c728 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "frontend/plot"] + path = frontend/plot + url = https://github.com/disorderedmaterials/plot diff --git a/frontend/CMakeLists.txt b/frontend/CMakeLists.txt index c12a0646..aaa9796b 100644 --- a/frontend/CMakeLists.txt +++ b/frontend/CMakeLists.txt @@ -33,6 +33,8 @@ find_package( COMPONENTS Core Gui Widgets Network Charts Xml REQUIRED) +add_subdirectory(plot/) + # Build main binary qt6_add_executable( jv2 @@ -108,10 +110,10 @@ qt6_add_executable( set_target_properties(jv2 PROPERTIES WIN32_EXECUTABLE ON) target_link_libraries(jv2 PRIVATE Qt6::Core Qt6::Widgets Qt6::Network - Qt6::Charts Qt6::Xml) + Qt6::Charts Qt6::Xml mildred) target_include_directories( jv2 PRIVATE ${PROJECT_SOURCE_DIR} ${Qt6Widgets_INCLUDE_DIRS} ${Qt6Xml_INCLUDE_DIRS} ${Qt6Network_INCLUDE_DIRS} - ${Qt6Charts_INCLUDE_DIRS}) + ${Qt6Charts_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/plot/src) diff --git a/frontend/plot b/frontend/plot new file mode 160000 index 00000000..a4661f0f --- /dev/null +++ b/frontend/plot @@ -0,0 +1 @@ +Subproject commit a4661f0fa266fcd116a020f959bff645e7f3e1f1 From ace95114a4bb6ed2f80863dafbca60071f17d156 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 20 Jan 2025 09:09:10 +0000 Subject: [PATCH 02/24] Start adding new plot widget. --- frontend/CMakeLists.txt | 3 ++ frontend/data.cpp | 5 +- frontend/plotLogDataWidget.cpp | 55 ++++++++++++++++++++++ frontend/plotLogDataWidget.h | 41 ++++++++++++++++ frontend/plotLogDataWidget.ui | 85 ++++++++++++++++++++++++++++++++++ 5 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 frontend/plotLogDataWidget.cpp create mode 100644 frontend/plotLogDataWidget.h create mode 100644 frontend/plotLogDataWidget.ui diff --git a/frontend/CMakeLists.txt b/frontend/CMakeLists.txt index aaa9796b..74c6a292 100644 --- a/frontend/CMakeLists.txt +++ b/frontend/CMakeLists.txt @@ -97,6 +97,9 @@ qt6_add_executable( journalSourcesDialog.cpp journalSourcesDialog.h journalSourcesDialog.ui + plotLogDataWidget.cpp + plotLogDataWidget.h + plotLogDataWidget.ui searchDialog.cpp searchDialog.h searchDialog.ui diff --git a/frontend/data.cpp b/frontend/data.cpp index 1ee29980..5c38be89 100644 --- a/frontend/data.cpp +++ b/frontend/data.cpp @@ -2,6 +2,7 @@ // Copyright (c) 2025 Team JournalViewer and contributors #include "mainWindow.h" +#include "plotLogDataWidget.h" #include #include #include @@ -200,8 +201,8 @@ void MainWindow::runDataContextMenuRequested(QPoint pos) } else if (selectedAction == plotSELog) { - backend_.getNexusFields(currentJournalSource(), selectedRunNumbers(), - [=](HttpRequestWorker *worker) { handlePlotSELogValue(worker); }); + auto *plot = new PlotLogDataWidget(this, backend_, currentJournalSource(), selectedRunNumbers()); + ui_.MainTabs->addTab(plot, "Test"); } else if (selectedAction == plotDetector) { diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp new file mode 100644 index 00000000..096cc760 --- /dev/null +++ b/frontend/plotLogDataWidget.cpp @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 Team JournalViewer and contributors + +#include "plotLogDataWidget.h" +#include + +PlotLogDataWidget::PlotLogDataWidget(QWidget *parent, Backend &backend, const JournalSource* source, const std::vector &runNumbers) : QWidget(parent), backend_(backend), source_(source), runNumbers_(runNumbers) +{ + ui_.setupUi(this); + +// propertyModel_.setRootItem(rootItem); + ui_.PropertyTree->setModel(&propertyModel_); + ui_.PropertyTree->expandAll(); + ui_.PropertyTree->resizeColumnToContents(0); + ui_.PropertyTree->resizeColumnToContents(1); + ui_.PropertyTree->setSelectionBehavior(QAbstractItemView::SelectRows); + + connect(ui_.PropertyTree->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, + SLOT(onTreeSelectionChanged(const QItemSelection &, const QItemSelection &))); + + // Acquire the available log data + backend_.getNexusFields(source_, runNumbers_, + [=](HttpRequestWorker *worker) { handleRetrieveSELogProperties(worker); }); +} + +void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker* worker) +{ // Check for errors + if (handleRequestError(worker, "retrieving log values from run") != NoError) + return; + + // Iterate over logs extracted from the target run data and construct our mapped values + + auto *rootItem = new GenericTreeItem({"Log Value", "Full Path"}); + foreach (const auto &log, worker->jsonResponse().array()) + { + auto logArray = log.toArray(); + if (logArray.size() < 2) + continue; + + // First item in the array is the name of the log value set / section + auto *sectionItem = rootItem->appendChild({logArray.first().toString(), ""}); + + // Remove the name item and proceed to iterate over log values + logArray.removeFirst(); + + auto logArrayVar = logArray.toVariantList(); + std::sort(logArrayVar.begin(), logArrayVar.end(), + [](QVariant &v1, QVariant &v2) { return v1.toString() < v2.toString(); }); + + foreach (const auto &block, logArrayVar) + sectionItem->appendChild({block.toString().split("/").last(), block.toString()}); + } +} + +PlotLogDataWidget::~PlotLogDataWidget() {} diff --git a/frontend/plotLogDataWidget.h b/frontend/plotLogDataWidget.h new file mode 100644 index 00000000..501e8ec7 --- /dev/null +++ b/frontend/plotLogDataWidget.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 Team JournalViewer and contributors + +#pragma once + +#include "ui_plotLogDataWidget.h" +#include "backend.h" +#include "genericTreeModel.h" +#include + +// Forward Declarations +class JournalSource; + +class PlotLogDataWidget : public QWidget +{ + Q_OBJECT + + public: + PlotLogDataWidget(QWidget *parent, Backend &backend, const JournalSource* source, const std::vector &runNumbers); + ~PlotLogDataWidget(); + + private: + // User interface object + Ui::PlotLogDataWidget ui_; + // Tree model for properties + GenericTreeModel propertyModel_; + // Main backend + Backend &backend_; + // Journal source from which the run numbers came + const JournalSource* source_{nullptr}; + // Run numbers to display on the plot + std::vector runNumbers_; + + private: + // Handle retrieved log properties data + void handleRetrieveSELogProperties(HttpRequestWorker* worker); + + private slots: + + signals: +}; diff --git a/frontend/plotLogDataWidget.ui b/frontend/plotLogDataWidget.ui new file mode 100644 index 00000000..60493dad --- /dev/null +++ b/frontend/plotLogDataWidget.ui @@ -0,0 +1,85 @@ + + + PlotLogDataWidget + + + true + + + + 0 + 0 + 1086 + 586 + + + + + + + Qt::Horizontal + + + + + 10 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + + + 0 + 0 + + + + + + + + 0 + 1 + + + + + + + + + 0 + 3 + + + + + + + + + + + + + Mildred::MildredWidget + QWidget +
plot/src/widget.h
+ 1 +
+
+ + +
From fbaf2054606b1e832d6beb40634a472b1436c568 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 20 Jan 2025 11:03:39 +0000 Subject: [PATCH 03/24] Namespace everything. --- frontend/args.cpp | 3 +++ frontend/args.h | 3 +++ frontend/backend.cpp | 3 +++ frontend/backend.h | 18 ++++++++++++++++-- frontend/chartView.cpp | 3 +++ frontend/chartView.h | 3 +++ frontend/data.cpp | 3 +++ frontend/errorHandling.cpp | 3 +++ frontend/export.cpp | 3 +++ frontend/filtering.cpp | 3 +++ frontend/finding.cpp | 3 +++ frontend/generation.cpp | 3 +++ frontend/genericTreeModel.cpp | 3 +++ frontend/genericTreeModel.h | 3 +++ frontend/graphWidget.cpp | 3 +++ frontend/graphWidget.h | 3 +++ frontend/graphWidget.ui | 12 ++++-------- frontend/httpRequestWorker.cpp | 3 +++ frontend/httpRequestWorker.h | 3 +++ frontend/instrument.cpp | 3 +++ frontend/instrument.h | 3 +++ frontend/instrumentModel.cpp | 3 +++ frontend/instrumentModel.h | 3 +++ frontend/instruments.cpp | 3 +++ frontend/journal.cpp | 3 +++ frontend/journal.h | 4 ++++ frontend/journalModel.cpp | 3 +++ frontend/journalModel.h | 3 +++ frontend/journalSource.cpp | 3 +++ frontend/journalSource.h | 3 +++ frontend/journalSourceFilterProxy.cpp | 3 +++ frontend/journalSourceFilterProxy.h | 5 ++++- frontend/journalSourceModel.cpp | 3 +++ frontend/journalSourceModel.h | 3 +++ frontend/journalSources.cpp | 3 +++ frontend/journalSourcesDialog.cpp | 3 +++ frontend/journalSourcesDialog.h | 3 +++ frontend/lock.cpp | 3 +++ frontend/lock.h | 3 +++ frontend/main.cpp | 4 ++-- frontend/mainWindow.cpp | 3 +++ frontend/mainWindow.h | 9 +++++++-- frontend/nexusInteraction.cpp | 3 +++ frontend/optionalRef.h | 3 +++ frontend/plotLogDataWidget.cpp | 21 +++++++++++++-------- frontend/plotLogDataWidget.h | 14 ++++++++++---- frontend/runDataFilterProxy.cpp | 3 +++ frontend/runDataFilterProxy.h | 5 ++++- frontend/runDataModel.cpp | 3 +++ frontend/runDataModel.h | 3 +++ frontend/seLogChooserDialog.cpp | 3 +++ frontend/seLogChooserDialog.h | 3 +++ frontend/searchDialog.cpp | 3 +++ frontend/searchDialog.h | 3 +++ frontend/searching.cpp | 3 +++ frontend/settings.cpp | 3 +++ frontend/uniqueName.h | 3 +++ frontend/version.h | 3 +++ frontend/visualisation.cpp | 3 +++ 59 files changed, 214 insertions(+), 28 deletions(-) diff --git a/frontend/args.cpp b/frontend/args.cpp index b7621703..b419677b 100644 --- a/frontend/args.cpp +++ b/frontend/args.cpp @@ -3,6 +3,8 @@ #include "args.h" +namespace JV2 +{ CLIArgs::CLIArgs() : helpOption_(addHelpOption()) { setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); @@ -34,3 +36,4 @@ bool CLIArgs::parseArguments(const QList &arguments) return true; } +} // namespace JV2 diff --git a/frontend/args.h b/frontend/args.h index a959c465..832632fd 100644 --- a/frontend/args.h +++ b/frontend/args.h @@ -7,6 +7,8 @@ #include #include +namespace JV2 +{ class CLIArgs : public QCommandLineParser { public: @@ -31,3 +33,4 @@ class CLIArgs : public QCommandLineParser const inline static QString UseWaitress = QStringLiteral("use-waitress"); const inline static QString DebugBackend = QStringLiteral("debug-backend"); }; +} // namespace JV2 diff --git a/frontend/backend.cpp b/frontend/backend.cpp index 1b11fe86..ff02bf3f 100644 --- a/frontend/backend.cpp +++ b/frontend/backend.cpp @@ -8,6 +8,8 @@ #include #include +namespace JV2 +{ Backend::Backend(const QCommandLineParser &args) : process_() { QStringList backendArgs; @@ -301,3 +303,4 @@ void Backend::generateFinalise(const JournalSource *source, JournalGenerationSty postRequest(createRoute("generate/finalise"), data, handler); } +} // namespace JV2 diff --git a/frontend/backend.h b/frontend/backend.h index a1af2293..95c8d7f8 100644 --- a/frontend/backend.h +++ b/frontend/backend.h @@ -4,13 +4,15 @@ #pragma once #include "httpRequestWorker.h" +#include #include #include #include +namespace JV2 +{ // Forward-declarations class JournalSource; -class QCommandLineParser; // Backend Process class Backend : public QObject @@ -44,10 +46,21 @@ class Backend : public QObject // Create a request HttpRequestWorker *createRequest(const QString &url, const HttpRequestWorker::HttpRequestHandler &handler = {}); + public: + // Error Codes + const inline static QString NoError = QStringLiteral("NoError"); + const inline static QString QNetworkReplyError = QStringLiteral("QNetworkReplyError"); + const inline static QString InvalidRequestError = QStringLiteral("InvalidRequestError"); + const inline static QString NetworkError = QStringLiteral("NetworkError"); + const inline static QString XMLParseError = QStringLiteral("XMLParseError"); + const inline static QString CollectionNotFoundError = QStringLiteral("CollectionNotFoundError"); + const inline static QString JournalNotFoundError = QStringLiteral("JournalNotFoundError"); + const inline static QString FileNotFoundError = QStringLiteral("FileNotFoundError"); + public slots: // Start the backend process void start(); - // Stop the backend processs + // Stop the backend process void stop(); signals: @@ -129,3 +142,4 @@ class Backend : public QObject void generateFinalise(const JournalSource *source, JournalGenerationStyle generationStyle, const HttpRequestWorker::HttpRequestHandler &handler = {}); }; +} // namespace JV2 diff --git a/frontend/chartView.cpp b/frontend/chartView.cpp index 37faac8e..ffeedcc6 100644 --- a/frontend/chartView.cpp +++ b/frontend/chartView.cpp @@ -14,6 +14,8 @@ #include #include +namespace JV2 +{ ChartView::ChartView(QChart *chart, QWidget *parent) : QChartView(chart, parent) { setRubberBand(QChartView::HorizontalRubberBand); @@ -317,3 +319,4 @@ void ChartView::mouseMoveEvent(QMouseEvent *event) QChartView::mouseMoveEvent(event); } +} // namespace JV2 diff --git a/frontend/chartView.h b/frontend/chartView.h index c7c69d56..5e9ca072 100644 --- a/frontend/chartView.h +++ b/frontend/chartView.h @@ -7,6 +7,8 @@ #include #include +namespace JV2 +{ class ChartView : public QChartView { Q_OBJECT @@ -43,3 +45,4 @@ class ChartView : public QChartView QGraphicsSimpleTextItem *coordStartLabelX_; QGraphicsSimpleTextItem *coordStartLabelY_; }; +} // namespace JV2 diff --git a/frontend/data.cpp b/frontend/data.cpp index 5c38be89..b80c0954 100644 --- a/frontend/data.cpp +++ b/frontend/data.cpp @@ -10,6 +10,8 @@ #include #include +namespace JV2 +{ /* * Private Functions */ @@ -215,3 +217,4 @@ void MainWindow::runDataContextMenuRequested(QPoint pos) [=](HttpRequestWorker *worker) { plotMonSpectra(worker); }); } } +} // namespace JV2 diff --git a/frontend/errorHandling.cpp b/frontend/errorHandling.cpp index 2458746a..5666142c 100644 --- a/frontend/errorHandling.cpp +++ b/frontend/errorHandling.cpp @@ -4,6 +4,8 @@ #include "mainWindow.h" #include +namespace JV2 +{ // Perform check for errors on http request, returning the handled error QString MainWindow::handleRequestError(HttpRequestWorker *worker, const QString &taskDescription) { @@ -51,3 +53,4 @@ void MainWindow::setErrorPage(const QString &errorTitle, const QString &errorTex } void MainWindow::on_ErrorOKButton_clicked(bool checked) { updateForCurrentSource(JournalSource::JournalSourceState::OK); } +} // namespace JV2 diff --git a/frontend/export.cpp b/frontend/export.cpp index 41e03b3c..da71a146 100644 --- a/frontend/export.cpp +++ b/frontend/export.cpp @@ -6,6 +6,8 @@ #include #include +namespace JV2 +{ void MainWindow::exportRunDataAsText() { // Save selection or all items? @@ -56,3 +58,4 @@ void MainWindow::exportRunDataAsText() */ void MainWindow::on_actionExportAsText_triggered() { exportRunDataAsText(); } +} // namespace JV2 diff --git a/frontend/filtering.cpp b/frontend/filtering.cpp index fbea5814..ae31da9b 100644 --- a/frontend/filtering.cpp +++ b/frontend/filtering.cpp @@ -5,6 +5,8 @@ #include #include +namespace JV2 +{ /* * UI */ @@ -46,3 +48,4 @@ void MainWindow::on_GroupRunsButton_clicked(bool checked) // Clears filter parameters void MainWindow::on_RunFilterClearButton_clicked(bool checked) { ui_.RunFilterEdit->clear(); } +} // namespace JV2 diff --git a/frontend/finding.cpp b/frontend/finding.cpp index 1f651a53..3f510b2f 100644 --- a/frontend/finding.cpp +++ b/frontend/finding.cpp @@ -4,6 +4,8 @@ #include "mainWindow.h" #include +namespace JV2 +{ /* * Private Functions */ @@ -132,3 +134,4 @@ void MainWindow::on_actionFind_triggered() void MainWindow::on_actionFindNext_triggered() { findDown(); } void MainWindow::on_actionFindPrevious_triggered() { findUp(); } void MainWindow::on_actionSelectAllFound_triggered() { selectAllSearches(); } +} // namespace JV2 diff --git a/frontend/generation.cpp b/frontend/generation.cpp index db607d17..5be077ea 100644 --- a/frontend/generation.cpp +++ b/frontend/generation.cpp @@ -4,6 +4,8 @@ #include "mainWindow.h" #include +namespace JV2 +{ /* * UI */ @@ -197,3 +199,4 @@ void MainWindow::handleGenerateScanStop(HttpRequestWorker *worker) sourceBeingGenerated_ = nullptr; } +} // namespace JV2 diff --git a/frontend/genericTreeModel.cpp b/frontend/genericTreeModel.cpp index 54374ffe..6172963f 100644 --- a/frontend/genericTreeModel.cpp +++ b/frontend/genericTreeModel.cpp @@ -3,6 +3,8 @@ #include "genericTreeModel.h" +namespace JV2 +{ /* * GenericTreeItem */ @@ -159,3 +161,4 @@ void GenericTreeModel::setRootItem(GenericTreeItem *rootItem) rootItem_ = rootItem; endResetModel(); } +} // namespace JV2 diff --git a/frontend/genericTreeModel.h b/frontend/genericTreeModel.h index 0a10e1f6..ea202145 100644 --- a/frontend/genericTreeModel.h +++ b/frontend/genericTreeModel.h @@ -6,6 +6,8 @@ #include #include +namespace JV2 +{ class GenericTreeItem { public: @@ -52,3 +54,4 @@ class GenericTreeModel : public QAbstractItemModel int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; }; +} // namespace JV2 diff --git a/frontend/graphWidget.cpp b/frontend/graphWidget.cpp index 0bf347c8..db39cd39 100644 --- a/frontend/graphWidget.cpp +++ b/frontend/graphWidget.cpp @@ -11,6 +11,8 @@ #include #include +namespace JV2 +{ GraphWidget::GraphWidget(QWidget *parent, QChart *chart, QString type) : QWidget(parent) { type_ = type; @@ -338,3 +340,4 @@ void GraphWidget::modifyAgainstWorker(HttpRequestWorker *worker, bool checked) ui_.chartView->chart()->axes()[1]->setMin(min); } } +} // namespace JV2 diff --git a/frontend/graphWidget.h b/frontend/graphWidget.h index 641dd4fc..20c6a6cf 100644 --- a/frontend/graphWidget.h +++ b/frontend/graphWidget.h @@ -10,6 +10,8 @@ #include #include +namespace JV2 +{ class GraphWidget : public QWidget { Q_OBJECT @@ -58,3 +60,4 @@ class GraphWidget : public QWidget void runDivide(QString currentDetector, QString run, bool checked); void monDivide(QString currentRun, QString mon, bool checked); }; +} // namespace JV2 diff --git a/frontend/graphWidget.ui b/frontend/graphWidget.ui index 901c6bea..43407412 100644 --- a/frontend/graphWidget.ui +++ b/frontend/graphWidget.ui @@ -186,7 +186,7 @@ - + @@ -194,14 +194,10 @@ - QChartView - QGraphicsView -
QtCharts
-
- - ChartView - QChartView + JV2::ChartView + QWidget
chartView.h
+ 1
diff --git a/frontend/httpRequestWorker.cpp b/frontend/httpRequestWorker.cpp index af8788ed..43836725 100644 --- a/frontend/httpRequestWorker.cpp +++ b/frontend/httpRequestWorker.cpp @@ -6,6 +6,8 @@ #include #include +namespace JV2 +{ HttpRequestWorker::HttpRequestWorker(QNetworkAccessManager &manager, const QString &url, HttpRequestHandler handler) : QObject() { // Set up the request @@ -71,3 +73,4 @@ QNetworkReply::NetworkError HttpRequestWorker::errorType() const { return errorT // Return error string (if available) const QString &HttpRequestWorker::errorString() const { return errorString_; } +} // namespace JV2 diff --git a/frontend/httpRequestWorker.h b/frontend/httpRequestWorker.h index a5d557cb..bc877fd9 100644 --- a/frontend/httpRequestWorker.h +++ b/frontend/httpRequestWorker.h @@ -14,6 +14,8 @@ class QNetworkAccessManager; class QNetworkReply; +namespace JV2 +{ // Object for handling an http request class HttpRequestWorker : public QObject { @@ -68,3 +70,4 @@ class HttpRequestWorker : public QObject // Process request once its complete void requestComplete(); }; +} // namespace JV2 diff --git a/frontend/instrument.cpp b/frontend/instrument.cpp index 11d46978..b626b56f 100644 --- a/frontend/instrument.cpp +++ b/frontend/instrument.cpp @@ -5,6 +5,8 @@ #include #include +namespace JV2 +{ // Static Singleton // -- Default columns for instrument types std::map Instrument::defaultColumns_; @@ -162,3 +164,4 @@ QString Instrument::pathComponent(PathType pathType, bool upperCased) const } return upperCased ? result.toUpper() : result.toLower(); } +} // namespace JV2 diff --git a/frontend/instrument.h b/frontend/instrument.h index 509273aa..81fd4b69 100644 --- a/frontend/instrument.h +++ b/frontend/instrument.h @@ -6,6 +6,8 @@ #include #include +namespace JV2 +{ // Instrument Definition class Instrument { @@ -87,3 +89,4 @@ class Instrument // Return specified path component for this instrument (lowercased by default) QString pathComponent(PathType pathType, bool upperCased = false) const; }; +} // namespace JV2 diff --git a/frontend/instrumentModel.cpp b/frontend/instrumentModel.cpp index 1ea10768..6860a0d0 100644 --- a/frontend/instrumentModel.cpp +++ b/frontend/instrumentModel.cpp @@ -3,6 +3,8 @@ #include "instrumentModel.h" +namespace JV2 +{ // Model to handle json data in table view InstrumentModel::InstrumentModel() : QAbstractListModel() {} @@ -70,3 +72,4 @@ QVariant InstrumentModel::headerData(int section, Qt::Orientation orientation, i return {}; } } +} // namespace JV2 diff --git a/frontend/instrumentModel.h b/frontend/instrumentModel.h index e40fc056..df19f283 100644 --- a/frontend/instrumentModel.h +++ b/frontend/instrumentModel.h @@ -7,6 +7,8 @@ #include "optionalRef.h" #include +namespace JV2 +{ // Model for Instrument definitions class InstrumentModel : public QAbstractListModel { @@ -36,3 +38,4 @@ class InstrumentModel : public QAbstractListModel QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; +} // namespace JV2 diff --git a/frontend/instruments.cpp b/frontend/instruments.cpp index 59d3ed49..296f1876 100644 --- a/frontend/instruments.cpp +++ b/frontend/instruments.cpp @@ -5,6 +5,8 @@ #include #include +namespace JV2 +{ /* * Private Functions */ @@ -103,3 +105,4 @@ OptionalReferenceWrapper MainWindow::currentInstrument() const return currentJournalSource_->currentInstrument(); } +} // namespace JV2 diff --git a/frontend/journal.cpp b/frontend/journal.cpp index e1782c6a..cf77928a 100644 --- a/frontend/journal.cpp +++ b/frontend/journal.cpp @@ -3,6 +3,8 @@ #include "journal.h" +namespace JV2 +{ Journal::Journal(QString name) : name_(name) {} /* @@ -20,3 +22,4 @@ void Journal::setFilename(const QString &filename) { filename_ = filename; } // Return filename const QString &Journal::filename() const { return filename_; } +} // namespace JV2 diff --git a/frontend/journal.h b/frontend/journal.h index c8698e2e..ed8a6a98 100644 --- a/frontend/journal.h +++ b/frontend/journal.h @@ -6,6 +6,9 @@ #include #include +namespace JV2 +{ + // Journal Definition class Journal { @@ -33,3 +36,4 @@ class Journal // Return filename const QString &filename() const; }; +} // namespace JV2 diff --git a/frontend/journalModel.cpp b/frontend/journalModel.cpp index b89df818..254fb264 100644 --- a/frontend/journalModel.cpp +++ b/frontend/journalModel.cpp @@ -3,6 +3,8 @@ #include "journalModel.h" +namespace JV2 +{ // Model to handle json data in table view JournalModel::JournalModel() : QAbstractListModel() {} @@ -70,3 +72,4 @@ QVariant JournalModel::headerData(int section, Qt::Orientation orientation, int return {}; } } +} // namespace JV2 diff --git a/frontend/journalModel.h b/frontend/journalModel.h index 87aec533..a03cf1c5 100644 --- a/frontend/journalModel.h +++ b/frontend/journalModel.h @@ -7,6 +7,8 @@ #include "optionalRef.h" #include +namespace JV2 +{ // Model for Journal definitions class JournalModel : public QAbstractListModel { @@ -36,3 +38,4 @@ class JournalModel : public QAbstractListModel QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; +} // namespace JV2 diff --git a/frontend/journalSource.cpp b/frontend/journalSource.cpp index c8f82214..9028e533 100644 --- a/frontend/journalSource.cpp +++ b/frontend/journalSource.cpp @@ -5,6 +5,8 @@ #include "instrument.h" #include +namespace JV2 +{ // Return text string for specified IndexingType type QString JournalSource::indexingType(JournalSource::IndexingType type) { @@ -408,3 +410,4 @@ void JournalSource::fromSettings(const QSettings &settings) .toString()); } } +} // namespace JV2 diff --git a/frontend/journalSource.h b/frontend/journalSource.h index b4e54af7..3a6d85a5 100644 --- a/frontend/journalSource.h +++ b/frontend/journalSource.h @@ -10,6 +10,8 @@ #include #include +namespace JV2 +{ // Forward Declarations class HttpRequestWorker; @@ -233,3 +235,4 @@ class JournalSource // Retrieve data from the supplied QSettings void fromSettings(const QSettings &settings); }; +} // namespace JV2 diff --git a/frontend/journalSourceFilterProxy.cpp b/frontend/journalSourceFilterProxy.cpp index bcec3d4f..bd8ffbae 100644 --- a/frontend/journalSourceFilterProxy.cpp +++ b/frontend/journalSourceFilterProxy.cpp @@ -6,6 +6,8 @@ #include #include +namespace JV2 +{ JournalSourceFilterProxy::JournalSourceFilterProxy(JournalSourceModel &journalSourceModel) : journalSourceModel_(journalSourceModel) { @@ -16,3 +18,4 @@ bool JournalSourceFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex { return (!showAvailableOnly_ || journalSourceModel_.getData(sourceRow)->isAvailable()); } +} // namespace JV2 diff --git a/frontend/journalSourceFilterProxy.h b/frontend/journalSourceFilterProxy.h index de6eee9d..4c9a6194 100644 --- a/frontend/journalSourceFilterProxy.h +++ b/frontend/journalSourceFilterProxy.h @@ -3,12 +3,14 @@ #pragma once +#include #include #include +namespace JV2 +{ // Forward Declarations class JournalSourceModel; -class QModelIndex; class JournalSourceFilterProxy : public QSortFilterProxyModel { @@ -26,3 +28,4 @@ class JournalSourceFilterProxy : public QSortFilterProxyModel protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; }; +} // namespace JV2 diff --git a/frontend/journalSourceModel.cpp b/frontend/journalSourceModel.cpp index 5ccd1df6..b0f2234f 100644 --- a/frontend/journalSourceModel.cpp +++ b/frontend/journalSourceModel.cpp @@ -4,6 +4,8 @@ #include "journalSourceModel.h" #include "uniqueName.h" +namespace JV2 +{ // Model to handle json data in table view JournalSourceModel::JournalSourceModel() : QAbstractListModel() {} @@ -151,3 +153,4 @@ QVariant JournalSourceModel::headerData(int section, Qt::Orientation orientation return {}; } } +} // namespace JV2 diff --git a/frontend/journalSourceModel.h b/frontend/journalSourceModel.h index 06668ced..b6caf8e5 100644 --- a/frontend/journalSourceModel.h +++ b/frontend/journalSourceModel.h @@ -7,6 +7,8 @@ #include "optionalRef.h" #include +namespace JV2 +{ // Model for JournalSource definitions class JournalSourceModel : public QAbstractListModel { @@ -42,3 +44,4 @@ class JournalSourceModel : public QAbstractListModel bool setData(const QModelIndex &index, const QVariant &value, int role) override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; +} // namespace JV2 diff --git a/frontend/journalSources.cpp b/frontend/journalSources.cpp index b177a7c5..f76c8a40 100644 --- a/frontend/journalSources.cpp +++ b/frontend/journalSources.cpp @@ -8,6 +8,8 @@ #include #include +namespace JV2 +{ /* * Private Functions */ @@ -326,3 +328,4 @@ void MainWindow::handleJumpToJournal(HttpRequestWorker *worker) backend_.getJournal(currentJournalSource(), [=](HttpRequestWorker *worker) { handleCompleteJournalRunData(worker, runNumber); }); } +} // namespace JV2 diff --git a/frontend/journalSourcesDialog.cpp b/frontend/journalSourcesDialog.cpp index b9850139..6da859ea 100644 --- a/frontend/journalSourcesDialog.cpp +++ b/frontend/journalSourcesDialog.cpp @@ -5,6 +5,8 @@ #include #include +namespace JV2 +{ JournalSourcesDialog::JournalSourcesDialog(QWidget *parent) : QDialog(parent) { ui_.setupUi(this); @@ -208,3 +210,4 @@ void JournalSourcesDialog::go(std::vector> &sourc exec(); } +} // namespace JV2 diff --git a/frontend/journalSourcesDialog.h b/frontend/journalSourcesDialog.h index 2453ad33..aad2e122 100644 --- a/frontend/journalSourcesDialog.h +++ b/frontend/journalSourcesDialog.h @@ -7,6 +7,8 @@ #include "lock.h" #include "ui_journalSourcesDialog.h" +namespace JV2 +{ // Forward Declarations class MainWindow; class JournalSource; @@ -59,3 +61,4 @@ class JournalSourcesDialog : public QDialog // Go! void go(std::vector> &sources); }; +} // namespace JV2 diff --git a/frontend/lock.cpp b/frontend/lock.cpp index 84175e1b..4e13cd33 100644 --- a/frontend/lock.cpp +++ b/frontend/lock.cpp @@ -4,6 +4,8 @@ #include "lock.h" #include +namespace JV2 +{ /* * Lock */ @@ -50,3 +52,4 @@ void Locker::unlock() unlocked_ = true; } +} // namespace JV2 diff --git a/frontend/lock.h b/frontend/lock.h index bec3272c..db1b19a7 100644 --- a/frontend/lock.h +++ b/frontend/lock.h @@ -3,6 +3,8 @@ #pragma once +namespace JV2 +{ class Lock { public: @@ -44,3 +46,4 @@ class Locker // Manually release the lock void unlock(); }; +} // namespace JV2 diff --git a/frontend/main.cpp b/frontend/main.cpp index 9e4de128..93b3dac7 100644 --- a/frontend/main.cpp +++ b/frontend/main.cpp @@ -12,11 +12,11 @@ int main(int argc, char *argv[]) QApplication::setWindowIcon(QIcon(":/icon")); // Set up and parse command-line arguments - CLIArgs parser; + JV2::CLIArgs parser; if (!parser.parseArguments(QApplication::arguments())) return 1; - MainWindow window(parser); + JV2::MainWindow window(parser); window.show(); return QApplication::exec(); } diff --git a/frontend/mainWindow.cpp b/frontend/mainWindow.cpp index 75b93b42..7d4ae348 100644 --- a/frontend/mainWindow.cpp +++ b/frontend/mainWindow.cpp @@ -8,6 +8,8 @@ #include #include +namespace JV2 +{ MainWindow::MainWindow(QCommandLineParser &cliParser) : QMainWindow(), backend_(cliParser), journalSourceFilterProxy_(journalSourceModel_), runDataFilterProxy_(runDataModel_) { @@ -191,3 +193,4 @@ void MainWindow::waitForBackend() }); pingTimer->start(); } +} // namespace JV2 diff --git a/frontend/mainWindow.h b/frontend/mainWindow.h index c335913b..18574b34 100644 --- a/frontend/mainWindow.h +++ b/frontend/mainWindow.h @@ -22,6 +22,8 @@ #include #include +namespace JV2 +{ class MainWindow : public QMainWindow { Q_OBJECT @@ -202,14 +204,16 @@ class MainWindow : public QMainWindow const inline static QString FileNotFoundError = QStringLiteral("FileNotFoundError"); private: - // Perform check for errors on http request, returning the handled error - QString handleRequestError(HttpRequestWorker *worker, const QString &taskDescription); // Update the error page void setErrorPage(const QString &errorTitle, const QString &errorText); private slots: void on_ErrorOKButton_clicked(bool checked); + public: + // Perform check for errors on http request, returning the handled error + QString handleRequestError(HttpRequestWorker *worker, const QString &taskDescription); + /* * Settings */ @@ -305,3 +309,4 @@ class MainWindow : public QMainWindow void runDivide(QString currentDetector, QString run, bool checked); void monDivide(QString currentRun, QString mon, bool checked); }; +} // namespace JV2 diff --git a/frontend/nexusInteraction.cpp b/frontend/nexusInteraction.cpp index 1139cb14..93b5c7bd 100644 --- a/frontend/nexusInteraction.cpp +++ b/frontend/nexusInteraction.cpp @@ -17,6 +17,8 @@ #include #include +namespace JV2 +{ void MainWindow::toggleAxis(int state) { auto *toggleBox = qobject_cast(sender()); @@ -279,3 +281,4 @@ void MainWindow::monDivide(QString currentRun, QString mon, bool checked) backend_.getNexusSpectrum(currentJournalSource(), "monitor", mon.toInt(), {currentRun.toInt()}, [=](HttpRequestWorker *worker) { window->modifyAgainstWorker(worker, checked); }); } +} // namespace JV2 diff --git a/frontend/optionalRef.h b/frontend/optionalRef.h index 173001da..c06fb18e 100644 --- a/frontend/optionalRef.h +++ b/frontend/optionalRef.h @@ -6,4 +6,7 @@ #include #include +namespace JV2 +{ template using OptionalReferenceWrapper = std::optional>; +} diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 096cc760..a75db7c1 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -4,28 +4,32 @@ #include "plotLogDataWidget.h" #include -PlotLogDataWidget::PlotLogDataWidget(QWidget *parent, Backend &backend, const JournalSource* source, const std::vector &runNumbers) : QWidget(parent), backend_(backend), source_(source), runNumbers_(runNumbers) +namespace JV2 +{ +PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const JournalSource *source, + const std::vector &runNumbers) + : QWidget(parent), mainWindow_(parent), backend_(backend), source_(source), runNumbers_(runNumbers) { ui_.setupUi(this); -// propertyModel_.setRootItem(rootItem); + // propertyModel_.setRootItem(rootItem); ui_.PropertyTree->setModel(&propertyModel_); ui_.PropertyTree->expandAll(); ui_.PropertyTree->resizeColumnToContents(0); ui_.PropertyTree->resizeColumnToContents(1); ui_.PropertyTree->setSelectionBehavior(QAbstractItemView::SelectRows); - + connect(ui_.PropertyTree->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(onTreeSelectionChanged(const QItemSelection &, const QItemSelection &))); // Acquire the available log data - backend_.getNexusFields(source_, runNumbers_, - [=](HttpRequestWorker *worker) { handleRetrieveSELogProperties(worker); }); + backend_.getNexusFields(source_, runNumbers_, [=](HttpRequestWorker *worker) { handleRetrieveSELogProperties(worker); }); } -void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker* worker) -{ // Check for errors - if (handleRequestError(worker, "retrieving log values from run") != NoError) +void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker *worker) +{ + // Check for errors + if (mainWindow_->handleRequestError(worker, "retrieving log values from run") != Backend::NoError) return; // Iterate over logs extracted from the target run data and construct our mapped values @@ -53,3 +57,4 @@ void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker* worker) } PlotLogDataWidget::~PlotLogDataWidget() {} +} // namespace JV2 diff --git a/frontend/plotLogDataWidget.h b/frontend/plotLogDataWidget.h index 501e8ec7..5d6e8ee4 100644 --- a/frontend/plotLogDataWidget.h +++ b/frontend/plotLogDataWidget.h @@ -3,11 +3,14 @@ #pragma once -#include "ui_plotLogDataWidget.h" #include "backend.h" #include "genericTreeModel.h" +#include "mainWindow.h" +#include "ui_plotLogDataWidget.h" #include +namespace JV2 +{ // Forward Declarations class JournalSource; @@ -16,26 +19,29 @@ class PlotLogDataWidget : public QWidget Q_OBJECT public: - PlotLogDataWidget(QWidget *parent, Backend &backend, const JournalSource* source, const std::vector &runNumbers); + PlotLogDataWidget(MainWindow *parent, Backend &backend, const JournalSource *source, const std::vector &runNumbers); ~PlotLogDataWidget(); private: // User interface object Ui::PlotLogDataWidget ui_; + // Main Window parent + MainWindow *mainWindow_{nullptr}; // Tree model for properties GenericTreeModel propertyModel_; // Main backend Backend &backend_; // Journal source from which the run numbers came - const JournalSource* source_{nullptr}; + const JournalSource *source_{nullptr}; // Run numbers to display on the plot std::vector runNumbers_; private: // Handle retrieved log properties data - void handleRetrieveSELogProperties(HttpRequestWorker* worker); + void handleRetrieveSELogProperties(HttpRequestWorker *worker); private slots: signals: }; +} // namespace JV2 diff --git a/frontend/runDataFilterProxy.cpp b/frontend/runDataFilterProxy.cpp index 477278e8..20f9aa8a 100644 --- a/frontend/runDataFilterProxy.cpp +++ b/frontend/runDataFilterProxy.cpp @@ -6,6 +6,8 @@ #include #include +namespace JV2 +{ RunDataFilterProxy::RunDataFilterProxy(RunDataModel &runDataModel) : runDataModel_(runDataModel) { setSourceModel(&runDataModel_); @@ -56,3 +58,4 @@ QString RunDataFilterProxy::getData(const QString &targetData, const QModelIndex { return runDataModel_.getData(targetData, mapToSource(index)); } +} // namespace JV2 diff --git a/frontend/runDataFilterProxy.h b/frontend/runDataFilterProxy.h index 0a4c29f1..2335bcfa 100644 --- a/frontend/runDataFilterProxy.h +++ b/frontend/runDataFilterProxy.h @@ -3,12 +3,14 @@ #pragma once +#include #include #include +namespace JV2 +{ // Forward Declarations class RunDataModel; -class QModelIndex; class RunDataFilterProxy : public QSortFilterProxyModel { @@ -36,3 +38,4 @@ class RunDataFilterProxy : public QSortFilterProxyModel protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; }; +} // namespace JV2 diff --git a/frontend/runDataModel.cpp b/frontend/runDataModel.cpp index 8ebf05f0..d8e3a5b9 100644 --- a/frontend/runDataModel.cpp +++ b/frontend/runDataModel.cpp @@ -6,6 +6,8 @@ #include #include +namespace JV2 +{ // Model to handle json data in table view RunDataModel::RunDataModel() : QAbstractTableModel() {} @@ -169,3 +171,4 @@ QVariant RunDataModel::headerData(int section, Qt::Orientation orientation, int return {}; } } +} // namespace JV2 diff --git a/frontend/runDataModel.h b/frontend/runDataModel.h index 272b36a1..db79511e 100644 --- a/frontend/runDataModel.h +++ b/frontend/runDataModel.h @@ -12,6 +12,8 @@ #include #include +namespace JV2 +{ // JSON Run Data Model class RunDataModel : public QAbstractTableModel { @@ -52,3 +54,4 @@ class RunDataModel : public QAbstractTableModel QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; }; +} // namespace JV2 diff --git a/frontend/seLogChooserDialog.cpp b/frontend/seLogChooserDialog.cpp index d1efb9b2..28f2e141 100644 --- a/frontend/seLogChooserDialog.cpp +++ b/frontend/seLogChooserDialog.cpp @@ -3,6 +3,8 @@ #include "seLogChooserDialog.h" +namespace JV2 +{ SELogChooserDialog::SELogChooserDialog(QWidget *parent, GenericTreeItem *rootItem) : QDialog(parent) { ui_.setupUi(this); @@ -62,3 +64,4 @@ QStringList SELogChooserDialog::getValues() return result; } +} // namespace JV2 diff --git a/frontend/seLogChooserDialog.h b/frontend/seLogChooserDialog.h index 284f58f7..e7b7305f 100644 --- a/frontend/seLogChooserDialog.h +++ b/frontend/seLogChooserDialog.h @@ -7,6 +7,8 @@ #include "optionalRef.h" #include "ui_seLogChooserDialog.h" +namespace JV2 +{ class SELogChooserDialog : public QDialog { Q_OBJECT @@ -32,3 +34,4 @@ class SELogChooserDialog : public QDialog QString getValue(); QStringList getValues(); }; +} // namespace JV2 diff --git a/frontend/searchDialog.cpp b/frontend/searchDialog.cpp index 8b4804a8..6a225e0b 100644 --- a/frontend/searchDialog.cpp +++ b/frontend/searchDialog.cpp @@ -4,6 +4,8 @@ #include "searchDialog.h" #include +namespace JV2 +{ SearchDialog::SearchDialog(QWidget *parent) : QDialog(parent) { ui_.setupUi(this); @@ -63,3 +65,4 @@ std::map SearchDialog::getQuery() return parameters; } +} // namespace JV2 diff --git a/frontend/searchDialog.h b/frontend/searchDialog.h index b738de99..7ec9734c 100644 --- a/frontend/searchDialog.h +++ b/frontend/searchDialog.h @@ -5,6 +5,8 @@ #include "ui_searchDialog.h" +namespace JV2 +{ // Forward Declarations class MainWindow; @@ -32,3 +34,4 @@ class SearchDialog : public QDialog // Get search query std::map getQuery(); }; +} // namespace JV2 diff --git a/frontend/searching.cpp b/frontend/searching.cpp index f0552141..1812be71 100644 --- a/frontend/searching.cpp +++ b/frontend/searching.cpp @@ -5,6 +5,8 @@ #include "searchDialog.h" #include +namespace JV2 +{ /* * UI */ @@ -158,3 +160,4 @@ void MainWindow::handleSearchResult(HttpRequestWorker *worker) updateForCurrentSource(JournalSource::JournalSourceState::OK); } +} // namespace JV2 diff --git a/frontend/settings.cpp b/frontend/settings.cpp index ecea20e1..f5e9f436 100644 --- a/frontend/settings.cpp +++ b/frontend/settings.cpp @@ -11,6 +11,8 @@ #include #include +namespace JV2 +{ /* * Private Functions */ @@ -162,3 +164,4 @@ void MainWindow::getJournalSourcesFromSettings(QCommandLineParser &cliParser) if (cliParser.isSet(CLIArgs::HideIDAaaS)) idaaasDataCache->setAvailable(false); } +} // namespace JV2 diff --git a/frontend/uniqueName.h b/frontend/uniqueName.h index 426579f6..bedd5d71 100644 --- a/frontend/uniqueName.h +++ b/frontend/uniqueName.h @@ -3,6 +3,8 @@ #include +namespace JV2 +{ // Return unique name for object template static QString uniqueName(const QString &baseName, const Range &objects, NameFunction nameFunction) @@ -20,3 +22,4 @@ static QString uniqueName(const QString &baseName, const Range &objects, NameFun return uniqueName; } +} // namespace JV2 diff --git a/frontend/version.h b/frontend/version.h index 5c99a87b..48ec17be 100644 --- a/frontend/version.h +++ b/frontend/version.h @@ -3,5 +3,8 @@ #pragma once +namespace JV2 +{ #define JV2VERSION "1.99.3" #define JV2URL "https://github.com/disorderedmaterials/jv2" +} // namespace JV2 diff --git a/frontend/visualisation.cpp b/frontend/visualisation.cpp index fed40345..143f02c7 100644 --- a/frontend/visualisation.cpp +++ b/frontend/visualisation.cpp @@ -11,6 +11,8 @@ #include #include +namespace JV2 +{ // Handle extracted SE log values for plotting void MainWindow::handlePlotSELogValue(HttpRequestWorker *worker) { @@ -283,3 +285,4 @@ void MainWindow::handleCreateSELogPlot(HttpRequestWorker *worker) ui_.MainTabs->setCurrentIndex(ui_.MainTabs->count() - 1); dateTimeChartView->setFocus(); } +} // namespace JV2 From 778a10eeaeb2de60b60ec49e8e7cf0d2b8deabb8 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 20 Jan 2025 11:53:22 +0000 Subject: [PATCH 04/24] Update, but need to move towards a custom model. --- frontend/genericTreeModel.cpp | 54 ++++++++++++++++++++++++---------- frontend/genericTreeModel.h | 10 +++++-- frontend/plotLogDataWidget.cpp | 14 +++++---- frontend/plotLogDataWidget.ui | 2 +- 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/frontend/genericTreeModel.cpp b/frontend/genericTreeModel.cpp index 6172963f..0b826d78 100644 --- a/frontend/genericTreeModel.cpp +++ b/frontend/genericTreeModel.cpp @@ -9,7 +9,10 @@ namespace JV2 * GenericTreeItem */ -GenericTreeItem::GenericTreeItem(const QList &data) : data_(data) {} +GenericTreeItem::GenericTreeItem(const QList &data, const QList &toolTips, const QList &flags) + : data_(data), toolTips_(toolTips), flags_(flags) +{ +} GenericTreeItem::~GenericTreeItem() { qDeleteAll(children_); } @@ -19,9 +22,10 @@ void GenericTreeItem::appendChild(GenericTreeItem *item) item->setParent(this); } -GenericTreeItem *GenericTreeItem::appendChild(const QList &data) +GenericTreeItem *GenericTreeItem::appendChild(const QList &data, const QList &toolTips, + const QList &flags) { - auto *item = new GenericTreeItem(data); + auto *item = new GenericTreeItem(data, toolTips, flags); item->setParent(this); children_.append(item); return item; @@ -49,10 +53,24 @@ int GenericTreeItem::columnCount() const { return data_.count(); } QVariant GenericTreeItem::data(int column) const { if (column < 0 || column >= data_.count()) - return QVariant(); + return {}; return data_.at(column); } +QVariant GenericTreeItem::toolTip(int column) const +{ + if (column < 0 || column >= toolTips_.count()) + return {}; + return toolTips_.at(column); +} + +Qt::ItemFlags GenericTreeItem::flags(int column) const +{ + if (column < 0 || column >= flags_.count()) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; + return flags_.at(column); +} + void GenericTreeItem::setParent(GenericTreeItem *parent) { parent_ = parent; } GenericTreeItem *GenericTreeItem::parent() { return parent_; } @@ -72,7 +90,7 @@ GenericTreeModel::~GenericTreeModel() QModelIndex GenericTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!rootItem_ || !hasIndex(row, column, parent)) - return QModelIndex(); + return {}; GenericTreeItem *parentItem; @@ -84,19 +102,19 @@ QModelIndex GenericTreeModel::index(int row, int column, const QModelIndex &pare GenericTreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); - return QModelIndex(); + return {}; } QModelIndex GenericTreeModel::parent(const QModelIndex &index) const { if (!rootItem_ || !index.isValid()) - return QModelIndex(); + return {}; - GenericTreeItem *childItem = static_cast(index.internalPointer()); - GenericTreeItem *parentItem = childItem->parent(); + auto *childItem = static_cast(index.internalPointer()); + auto *parentItem = childItem->parent(); if (parentItem == rootItem_) - return QModelIndex(); + return {}; return createIndex(parentItem->row(), 0, parentItem); } @@ -125,14 +143,16 @@ int GenericTreeModel::columnCount(const QModelIndex &parent) const QVariant GenericTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) - return QVariant(); + return {}; - if (role != Qt::DisplayRole) - return QVariant(); + auto *item = static_cast(index.internalPointer()); - GenericTreeItem *item = static_cast(index.internalPointer()); + if (role == Qt::DisplayRole) + return item->data(index.column()); + else if (role == Qt::ToolTipRole) + return item->toolTip(index.column()); - return item->data(index.column()); + return {}; } Qt::ItemFlags GenericTreeModel::flags(const QModelIndex &index) const @@ -140,7 +160,9 @@ Qt::ItemFlags GenericTreeModel::flags(const QModelIndex &index) const if (!index.isValid()) return Qt::NoItemFlags; - return QAbstractItemModel::flags(index); + auto *item = static_cast(index.internalPointer()); + + return item->flags(index.column()); } QVariant GenericTreeModel::headerData(int section, Qt::Orientation orientation, int role) const diff --git a/frontend/genericTreeModel.h b/frontend/genericTreeModel.h index ea202145..d4126ee4 100644 --- a/frontend/genericTreeModel.h +++ b/frontend/genericTreeModel.h @@ -11,21 +11,27 @@ namespace JV2 class GenericTreeItem { public: - explicit GenericTreeItem(const QList &data); + explicit GenericTreeItem(const QList &data, const QList &toolTips = {}, + const QList &flags = {}); ~GenericTreeItem(); private: QList children_; QList data_; + QList toolTips_; + QList flags_; GenericTreeItem *parent_{nullptr}; public: void appendChild(GenericTreeItem *child); - GenericTreeItem *appendChild(const QList &data); + GenericTreeItem *appendChild(const QList &data, const QList &toolTips = {}, + const QList &flags = {}); GenericTreeItem *child(int row); int childCount() const; int columnCount() const; QVariant data(int column) const; + QVariant toolTip(int column) const; + Qt::ItemFlags flags(int column) const; int row() const; void setParent(GenericTreeItem *parent); GenericTreeItem *parent(); diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index a75db7c1..0c95db11 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -12,7 +12,6 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const { ui_.setupUi(this); - // propertyModel_.setRootItem(rootItem); ui_.PropertyTree->setModel(&propertyModel_); ui_.PropertyTree->expandAll(); ui_.PropertyTree->resizeColumnToContents(0); @@ -26,6 +25,8 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const backend_.getNexusFields(source_, runNumbers_, [=](HttpRequestWorker *worker) { handleRetrieveSELogProperties(worker); }); } +PlotLogDataWidget::~PlotLogDataWidget() {} + void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker *worker) { // Check for errors @@ -33,8 +34,7 @@ void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker *worker) return; // Iterate over logs extracted from the target run data and construct our mapped values - - auto *rootItem = new GenericTreeItem({"Log Value", "Full Path"}); + auto *rootItem = new GenericTreeItem({"Log Value"}); foreach (const auto &log, worker->jsonResponse().array()) { auto logArray = log.toArray(); @@ -42,7 +42,7 @@ void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker *worker) continue; // First item in the array is the name of the log value set / section - auto *sectionItem = rootItem->appendChild({logArray.first().toString(), ""}); + auto *sectionItem = rootItem->appendChild({logArray.first().toString()}); // Remove the name item and proceed to iterate over log values logArray.removeFirst(); @@ -52,9 +52,11 @@ void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker *worker) [](QVariant &v1, QVariant &v2) { return v1.toString() < v2.toString(); }); foreach (const auto &block, logArrayVar) - sectionItem->appendChild({block.toString().split("/").last(), block.toString()}); + sectionItem->appendChild({block.toString().split("/").last()}, {block.toString()}, + {Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable}); } + + propertyModel_.setRootItem(rootItem); } -PlotLogDataWidget::~PlotLogDataWidget() {} } // namespace JV2 diff --git a/frontend/plotLogDataWidget.ui b/frontend/plotLogDataWidget.ui index 60493dad..4273747b 100644 --- a/frontend/plotLogDataWidget.ui +++ b/frontend/plotLogDataWidget.ui @@ -57,7 +57,7 @@ - + 0 From d84e682228fffd60b18f639e958de11fa7a144e3 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 20 Jan 2025 15:18:08 +0000 Subject: [PATCH 05/24] Add LogValue class and model/proxy. --- frontend/CMakeLists.txt | 6 ++ frontend/backend.cpp | 6 +- frontend/backend.h | 6 +- frontend/logValue.cpp | 22 +++++++ frontend/logValue.h | 35 +++++++++++ frontend/logValueFilterProxy.cpp | 28 +++++++++ frontend/logValueFilterProxy.h | 35 +++++++++++ frontend/logValueModel.cpp | 105 +++++++++++++++++++++++++++++++ frontend/logValueModel.h | 43 +++++++++++++ frontend/plotLogDataWidget.cpp | 28 ++++----- frontend/plotLogDataWidget.h | 11 +++- frontend/plotLogDataWidget.ui | 2 +- 12 files changed, 300 insertions(+), 27 deletions(-) create mode 100644 frontend/logValue.cpp create mode 100644 frontend/logValue.h create mode 100644 frontend/logValueFilterProxy.cpp create mode 100644 frontend/logValueFilterProxy.h create mode 100644 frontend/logValueModel.cpp create mode 100644 frontend/logValueModel.h diff --git a/frontend/CMakeLists.txt b/frontend/CMakeLists.txt index 74c6a292..da67c407 100644 --- a/frontend/CMakeLists.txt +++ b/frontend/CMakeLists.txt @@ -48,6 +48,8 @@ qt6_add_executable( journal.h lock.cpp lock.h + logValue.cpp + logValue.h optionalRef.h # Backend backend.cpp @@ -84,6 +86,10 @@ qt6_add_executable( journalSourceFilterProxy.h journalSourceModel.cpp journalSourceModel.h + logValueFilterProxy.cpp + logValueFilterProxy.h + logValueModel.cpp + logValueModel.h runDataModel.cpp runDataModel.h runDataFilterProxy.cpp diff --git a/frontend/backend.cpp b/frontend/backend.cpp index ff02bf3f..ddd60cb9 100644 --- a/frontend/backend.cpp +++ b/frontend/backend.cpp @@ -173,9 +173,9 @@ void Backend::acquireAllJournalsStop(const HttpRequestWorker::HttpRequestHandler * NeXuS Endpoints */ -// Get NeXuS log values present in specified run files -void Backend::getNexusFields(const JournalSource *source, const std::vector &runNos, - const HttpRequestWorker::HttpRequestHandler &handler) +// Get all NeXuS log values present over specified run files +void Backend::getNeXuSLogValues(const JournalSource *source, const std::vector &runNos, + const HttpRequestWorker::HttpRequestHandler &handler) { auto data = source->sourceObjectData(); diff --git a/frontend/backend.h b/frontend/backend.h index 95c8d7f8..fbf2abf6 100644 --- a/frontend/backend.h +++ b/frontend/backend.h @@ -101,9 +101,9 @@ class Backend : public QObject * NeXuS Endpoints */ public: - // Get NeXuS log values present in specified run files - void getNexusFields(const JournalSource *source, const std::vector &runNos, - const HttpRequestWorker::HttpRequestHandler &handler = {}); + // Get all NeXuS log values present over specified run files + void getNeXuSLogValues(const JournalSource *source, const std::vector &runNos, + const HttpRequestWorker::HttpRequestHandler &handler = {}); // Get NeXuS log value data for specified run files void getNexusLogValueData(const JournalSource *source, const std::vector &runNos, const QString &logValue, const HttpRequestWorker::HttpRequestHandler &handler = {}); diff --git a/frontend/logValue.cpp b/frontend/logValue.cpp new file mode 100644 index 00000000..0ca98d5f --- /dev/null +++ b/frontend/logValue.cpp @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 Team JournalViewer and contributors + +#include "logValue.h" + +namespace JV2 +{ +LogValue::LogValue(const QString &name, const QString &location) : name_(name), neXuSLocation_(location) {} + +// Return the name of the property +const QString &LogValue::name() const { return name_; } + +// Return the NeXuS location path of the property +const QString &LogValue::neXuSLocation() const { return neXuSLocation_; } + +// Return whether the property is selected +bool LogValue::isSelected() const { return selected_; } + +// Set whether the property is selected +void LogValue::setSelected(bool selected) { selected_ = selected; } + +} // namespace JV2 diff --git a/frontend/logValue.h b/frontend/logValue.h new file mode 100644 index 00000000..863bb451 --- /dev/null +++ b/frontend/logValue.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 Team JournalViewer and contributors + +#pragma once + +#include + +namespace JV2 +{ +// Log Property Definition +class LogValue +{ + public: + LogValue(const QString &name, const QString &location = {}); + ~LogValue() = default; + + private: + // Display name of the property + QString name_; + // NeXuS location of the property (if relevant) + QString neXuSLocation_; + // Whether this property is selected + bool selected_{false}; + + public: + // Return the name of the property + const QString &name() const; + // Return the NeXuS location path of the property + const QString &neXuSLocation() const; + // Return whether the property is selected + bool isSelected() const; + // Set whether the property is selected + void setSelected(bool selected); +}; +} // namespace JV2 diff --git a/frontend/logValueFilterProxy.cpp b/frontend/logValueFilterProxy.cpp new file mode 100644 index 00000000..43d79a3c --- /dev/null +++ b/frontend/logValueFilterProxy.cpp @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 Team JournalViewer and contributors + +#include "logValueFilterProxy.h" +#include "logValueModel.h" +#include +#include + +namespace JV2 +{ +LogValueFilterProxy::LogValueFilterProxy(LogValueModel &logValueModel) : logValueModel_(logValueModel) +{ + setSourceModel(&logValueModel_); +} + +bool LogValueFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + return (searchString_.isEmpty() || logValueModel_.getData(sourceRow)->get().name().contains(searchString_)); +} + +// Set search string +void LogValueFilterProxy::setSearchString(const QString &search) +{ + searchString_ = search; + invalidate(); +} + +} // namespace JV2 diff --git a/frontend/logValueFilterProxy.h b/frontend/logValueFilterProxy.h new file mode 100644 index 00000000..60fbf730 --- /dev/null +++ b/frontend/logValueFilterProxy.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 Team JournalViewer and contributors + +#pragma once + +#include +#include +#include + +namespace JV2 +{ +// Forward Declarations +class LogValueModel; + +class LogValueFilterProxy : public QSortFilterProxyModel +{ + Q_OBJECT + + public: + LogValueFilterProxy(LogValueModel &journalSourceModel); + + private: + // Target model + LogValueModel &logValueModel_; + // Search string + QString searchString_; + + protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + + public: + // Set search string + void setSearchString(const QString &search); +}; +} // namespace JV2 diff --git a/frontend/logValueModel.cpp b/frontend/logValueModel.cpp new file mode 100644 index 00000000..6ffb6710 --- /dev/null +++ b/frontend/logValueModel.cpp @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team LogValueViewer and contributors + +#include "logValueModel.h" + +namespace JV2 +{ +// Model to handle json data in table view +LogValueModel::LogValueModel() : QAbstractListModel() {} + +/* + * Public Functions + */ + +// Set the source data for the model +void LogValueModel::setData(OptionalReferenceWrapper> properties) +{ + beginResetModel(); + data_ = properties; + endResetModel(); +} + +// Get LogValue row specified +OptionalReferenceWrapper LogValueModel::getData(int row) const +{ + if (!data_ || row == -1 || row >= rowCount()) + return {}; + + return data_->get()[row]; +} + +// Get LogValue at index specified +OptionalReferenceWrapper LogValueModel::getData(const QModelIndex &index) const { return getData(index.row()); } + +/* + * QAbstractListModel Overrides + */ + +int LogValueModel::rowCount(const QModelIndex &parent) const { return data_ ? data_->get().size() : 0; } + +int LogValueModel::columnCount(const QModelIndex &parent) const { return 1; } + +Qt::ItemFlags LogValueModel::flags(const QModelIndex &index) const +{ + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; +} + +QVariant LogValueModel::data(const QModelIndex &index, int role) const +{ + if (!data_) + return {}; + + // Column zero is the only relevant one + if (index.column() != 0) + return {}; + + auto optData = getData(index); + if (!optData) + return {}; + auto &data = optData->get(); + + switch (role) + { + case (Qt::DisplayRole): + case (Qt::EditRole): + return data.name(); + case (Qt::ToolTipRole): + return data.neXuSLocation(); + case (Qt::CheckStateRole): + return data.isSelected(); + default: + return {}; + } +} + +bool LogValueModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!data_) + return false; + + // Column zero is the only relevant one + if (index.column() != 0) + return false; + + auto optData = getData(index); + if (!optData) + return {}; + auto &data = optData->get(); + + if (role != Qt::CheckStateRole) + return false; + + data.setSelected(value.value() == Qt::Checked); + + return true; +} + +QVariant LogValueModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + return {}; + + return "Log Value"; +} +} // namespace JV2 diff --git a/frontend/logValueModel.h b/frontend/logValueModel.h new file mode 100644 index 00000000..4a576870 --- /dev/null +++ b/frontend/logValueModel.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team LogValueViewer and contributors + +#pragma once + +#include "logValue.h" +#include "optionalRef.h" +#include + +namespace JV2 +{ +// Model for LogValue definitions +class LogValueModel : public QAbstractListModel +{ + public: + LogValueModel(); + + private: + // LogValue data for the model + OptionalReferenceWrapper> data_; + // Whether to show availability as checkboxes + bool showAvailability_{false}; + + public: + // Set the source data for the model + void setData(OptionalReferenceWrapper> properties); + // Get LogValue at row specified + OptionalReferenceWrapper getData(int row) const; + // Get LogValue at index specified + OptionalReferenceWrapper getData(const QModelIndex &index) const; + + /* + * QAbstractTableModel Overrides + */ + public: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; +}; +} // namespace JV2 diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 0c95db11..485eedea 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -8,21 +8,19 @@ namespace JV2 { PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const JournalSource *source, const std::vector &runNumbers) - : QWidget(parent), mainWindow_(parent), backend_(backend), source_(source), runNumbers_(runNumbers) + : QWidget(parent), mainWindow_(parent), backend_(backend), source_(source), runNumbers_(runNumbers), + logValueFilterProxy_(logValueModel_) { ui_.setupUi(this); - ui_.PropertyTree->setModel(&propertyModel_); - ui_.PropertyTree->expandAll(); - ui_.PropertyTree->resizeColumnToContents(0); - ui_.PropertyTree->resizeColumnToContents(1); - ui_.PropertyTree->setSelectionBehavior(QAbstractItemView::SelectRows); + ui_.PropertyList->setModel(&logValueFilterProxy_); + ui_.PropertyList->setSelectionBehavior(QAbstractItemView::SelectRows); - connect(ui_.PropertyTree->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, + connect(ui_.PropertyList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(onTreeSelectionChanged(const QItemSelection &, const QItemSelection &))); // Acquire the available log data - backend_.getNexusFields(source_, runNumbers_, [=](HttpRequestWorker *worker) { handleRetrieveSELogProperties(worker); }); + backend_.getNeXuSLogValues(source_, runNumbers_, [=](HttpRequestWorker *worker) { handleRetrieveSELogProperties(worker); }); } PlotLogDataWidget::~PlotLogDataWidget() {} @@ -33,17 +31,15 @@ void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker *worker) if (mainWindow_->handleRequestError(worker, "retrieving log values from run") != Backend::NoError) return; - // Iterate over logs extracted from the target run data and construct our mapped values - auto *rootItem = new GenericTreeItem({"Log Value"}); + // Iterate over log values extracted from the target run data and create a vector of all those available + logValues_.reserve(1024); + logValues_.clear(); foreach (const auto &log, worker->jsonResponse().array()) { auto logArray = log.toArray(); if (logArray.size() < 2) continue; - // First item in the array is the name of the log value set / section - auto *sectionItem = rootItem->appendChild({logArray.first().toString()}); - // Remove the name item and proceed to iterate over log values logArray.removeFirst(); @@ -52,11 +48,9 @@ void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker *worker) [](QVariant &v1, QVariant &v2) { return v1.toString() < v2.toString(); }); foreach (const auto &block, logArrayVar) - sectionItem->appendChild({block.toString().split("/").last()}, {block.toString()}, - {Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable}); + logValues_.emplace_back(block.toString().split("/").last(), block.toString()); } - propertyModel_.setRootItem(rootItem); + logValueModel_.setData(logValues_); } - } // namespace JV2 diff --git a/frontend/plotLogDataWidget.h b/frontend/plotLogDataWidget.h index 5d6e8ee4..8e2a1ace 100644 --- a/frontend/plotLogDataWidget.h +++ b/frontend/plotLogDataWidget.h @@ -4,7 +4,9 @@ #pragma once #include "backend.h" -#include "genericTreeModel.h" +#include "logValue.h" +#include "logValueFilterProxy.h" +#include "logValueModel.h" #include "mainWindow.h" #include "ui_plotLogDataWidget.h" #include @@ -25,14 +27,17 @@ class PlotLogDataWidget : public QWidget private: // User interface object Ui::PlotLogDataWidget ui_; + // Model and proxy for data + LogValueModel logValueModel_; + LogValueFilterProxy logValueFilterProxy_; // Main Window parent MainWindow *mainWindow_{nullptr}; - // Tree model for properties - GenericTreeModel propertyModel_; // Main backend Backend &backend_; // Journal source from which the run numbers came const JournalSource *source_{nullptr}; + // Log values available for plotting + std::vector logValues_; // Run numbers to display on the plot std::vector runNumbers_; diff --git a/frontend/plotLogDataWidget.ui b/frontend/plotLogDataWidget.ui index 4273747b..695175c9 100644 --- a/frontend/plotLogDataWidget.ui +++ b/frontend/plotLogDataWidget.ui @@ -57,7 +57,7 @@ - + 0 From 3fc8dcb4eda3bb0c959392ab972e375c667d6d44 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Mon, 20 Jan 2025 15:56:59 +0000 Subject: [PATCH 06/24] Basic interaction. --- frontend/logValueModel.cpp | 4 +++- frontend/plotLogDataWidget.cpp | 12 ++++++++++-- frontend/plotLogDataWidget.h | 2 ++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/frontend/logValueModel.cpp b/frontend/logValueModel.cpp index 6ffb6710..db4782bd 100644 --- a/frontend/logValueModel.cpp +++ b/frontend/logValueModel.cpp @@ -67,7 +67,7 @@ QVariant LogValueModel::data(const QModelIndex &index, int role) const case (Qt::ToolTipRole): return data.neXuSLocation(); case (Qt::CheckStateRole): - return data.isSelected(); + return data.isSelected() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked; default: return {}; } @@ -92,6 +92,8 @@ bool LogValueModel::setData(const QModelIndex &index, const QVariant &value, int data.setSelected(value.value() == Qt::Checked); + emit(dataChanged(index, index)); + return true; } diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 485eedea..77490619 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -16,8 +16,8 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const ui_.PropertyList->setModel(&logValueFilterProxy_); ui_.PropertyList->setSelectionBehavior(QAbstractItemView::SelectRows); - connect(ui_.PropertyList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, - SLOT(onTreeSelectionChanged(const QItemSelection &, const QItemSelection &))); + connect(&logValueModel_, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QList &)), this, + SLOT(logValuesChanged(const QModelIndex &, const QModelIndex &, const QList &))); // Acquire the available log data backend_.getNeXuSLogValues(source_, runNumbers_, [=](HttpRequestWorker *worker) { handleRetrieveSELogProperties(worker); }); @@ -53,4 +53,12 @@ void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker *worker) logValueModel_.setData(logValues_); } + +// Log value selection changed +void PlotLogDataWidget::logValuesChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) +{ + auto optData = logValueModel_.getData(topLeft); + auto &data = optData->get(); + qDebug() << "Toggled data was " + data.name(); +} } // namespace JV2 diff --git a/frontend/plotLogDataWidget.h b/frontend/plotLogDataWidget.h index 8e2a1ace..196f8a0f 100644 --- a/frontend/plotLogDataWidget.h +++ b/frontend/plotLogDataWidget.h @@ -46,6 +46,8 @@ class PlotLogDataWidget : public QWidget void handleRetrieveSELogProperties(HttpRequestWorker *worker); private slots: + // Log value selection changed + void logValuesChanged(const QModelIndex &, const QModelIndex &, const QList &); signals: }; From 9af416434a07bcc139838a9e0a61056110ebb89b Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 21 Jan 2025 11:54:54 +0000 Subject: [PATCH 07/24] Add per-run storage of basic log value data. --- frontend/CMakeLists.txt | 2 + frontend/logValue.cpp | 11 ++++ frontend/logValue.h | 15 +++++ frontend/logValueData.cpp | 15 +++++ frontend/logValueData.h | 27 +++++++++ frontend/plotLogDataWidget.cpp | 102 +++++++++++++++++++++++++++++++-- frontend/plotLogDataWidget.h | 11 ++-- 7 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 frontend/logValueData.cpp create mode 100644 frontend/logValueData.h diff --git a/frontend/CMakeLists.txt b/frontend/CMakeLists.txt index da67c407..4e65cae1 100644 --- a/frontend/CMakeLists.txt +++ b/frontend/CMakeLists.txt @@ -50,6 +50,8 @@ qt6_add_executable( lock.h logValue.cpp logValue.h + logValueData.cpp + logValueData.h optionalRef.h # Backend backend.cpp diff --git a/frontend/logValue.cpp b/frontend/logValue.cpp index 0ca98d5f..f1e443f0 100644 --- a/frontend/logValue.cpp +++ b/frontend/logValue.cpp @@ -7,6 +7,10 @@ namespace JV2 { LogValue::LogValue(const QString &name, const QString &location) : name_(name), neXuSLocation_(location) {} +/* + * Basic Data + */ + // Return the name of the property const QString &LogValue::name() const { return name_; } @@ -19,4 +23,11 @@ bool LogValue::isSelected() const { return selected_; } // Set whether the property is selected void LogValue::setSelected(bool selected) { selected_ = selected; } +/* + * Run Data + */ + +// Add data for specific run +void LogValue::addData(QString id, LogValueData data) { data_[id] = std::move(data); } + } // namespace JV2 diff --git a/frontend/logValue.h b/frontend/logValue.h index 863bb451..716fe718 100644 --- a/frontend/logValue.h +++ b/frontend/logValue.h @@ -3,6 +3,7 @@ #pragma once +#include "logValueData.h" #include namespace JV2 @@ -14,6 +15,9 @@ class LogValue LogValue(const QString &name, const QString &location = {}); ~LogValue() = default; + /* + * Basic Data + */ private: // Display name of the property QString name_; @@ -31,5 +35,16 @@ class LogValue bool isSelected() const; // Set whether the property is selected void setSelected(bool selected); + + /* + * Run Data + */ + private: + // Log value data per-run + std::map data_; + + public: + // Add data for specific run + void addData(QString id, LogValueData data); }; } // namespace JV2 diff --git a/frontend/logValueData.cpp b/frontend/logValueData.cpp new file mode 100644 index 00000000..07f90680 --- /dev/null +++ b/frontend/logValueData.cpp @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 Team JournalViewer and contributors + +#include "logValueData.h" + +namespace JV2 +{ +LogValueData::LogValueData() {} +LogValueData::LogValueData(const QDateTime &start, const QDateTime &end, const std::vector &epochTimes, + const std::vector &values) + : startTime_(start), endTime_(end), epochTimes_(epochTimes), values_(values) +{ +} + +} // namespace JV2 diff --git a/frontend/logValueData.h b/frontend/logValueData.h new file mode 100644 index 00000000..b8485b16 --- /dev/null +++ b/frontend/logValueData.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 Team JournalViewer and contributors + +#pragma once + +#include "logValueData.h" +#include + +namespace JV2 +{ +// Log value data +class LogValueData +{ + public: + LogValueData(); + LogValueData(const QDateTime &start, const QDateTime &end, const std::vector &epochTimes, + const std::vector &values); + + private: + // Start and end times + QDateTime startTime_, endTime_; + // Time values in milliseconds since epoch + std::vector epochTimes_; + // Values + std::vector values_; +}; +} // namespace JV2 diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 77490619..de4d1e03 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -6,9 +6,9 @@ namespace JV2 { -PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const JournalSource *source, +PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const JournalSource *journalSource, const std::vector &runNumbers) - : QWidget(parent), mainWindow_(parent), backend_(backend), source_(source), runNumbers_(runNumbers), + : QWidget(parent), mainWindow_(parent), backend_(backend), journalSource_(journalSource), runNumbers_(runNumbers), logValueFilterProxy_(logValueModel_) { ui_.setupUi(this); @@ -20,12 +20,17 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const SLOT(logValuesChanged(const QModelIndex &, const QModelIndex &, const QList &))); // Acquire the available log data - backend_.getNeXuSLogValues(source_, runNumbers_, [=](HttpRequestWorker *worker) { handleRetrieveSELogProperties(worker); }); + backend_.getNeXuSLogValues(journalSource_, runNumbers_, + [=](HttpRequestWorker *worker) { handleRetrieveSELogValues(worker); }); } PlotLogDataWidget::~PlotLogDataWidget() {} -void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker *worker) +/* + * Private Functions + */ + +void PlotLogDataWidget::handleRetrieveSELogValues(HttpRequestWorker *worker) { // Check for errors if (mainWindow_->handleRequestError(worker, "retrieving log values from run") != Backend::NoError) @@ -54,11 +59,100 @@ void PlotLogDataWidget::handleRetrieveSELogProperties(HttpRequestWorker *worker) logValueModel_.setData(logValues_); } +// Handle retrieved log value data +void PlotLogDataWidget::handleRetrieveSELogValueData(HttpRequestWorker *worker) +{ + // Check network reply + if (mainWindow_->handleRequestError(worker, "trying to retrieve log value data") != Backend::NoError) + { + ui_.PropertyList->setEnabled(true); + return; + } + + /* The expected result from the backend is as follows: + * + * result = { + * logValue: "name_of_log_value", + * runNumbers: { run1, run2, run3 ... runN } + * data: { + * run1: { + * timeRange: [ datetime, datetime ], + * data: [ (x,y), (x2,y2), ..., (xn,yn) ] + * }, + * ... + * runN: { + * ... + * } + * } + */ + + const auto responseData = worker->jsonResponse().object(); + auto logValueName = responseData["logValue"].toString().section('/', -1); + qDebug() << logValueName; + + // Find the associated LogValue + auto valueIt = std::find_if(logValues_.begin(), logValues_.end(), + [logValueName](auto &value) { return value.name() == logValueName; }); + if (valueIt == logValues_.end()) + { + ui_.PropertyList->setEnabled(true); + return; + } + auto &logValue = *valueIt; + + const auto data = responseData["data"].toObject(); + + foreach (const auto &run, data) + { + // Get the data name (run number) + const auto dataName = run[QString("runNumber")].toString(); + qDebug() << dataName; + + // Extract the time range data + const auto timeRange = run[QString("timeRange")].toArray(); + + // Get start and end times + auto startTime = QDateTime::fromString(timeRange.first()[0].toString(), "yyyy-MM-dd'T'HH:mm:ss"); + auto endTime = QDateTime::fromString(timeRange.first()[1].toString(), "yyyy-MM-dd'T'HH:mm:ss"); + + // Get time / value vectors + // TODO Need to check / detect enumerated data here + const auto fieldDataArray = run[QString("data")].toArray(); + std::vector epochTimes(1024); + std::vector values(1024); + foreach (const auto &dataPair, fieldDataArray) + { + auto dataPairArray = dataPair.toArray(); + epochTimes.push_back(startTime.addMSecs(dataPairArray[0].toDouble() * 1000).toMSecsSinceEpoch()); + values.push_back(dataPairArray[1].toDouble()); + } + + // Push the new data + logValue.addData(dataName, {startTime, endTime, epochTimes, values}); + } + + ui_.PropertyList->setEnabled(true); +} + +/* + * Private Slots + */ + // Log value selection changed void PlotLogDataWidget::logValuesChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) { auto optData = logValueModel_.getData(topLeft); auto &data = optData->get(); qDebug() << "Toggled data was " + data.name(); + + // We might already have the data, so check before we go off retrieving it again... + // TODO + + // Disable the property list for now + ui_.PropertyList->setDisabled(true); + + // Request the log value data + backend_.getNexusLogValueData(journalSource_, runNumbers_, data.neXuSLocation(), + [=](HttpRequestWorker *worker) { handleRetrieveSELogValueData(worker); }); } } // namespace JV2 diff --git a/frontend/plotLogDataWidget.h b/frontend/plotLogDataWidget.h index 196f8a0f..0a146828 100644 --- a/frontend/plotLogDataWidget.h +++ b/frontend/plotLogDataWidget.h @@ -21,7 +21,8 @@ class PlotLogDataWidget : public QWidget Q_OBJECT public: - PlotLogDataWidget(MainWindow *parent, Backend &backend, const JournalSource *source, const std::vector &runNumbers); + PlotLogDataWidget(MainWindow *parent, Backend &backend, const JournalSource *journalSource, + const std::vector &runNumbers); ~PlotLogDataWidget(); private: @@ -35,15 +36,17 @@ class PlotLogDataWidget : public QWidget // Main backend Backend &backend_; // Journal source from which the run numbers came - const JournalSource *source_{nullptr}; + const JournalSource *journalSource_{nullptr}; // Log values available for plotting std::vector logValues_; // Run numbers to display on the plot std::vector runNumbers_; private: - // Handle retrieved log properties data - void handleRetrieveSELogProperties(HttpRequestWorker *worker); + // Handle retrieved log values + void handleRetrieveSELogValues(HttpRequestWorker *worker); + // Handle retrieved log value data + void handleRetrieveSELogValueData(HttpRequestWorker *worker); private slots: // Log value selection changed From 91c8c251f1d2261c1d75155214d65642881fbe5f Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Tue, 21 Jan 2025 14:58:36 +0000 Subject: [PATCH 08/24] Basic plotting is back. --- frontend/logValue.cpp | 3 ++ frontend/logValue.h | 2 ++ frontend/logValueData.cpp | 10 ++++-- frontend/logValueData.h | 12 +++++-- frontend/plotLogDataWidget.cpp | 62 +++++++++++++++++++++++++++------- frontend/plotLogDataWidget.h | 4 +++ 6 files changed, 75 insertions(+), 18 deletions(-) diff --git a/frontend/logValue.cpp b/frontend/logValue.cpp index f1e443f0..dee723a5 100644 --- a/frontend/logValue.cpp +++ b/frontend/logValue.cpp @@ -30,4 +30,7 @@ void LogValue::setSelected(bool selected) { selected_ = selected; } // Add data for specific run void LogValue::addData(QString id, LogValueData data) { data_[id] = std::move(data); } +// Return all data +const std::map &LogValue::data() const { return data_; } + } // namespace JV2 diff --git a/frontend/logValue.h b/frontend/logValue.h index 716fe718..5d2d31f0 100644 --- a/frontend/logValue.h +++ b/frontend/logValue.h @@ -46,5 +46,7 @@ class LogValue public: // Add data for specific run void addData(QString id, LogValueData data); + // Return all data + const std::map &data() const; }; } // namespace JV2 diff --git a/frontend/logValueData.cpp b/frontend/logValueData.cpp index 07f90680..b0defeb8 100644 --- a/frontend/logValueData.cpp +++ b/frontend/logValueData.cpp @@ -6,10 +6,16 @@ namespace JV2 { LogValueData::LogValueData() {} -LogValueData::LogValueData(const QDateTime &start, const QDateTime &end, const std::vector &epochTimes, +LogValueData::LogValueData(const QDateTime &start, const QDateTime &end, const std::vector ×, const std::vector &values) - : startTime_(start), endTime_(end), epochTimes_(epochTimes), values_(values) + : startTime_(start), endTime_(end), times_(times), values_(values) { } +// Return time points in seconds since startTime_ +const std::vector &LogValueData::times() const { return times_; } + +// Return values +const std::vector &LogValueData::values() const { return values_; } + } // namespace JV2 diff --git a/frontend/logValueData.h b/frontend/logValueData.h index b8485b16..8be1550b 100644 --- a/frontend/logValueData.h +++ b/frontend/logValueData.h @@ -13,15 +13,21 @@ class LogValueData { public: LogValueData(); - LogValueData(const QDateTime &start, const QDateTime &end, const std::vector &epochTimes, + LogValueData(const QDateTime &start, const QDateTime &end, const std::vector ×, const std::vector &values); private: // Start and end times QDateTime startTime_, endTime_; - // Time values in milliseconds since epoch - std::vector epochTimes_; + // Time points in seconds relative to startTime_ + std::vector times_; // Values std::vector values_; + + public: + // Return time points in seconds relative to startTime_ + const std::vector ×() const; + // Return values + const std::vector &values() const; }; } // namespace JV2 diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index de4d1e03..8753cda6 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -114,16 +114,19 @@ void PlotLogDataWidget::handleRetrieveSELogValueData(HttpRequestWorker *worker) // Get start and end times auto startTime = QDateTime::fromString(timeRange.first()[0].toString(), "yyyy-MM-dd'T'HH:mm:ss"); auto endTime = QDateTime::fromString(timeRange.first()[1].toString(), "yyyy-MM-dd'T'HH:mm:ss"); + auto startSecs = startTime.toSecsSinceEpoch(); // Get time / value vectors // TODO Need to check / detect enumerated data here const auto fieldDataArray = run[QString("data")].toArray(); - std::vector epochTimes(1024); - std::vector values(1024); + std::vector epochTimes; + epochTimes.reserve(1024); + std::vector values; + values.reserve(1024); foreach (const auto &dataPair, fieldDataArray) { auto dataPairArray = dataPair.toArray(); - epochTimes.push_back(startTime.addMSecs(dataPairArray[0].toDouble() * 1000).toMSecsSinceEpoch()); + epochTimes.push_back(dataPairArray[0].toDouble()); values.push_back(dataPairArray[1].toDouble()); } @@ -131,9 +134,33 @@ void PlotLogDataWidget::handleRetrieveSELogValueData(HttpRequestWorker *worker) logValue.addData(dataName, {startTime, endTime, epochTimes, values}); } + // Add the data to the plot + showData(logValue); + ui_.PropertyList->setEnabled(true); } +// Show data from the supplied LogValue on the plot +void PlotLogDataWidget::showData(const LogValue &logValue) +{ + // Add each contained per-run dataset to the plot + for (auto &&[dataName, data] : logValue.data()) + { + // Create a display group with some default policies + auto group = ui_.Plot->addDisplayGroup(); + group->setSingleColour({255, 0, 200, 255}); + + // Create a renderable and add it to the group + auto *renderable = ui_.Plot->addData1D((dataName + "/" + logValue.name()).toStdString()); + renderable->setData(data.times(), data.values()); + group->addTarget(renderable); + // entities_.emplace_back(renderable); + } +} + +// Hide data from the supplied LogValue from the plot +void PlotLogDataWidget::hideData(const LogValue &logValue) {} + /* * Private Slots */ @@ -141,18 +168,27 @@ void PlotLogDataWidget::handleRetrieveSELogValueData(HttpRequestWorker *worker) // Log value selection changed void PlotLogDataWidget::logValuesChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) { - auto optData = logValueModel_.getData(topLeft); - auto &data = optData->get(); - qDebug() << "Toggled data was " + data.name(); + auto optLogValue = logValueModel_.getData(topLeft); + auto &logValue = optLogValue->get(); + qDebug() << "Toggled data was " + logValue.name(); - // We might already have the data, so check before we go off retrieving it again... - // TODO + // If the logValue has been selected, then either redisplay or retrieve the data + if (logValue.isSelected()) + { + // We might already have the data, so check before we go off retrieving it again... + // TODO - // Disable the property list for now - ui_.PropertyList->setDisabled(true); + // Disable the property list for now + ui_.PropertyList->setDisabled(true); - // Request the log value data - backend_.getNexusLogValueData(journalSource_, runNumbers_, data.neXuSLocation(), - [=](HttpRequestWorker *worker) { handleRetrieveSELogValueData(worker); }); + // Request the log value data + backend_.getNexusLogValueData(journalSource_, runNumbers_, logValue.neXuSLocation(), + [=](HttpRequestWorker *worker) { handleRetrieveSELogValueData(worker); }); + } + else + { + // Just hide the data as this value is no longer selected + hideData(logValue); + } } } // namespace JV2 diff --git a/frontend/plotLogDataWidget.h b/frontend/plotLogDataWidget.h index 0a146828..a5c53ea2 100644 --- a/frontend/plotLogDataWidget.h +++ b/frontend/plotLogDataWidget.h @@ -47,6 +47,10 @@ class PlotLogDataWidget : public QWidget void handleRetrieveSELogValues(HttpRequestWorker *worker); // Handle retrieved log value data void handleRetrieveSELogValueData(HttpRequestWorker *worker); + // Show data from the supplied LogValue on the plot + void showData(const LogValue &logValue); + // Hide data from the supplied LogValue from the plot + void hideData(const LogValue &logValue); private slots: // Log value selection changed From 1d1c95ec7af508a6b0342cb00e7b00218d78dfed Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Wed, 22 Jan 2025 14:53:59 +0000 Subject: [PATCH 09/24] Update QuickPlot. --- frontend/plot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/plot b/frontend/plot index a4661f0f..11478b2a 160000 --- a/frontend/plot +++ b/frontend/plot @@ -1 +1 @@ -Subproject commit a4661f0fa266fcd116a020f959bff645e7f3e1f1 +Subproject commit 11478b2aad1faf86c2920382f8618dd016a2053f From acdade6f14693f8bc68953f97d96b86555dd0ee3 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Wed, 22 Jan 2025 14:54:18 +0000 Subject: [PATCH 10/24] Remove and reuse data. --- frontend/plotLogDataWidget.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 8753cda6..2f71ca9d 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -151,15 +151,20 @@ void PlotLogDataWidget::showData(const LogValue &logValue) group->setSingleColour({255, 0, 200, 255}); // Create a renderable and add it to the group - auto *renderable = ui_.Plot->addData1D((dataName + "/" + logValue.name()).toStdString()); + auto *renderable = ui_.Plot->addData1D((dataName + "/" + logValue.name())); renderable->setData(data.times(), data.values()); group->addTarget(renderable); - // entities_.emplace_back(renderable); } } // Hide data from the supplied LogValue from the plot -void PlotLogDataWidget::hideData(const LogValue &logValue) {} +void PlotLogDataWidget::hideData(const LogValue &logValue) +{ + for (auto &&[dataName, data] : logValue.data()) + { + ui_.Plot->removeData1D((dataName + "/" + logValue.name())); + } +} /* * Private Slots @@ -176,7 +181,11 @@ void PlotLogDataWidget::logValuesChanged(const QModelIndex &topLeft, const QMode if (logValue.isSelected()) { // We might already have the data, so check before we go off retrieving it again... - // TODO + if (!logValue.data().empty()) + { + showData(logValue); + return; + } // Disable the property list for now ui_.PropertyList->setDisabled(true); From 473fc27f6dc407189a65845ffe8d92f45317abb1 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Wed, 22 Jan 2025 14:58:54 +0000 Subject: [PATCH 11/24] Remove old code. --- frontend/CMakeLists.txt | 1 - frontend/visualisation.cpp | 288 ------------------------------------- 2 files changed, 289 deletions(-) delete mode 100644 frontend/visualisation.cpp diff --git a/frontend/CMakeLists.txt b/frontend/CMakeLists.txt index 4e65cae1..17ff536b 100644 --- a/frontend/CMakeLists.txt +++ b/frontend/CMakeLists.txt @@ -75,7 +75,6 @@ qt6_add_executable( nexusInteraction.cpp searching.cpp settings.cpp - visualisation.cpp version.h # Models genericTreeModel.cpp diff --git a/frontend/visualisation.cpp b/frontend/visualisation.cpp deleted file mode 100644 index 143f02c7..00000000 --- a/frontend/visualisation.cpp +++ /dev/null @@ -1,288 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -// Copyright (c) 2025 Team JournalViewer and contributors - -#include "chartView.h" -#include "mainWindow.h" -#include "seLogChooserDialog.h" -#include -#include -#include -#include -#include -#include - -namespace JV2 -{ -// Handle extracted SE log values for plotting -void MainWindow::handlePlotSELogValue(HttpRequestWorker *worker) -{ - // Check for errors - if (handleRequestError(worker, "retrieving log values from run") != NoError) - return; - - // Iterate over logs extracted from the target run data and construct our mapped values - auto *rootItem = new GenericTreeItem({"Log Value", "Full Path"}); - foreach (const auto &log, worker->jsonResponse().array()) - { - auto logArray = log.toArray(); - if (logArray.size() < 2) - continue; - - // First item in the array is the name of the log value set / section - auto *sectionItem = rootItem->appendChild({logArray.first().toString(), ""}); - - // Remove the name item and proceed to iterate over log values - logArray.removeFirst(); - - auto logArrayVar = logArray.toVariantList(); - std::sort(logArrayVar.begin(), logArrayVar.end(), - [](QVariant &v1, QVariant &v2) { return v1.toString() < v2.toString(); }); - - foreach (const auto &block, logArrayVar) - sectionItem->appendChild({block.toString().split("/").last(), block.toString()}); - } - - // Create the dialog - SELogChooserDialog chooserDialog(this, rootItem); - - auto logValue = chooserDialog.getValue(); - if (logValue.isEmpty()) - return; - - // Request the log value data - backend_.getNexusLogValueData(currentJournalSource(), selectedRunNumbers(), logValue, - [=](HttpRequestWorker *worker) { handleCreateSELogPlot(worker); }); -} - -// Handle plotting of SE log data -void MainWindow::handleCreateSELogPlot(HttpRequestWorker *worker) -{ - auto *window = new QWidget; - auto *dateTimeChart = new QChart(); - auto *dateTimeChartView = new ChartView(dateTimeChart, window); - auto *relTimeChart = new QChart(); - auto *relTimeChartView = new ChartView(relTimeChart, window); - - // Check network reply - if (handleRequestError(worker, "trying to graph a log value") != NoError) - return; - - // The expected result from the backend is as follows: - // - // result = { - // logValue: "name_of_log_value", - // runNumbers: { run1, run2, run3 ... runN } - // data: { - // run1: { - // timeRange: [ datetime, datetime ], - // data: [ (x,y), (x2,y2), ..., (xn,yn) ] - // }, - // ... - // runN: { - // ... - // } - // } - - const auto receivedData = worker->jsonResponse().object(); - auto logValueName = receivedData["logValue"].toString().section('/', -1); - qDebug() << logValueName; - - const auto logValueData = receivedData["data"].toObject(); - - auto *timeAxis = new QDateTimeAxis(); - timeAxis->setFormat("yyyy-MM-dd
H:mm:ss"); - dateTimeChart->addAxis(timeAxis, Qt::AlignBottom); - - auto *dateTimeYAxis = new QValueAxis(); - dateTimeYAxis->setRange(0, 0); - - auto *dateTimeStringAxis = new QCategoryAxis(); - QStringList categoryValues; - - auto *relTimeXAxis = new QValueAxis(); - relTimeXAxis->setTitleText("Relative Time (s)"); - relTimeChart->addAxis(relTimeXAxis, Qt::AlignBottom); - - auto *relTimeYAxis = new QValueAxis(); - - auto *relTimeStringAxis = new QCategoryAxis(); - - QList chartFields; - bool firstRun = true; - foreach (const auto &run, logValueData) - { - // Get the data name (run number) - const auto dataName = run[QString("runNumber")].toString(); - qDebug() << run[QString("runNumber")].toString(); - - // Extract the time range data - const auto timeRange = run[QString("timeRange")].toArray(); - - auto startTime = QDateTime::fromString(timeRange.first()[0].toString(), "yyyy-MM-dd'T'HH:mm:ss"); - auto endTime = QDateTime::fromString(timeRange.first()[1].toString(), "yyyy-MM-dd'T'HH:mm:ss"); - - if (firstRun) - { - timeAxis->setRange(startTime, endTime); - relTimeXAxis->setRange(0, 0); - } - - const auto fieldDataArray = run[QString("data")].toArray(); - - // foreach (const auto &fieldData, runFieldsArray) - // { - // auto fieldDataArray = fieldData.toArray(); - // fieldDataArray.removeFirst(); - // if (!fieldDataArray.first()[1].isString()) - // break; - // foreach (const auto &dataPair, fieldDataArray) - // { - // auto dataPairArray = dataPair.toArray(); - // categoryValues.append(dataPairArray[1].toString()); - // } - // } - - if (!categoryValues.isEmpty()) - { - categoryValues.removeDuplicates(); - categoryValues.sort(); - } - if (firstRun) - { - if (!categoryValues.isEmpty()) - { - dateTimeChart->addAxis(dateTimeStringAxis, Qt::AlignLeft); - relTimeChart->addAxis(relTimeStringAxis, Qt::AlignLeft); - } - else - { - dateTimeChart->addAxis(dateTimeYAxis, Qt::AlignLeft); - relTimeChart->addAxis(relTimeYAxis, Qt::AlignLeft); - } - firstRun = false; - } - - // For each plot point - auto *dateSeries = new QLineSeries(); - auto *relSeries = new QLineSeries(); - - connect(dateSeries, &QLineSeries::hovered, - [=](const QPointF point, bool hovered) { dateTimeChartView->setHovered(point, hovered, dateSeries->name()); }); - connect(dateTimeChartView, SIGNAL(showCoordinates(qreal, qreal, QString)), this, - SLOT(showStatus(qreal, qreal, QString))); - connect(dateTimeChartView, SIGNAL(clearCoordinates()), statusBar(), SLOT(clearMessage())); - connect(relSeries, &QLineSeries::hovered, - [=](const QPointF point, bool hovered) { relTimeChartView->setHovered(point, hovered, relSeries->name()); }); - connect(relTimeChartView, SIGNAL(showCoordinates(qreal, qreal, QString)), this, - SLOT(showStatus(qreal, qreal, QString))); - connect(relTimeChartView, SIGNAL(clearCoordinates()), statusBar(), SLOT(clearMessage())); - - // Set dateSeries ID - if (!chartFields.contains(logValueName)) - chartFields.append(logValueName); - dateSeries->setName(dataName); - relSeries->setName(dataName); - - if (fieldDataArray.first()[1].isString()) - { - foreach (const auto &dataPair, fieldDataArray) - { - auto dataPairArray = dataPair.toArray(); - dateSeries->append(startTime.addSecs(dataPairArray[0].toDouble()).toMSecsSinceEpoch(), - categoryValues.indexOf(dataPairArray[1].toString())); - relSeries->append(dataPairArray[0].toDouble(), categoryValues.indexOf(dataPairArray[1].toString())); - } - } - else - { - foreach (const auto &dataPair, fieldDataArray) - { - auto dataPairArray = dataPair.toArray(); - dateSeries->append(startTime.addSecs(dataPairArray[0].toDouble()).toMSecsSinceEpoch(), - dataPairArray[1].toDouble()); - relSeries->append(dataPairArray[0].toDouble(), dataPairArray[1].toDouble()); - if (dateTimeYAxis->min() == 0 && dateTimeYAxis->max() == 0) - dateTimeYAxis->setRange(dataPairArray[1].toDouble(), dataPairArray[1].toDouble()); - if (dataPairArray[1].toDouble() < dateTimeYAxis->min()) - dateTimeYAxis->setMin(dataPairArray[1].toDouble()); - if (dataPairArray[1].toDouble() > dateTimeYAxis->max()) - dateTimeYAxis->setMax(dataPairArray[1].toDouble()); - } - } - if (startTime.addSecs(startTime.secsTo(QDateTime::fromMSecsSinceEpoch(dateSeries->at(0).x()))) < timeAxis->min()) - timeAxis->setMin(startTime.addSecs(startTime.secsTo(QDateTime::fromMSecsSinceEpoch(dateSeries->at(0).x())))); - if (endTime > timeAxis->max()) - timeAxis->setMax(endTime); - - if (relSeries->at(0).x() < relTimeXAxis->min()) - relTimeXAxis->setMin(relSeries->at(0).x()); - if (relSeries->at(relSeries->count() - 1).x() > relTimeXAxis->max()) - relTimeXAxis->setMax(relSeries->at(relSeries->count() - 1).x()); - - dateTimeChart->addSeries(dateSeries); - dateSeries->attachAxis(timeAxis); - relTimeChart->addSeries(relSeries); - relSeries->attachAxis(relTimeXAxis); - if (categoryValues.isEmpty()) - { - dateSeries->attachAxis(dateTimeYAxis); - relSeries->attachAxis(relTimeYAxis); - } - else - { - dateSeries->attachAxis(dateTimeStringAxis); - relSeries->attachAxis(relTimeStringAxis); - } - } - - if (!categoryValues.isEmpty()) - { - dateTimeStringAxis->setRange(0, categoryValues.count() - 1); - relTimeStringAxis->setRange(0, categoryValues.count() - 1); - for (auto i = 0; i < categoryValues.count(); i++) - { - dateTimeStringAxis->append(categoryValues[i], i); - relTimeStringAxis->append(categoryValues[i], i); - } - dateTimeStringAxis->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue); - relTimeStringAxis->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue); - } - - relTimeYAxis->setRange(dateTimeYAxis->min(), dateTimeYAxis->max()); - - auto *gridLayout = new QGridLayout(window); - auto *axisToggleCheck = new QCheckBox("Plot relative to run start times", window); - connect(axisToggleCheck, SIGNAL(stateChanged(int)), this, SLOT(toggleAxis(int))); - - gridLayout->addWidget(dateTimeChartView, 1, 0, -1, -1); - gridLayout->addWidget(relTimeChartView, 1, 0, -1, -1); - relTimeChartView->hide(); - gridLayout->addWidget(axisToggleCheck, 0, 0); - QString tabName; - for (auto i = 0; i < chartFields.size(); i++) - { - tabName += chartFields[i]; - if (i < chartFields.size() - 1) - tabName += ","; - } - if (!categoryValues.isEmpty()) - { - dateTimeStringAxis->setTitleText(tabName); - relTimeStringAxis->setTitleText(tabName); - } - else - { - dateTimeYAxis->setTitleText(tabName); - relTimeYAxis->setTitleText(tabName); - } - ui_.MainTabs->addTab(window, tabName); - QString runs; - for (auto series : dateTimeChart->series()) - runs.append(series->name() + ", "); - runs.chop(2); - QString toolTip = tabName + "\n" + runs; - ui_.MainTabs->setTabToolTip(ui_.MainTabs->count() - 1, toolTip); - ui_.MainTabs->setCurrentIndex(ui_.MainTabs->count() - 1); - dateTimeChartView->setFocus(); -} -} // namespace JV2 From 36c71b5e81311a2abaf8c4e30ed70c26a49a4016 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Wed, 22 Jan 2025 15:28:16 +0000 Subject: [PATCH 12/24] Link plot to tab, updating title. --- frontend/data.cpp | 4 +++- frontend/mainWindow.cpp | 9 +++++++++ frontend/mainWindow.h | 3 +++ frontend/plotLogDataWidget.cpp | 25 +++++++++++++++++++++++++ frontend/plotLogDataWidget.h | 6 ++++++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/frontend/data.cpp b/frontend/data.cpp index b80c0954..bf595d67 100644 --- a/frontend/data.cpp +++ b/frontend/data.cpp @@ -204,7 +204,9 @@ void MainWindow::runDataContextMenuRequested(QPoint pos) else if (selectedAction == plotSELog) { auto *plot = new PlotLogDataWidget(this, backend_, currentJournalSource(), selectedRunNumbers()); - ui_.MainTabs->addTab(plot, "Test"); + auto index = ui_.MainTabs->addTab(plot, plot->summaryText()); + connect(plot, SIGNAL(summaryTextChanged(QString)), this, SLOT(setTabTitle(QString))); + ui_.MainTabs->setCurrentIndex(index); } else if (selectedAction == plotDetector) { diff --git a/frontend/mainWindow.cpp b/frontend/mainWindow.cpp index 7d4ae348..1a4c9cba 100644 --- a/frontend/mainWindow.cpp +++ b/frontend/mainWindow.cpp @@ -134,6 +134,15 @@ void MainWindow::updateForCurrentSource(std::optionalsetEnabled(currentJournalSource_->type() == JournalSource::IndexingType::Generated); } +// Set tab title +void MainWindow::setTabTitle(const QString &title) +{ + // Get sender widget and find tab index + auto index = ui_.MainTabs->indexOf(dynamic_cast(sender())); + if (index != -1) + ui_.MainTabs->setTabText(index, title); +} + void MainWindow::removeTab(int index) { delete ui_.MainTabs->widget(index); } /* diff --git a/frontend/mainWindow.h b/frontend/mainWindow.h index 18574b34..13ed46c3 100644 --- a/frontend/mainWindow.h +++ b/frontend/mainWindow.h @@ -49,6 +49,9 @@ class MainWindow : public QMainWindow void updateForCurrentSource(std::optional newState = {}); private slots: + // Set tab title + void setTabTitle(const QString &title); + // Remove tab void removeTab(int index); // Notification point for backend startup void backendStarted(const QString &result); diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 2f71ca9d..07566a2c 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -155,6 +155,8 @@ void PlotLogDataWidget::showData(const LogValue &logValue) renderable->setData(data.times(), data.values()); group->addTarget(renderable); } + + emit(summaryTextChanged(summaryText(logValue.name()))); } // Hide data from the supplied LogValue from the plot @@ -200,4 +202,27 @@ void PlotLogDataWidget::logValuesChanged(const QModelIndex &topLeft, const QMode hideData(logValue); } } + +/* + * Public + */ + +// Create summary text for the plot +QString PlotLogDataWidget::summaryText(const QString &lastProperty) const +{ + if (runNumbers_.empty()) + return "Nothing"; + + // Run number (count) + auto result = QString("%1").arg(runNumbers_.front()); + if (runNumbers_.size() > 1) + result += QString("(+%1)").arg(runNumbers_.size() - 1); + + // Last Property + if (!lastProperty.isEmpty()) + result += QString(" / %1").arg(lastProperty); + + return result; +} + } // namespace JV2 diff --git a/frontend/plotLogDataWidget.h b/frontend/plotLogDataWidget.h index a5c53ea2..87b8d928 100644 --- a/frontend/plotLogDataWidget.h +++ b/frontend/plotLogDataWidget.h @@ -56,6 +56,12 @@ class PlotLogDataWidget : public QWidget // Log value selection changed void logValuesChanged(const QModelIndex &, const QModelIndex &, const QList &); + public: + // Create summary text for the plot + QString summaryText(const QString &lastProperty = {}) const; + signals: + // Summary text updated + void summaryTextChanged(QString); }; } // namespace JV2 From 74aae55a2822a0adbc0de3dbe024040db03fbe4c Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Wed, 22 Jan 2025 15:52:06 +0000 Subject: [PATCH 13/24] More dead code removal. --- frontend/mainWindow.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/frontend/mainWindow.h b/frontend/mainWindow.h index 13ed46c3..0a5e7d58 100644 --- a/frontend/mainWindow.h +++ b/frontend/mainWindow.h @@ -285,15 +285,6 @@ class MainWindow : public QMainWindow // Handle search result void handleSearchResult(HttpRequestWorker *worker); - /* - * Visualisation - */ - private: - // Handle extracted SE log values for plotting - void handlePlotSELogValue(HttpRequestWorker *worker); - // Handle plotting of SE log data - void handleCreateSELogPlot(HttpRequestWorker *worker); - /* * Nexus Interaction Stuff To Be Organised */ From 3a1bb3bee96e7c2801d7b63a5075255028da5836 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Wed, 22 Jan 2025 16:03:00 +0000 Subject: [PATCH 14/24] Autoscale on first data addition. --- frontend/plotLogDataWidget.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 07566a2c..fe1d4b02 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -143,6 +143,9 @@ void PlotLogDataWidget::handleRetrieveSELogValueData(HttpRequestWorker *worker) // Show data from the supplied LogValue on the plot void PlotLogDataWidget::showData(const LogValue &logValue) { + // Record whether the plot is currently empty + auto plotEmpty = ui_.Plot->nDataEntities() == 0; + // Add each contained per-run dataset to the plot for (auto &&[dataName, data] : logValue.data()) { @@ -156,6 +159,10 @@ void PlotLogDataWidget::showData(const LogValue &logValue) group->addTarget(renderable); } + // If the plot was empty when we started, auto-scale it now + if (plotEmpty) + ui_.Plot->showAllData(); + emit(summaryTextChanged(summaryText(logValue.name()))); } From fd058e4e16b55d47bb7176bd902b5d7e8927badd Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 24 Jan 2025 10:13:18 +0000 Subject: [PATCH 15/24] Sorting, new layout. --- frontend/CMakeLists.txt | 4 + frontend/logValue.h | 2 +- frontend/logValueFilterProxy.cpp | 20 +++- frontend/logValueFilterProxy.h | 10 +- frontend/logValueGroup.cpp | 23 ++++ frontend/logValueGroup.h | 35 ++++++ frontend/logValueGroupModel.cpp | 108 ++++++++++++++++++ frontend/logValueGroupModel.h | 43 +++++++ frontend/plotLogDataWidget.cpp | 60 +++++++--- frontend/plotLogDataWidget.h | 17 ++- frontend/plotLogDataWidget.ui | 188 +++++++++++++++++++++++++++++-- 11 files changed, 472 insertions(+), 38 deletions(-) create mode 100644 frontend/logValueGroup.cpp create mode 100644 frontend/logValueGroup.h create mode 100644 frontend/logValueGroupModel.cpp create mode 100644 frontend/logValueGroupModel.h diff --git a/frontend/CMakeLists.txt b/frontend/CMakeLists.txt index 17ff536b..388c0f72 100644 --- a/frontend/CMakeLists.txt +++ b/frontend/CMakeLists.txt @@ -52,6 +52,8 @@ qt6_add_executable( logValue.h logValueData.cpp logValueData.h + logValueGroup.cpp + logValueGroup.h optionalRef.h # Backend backend.cpp @@ -89,6 +91,8 @@ qt6_add_executable( journalSourceModel.h logValueFilterProxy.cpp logValueFilterProxy.h + logValueGroupModel.cpp + logValueGroupModel.h logValueModel.cpp logValueModel.h runDataModel.cpp diff --git a/frontend/logValue.h b/frontend/logValue.h index 5d2d31f0..b5b2bee5 100644 --- a/frontend/logValue.h +++ b/frontend/logValue.h @@ -12,7 +12,7 @@ namespace JV2 class LogValue { public: - LogValue(const QString &name, const QString &location = {}); + LogValue(const QString &name = {}, const QString &location = {}); ~LogValue() = default; /* diff --git a/frontend/logValueFilterProxy.cpp b/frontend/logValueFilterProxy.cpp index 43d79a3c..8ee9bd53 100644 --- a/frontend/logValueFilterProxy.cpp +++ b/frontend/logValueFilterProxy.cpp @@ -15,13 +15,25 @@ LogValueFilterProxy::LogValueFilterProxy(LogValueModel &logValueModel) : logValu bool LogValueFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { - return (searchString_.isEmpty() || logValueModel_.getData(sourceRow)->get().name().contains(searchString_)); + const auto &logValue = logValueModel_.getData(sourceRow)->get(); + + if (showSelectedOnly_ && !logValue.isSelected()) + return false; + + return (filterString_.isEmpty() || logValueModel_.getData(sourceRow)->get().name().contains(filterString_)); +} + +// Set filter string, or empty string to disable +void LogValueFilterProxy::setFilterString(const QString &search) +{ + filterString_ = search; + invalidate(); } -// Set search string -void LogValueFilterProxy::setSearchString(const QString &search) +// Set whether to show only selected log values +void LogValueFilterProxy::setShowSelectedOnly(bool selectedOnly) { - searchString_ = search; + showSelectedOnly_ = selectedOnly; invalidate(); } diff --git a/frontend/logValueFilterProxy.h b/frontend/logValueFilterProxy.h index 60fbf730..0a785f83 100644 --- a/frontend/logValueFilterProxy.h +++ b/frontend/logValueFilterProxy.h @@ -23,13 +23,17 @@ class LogValueFilterProxy : public QSortFilterProxyModel // Target model LogValueModel &logValueModel_; // Search string - QString searchString_; + QString filterString_; + // Whether to show only selected log values + bool showSelectedOnly_{false}; protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; public: - // Set search string - void setSearchString(const QString &search); + // Set filter string, or empty string to disable + void setFilterString(const QString &search); + // Set whether to show only selected log values + void setShowSelectedOnly(bool selectedOnly); }; } // namespace JV2 diff --git a/frontend/logValueGroup.cpp b/frontend/logValueGroup.cpp new file mode 100644 index 00000000..c05e7fdc --- /dev/null +++ b/frontend/logValueGroup.cpp @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 Team JournalViewer and contributors + +#include "logValueGroup.h" + +namespace JV2 +{ +LogValueGroup::LogValueGroup(const QString &name, bool selected) : name_(name), selected_(selected) {} + +/* + * Basic Data + */ + +// Return the name of the property +const QString &LogValueGroup::name() const { return name_; } + +// Return whether the property is selected +bool LogValueGroup::isSelected() const { return selected_; } + +// Set whether the property is selected +void LogValueGroup::setSelected(bool selected) { selected_ = selected; } + +} // namespace JV2 diff --git a/frontend/logValueGroup.h b/frontend/logValueGroup.h new file mode 100644 index 00000000..4110989c --- /dev/null +++ b/frontend/logValueGroup.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2025 Team JournalViewer and contributors + +#pragma once + +#include "logValueData.h" +#include + +namespace JV2 +{ +// Log Value Group +class LogValueGroup +{ + public: + LogValueGroup(const QString &name, bool selected = false); + ~LogValueGroup() = default; + + /* + * Basic Data + */ + private: + // Display name of the group + QString name_; + // Whether this group is selected + bool selected_{false}; + + public: + // Return the name of the property + const QString &name() const; + // Return whether the property is selected + bool isSelected() const; + // Set whether the property is selected + void setSelected(bool selected); +}; +} // namespace JV2 diff --git a/frontend/logValueGroupModel.cpp b/frontend/logValueGroupModel.cpp new file mode 100644 index 00000000..799f5c63 --- /dev/null +++ b/frontend/logValueGroupModel.cpp @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team LogValueGroupViewer and contributors + +#include "logValueGroupModel.h" + +namespace JV2 +{ +// Model to handle json data in table view +LogValueGroupModel::LogValueGroupModel() : QAbstractListModel() {} + +/* + * Public Functions + */ + +// Set the source data for the model +void LogValueGroupModel::setData(OptionalReferenceWrapper> properties) +{ + beginResetModel(); + data_ = properties; + endResetModel(); +} + +// Get LogValueGroup row specified +OptionalReferenceWrapper LogValueGroupModel::getData(int row) const +{ + if (!data_ || row == -1 || row >= rowCount()) + return {}; + + return data_->get()[row]; +} + +// Get LogValueGroup at index specified +OptionalReferenceWrapper LogValueGroupModel::getData(const QModelIndex &index) const +{ + return getData(index.row()); +} + +/* + * QAbstractListModel Overrides + */ + +int LogValueGroupModel::rowCount(const QModelIndex &parent) const { return data_ ? data_->get().size() : 0; } + +int LogValueGroupModel::columnCount(const QModelIndex &parent) const { return 1; } + +Qt::ItemFlags LogValueGroupModel::flags(const QModelIndex &index) const +{ + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; +} + +QVariant LogValueGroupModel::data(const QModelIndex &index, int role) const +{ + if (!data_) + return {}; + + // Column zero is the only relevant one + if (index.column() != 0) + return {}; + + auto optData = getData(index); + if (!optData) + return {}; + auto &data = optData->get(); + + switch (role) + { + case (Qt::DisplayRole): + case (Qt::EditRole): + return data.name(); + case (Qt::CheckStateRole): + return data.isSelected() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked; + default: + return {}; + } +} + +bool LogValueGroupModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!data_) + return false; + + // Column zero is the only relevant one + if (index.column() != 0) + return false; + + auto optData = getData(index); + if (!optData) + return {}; + auto &data = optData->get(); + + if (role != Qt::CheckStateRole) + return false; + + data.setSelected(value.value() == Qt::Checked); + + emit(dataChanged(index, index)); + + return true; +} + +QVariant LogValueGroupModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + return {}; + + return "Log Value"; +} +} // namespace JV2 diff --git a/frontend/logValueGroupModel.h b/frontend/logValueGroupModel.h new file mode 100644 index 00000000..1ba84f02 --- /dev/null +++ b/frontend/logValueGroupModel.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (c) 2024 Team LogValueGroupViewer and contributors + +#pragma once + +#include "logValueGroup.h" +#include "optionalRef.h" +#include + +namespace JV2 +{ +// Model for LogValueGroup definitions +class LogValueGroupModel : public QAbstractListModel +{ + public: + LogValueGroupModel(); + + private: + // LogValueGroup data for the model + OptionalReferenceWrapper> data_; + // Whether to show availability as checkboxes + bool showAvailability_{false}; + + public: + // Set the source data for the model + void setData(OptionalReferenceWrapper> properties); + // Get LogValueGroup at row specified + OptionalReferenceWrapper getData(int row) const; + // Get LogValueGroup at index specified + OptionalReferenceWrapper getData(const QModelIndex &index) const; + + /* + * QAbstractTableModel Overrides + */ + public: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; +}; +} // namespace JV2 diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index fe1d4b02..631ea0cb 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -3,29 +3,39 @@ #include "plotLogDataWidget.h" #include +#include namespace JV2 { PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const JournalSource *journalSource, const std::vector &runNumbers) : QWidget(parent), mainWindow_(parent), backend_(backend), journalSource_(journalSource), runNumbers_(runNumbers), - logValueFilterProxy_(logValueModel_) + availableLogValueFilterProxy_(logValueModel_), shownLogValueFilterProxy_(logValueModel_) { ui_.setupUi(this); - ui_.PropertyList->setModel(&logValueFilterProxy_); - ui_.PropertyList->setSelectionBehavior(QAbstractItemView::SelectRows); + // Create LogValueGroups for each run we've been given and set up the relevant list model + for (auto runNumber : runNumbers_) + logValueGroups_.emplace_back(QString::number(runNumber), true); + ui_.RunNumberList->setModel(&logValueGroupModel_); + logValueGroupModel_.setData(logValueGroups_); + + connect(&logValueGroupModel_, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QList &)), this, + SLOT(logValueGroupChanged(const QModelIndex &, const QModelIndex &, const QList &))); + + // Set up the available log value list and model + availableLogValueFilterProxy_.sort(0); + ui_.AvailableLogValueList->setModel(&availableLogValueFilterProxy_); + ui_.AvailableLogValueList->setSelectionBehavior(QAbstractItemView::SelectRows); connect(&logValueModel_, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QList &)), this, - SLOT(logValuesChanged(const QModelIndex &, const QModelIndex &, const QList &))); + SLOT(logValueChanged(const QModelIndex &, const QModelIndex &, const QList &))); // Acquire the available log data backend_.getNeXuSLogValues(journalSource_, runNumbers_, [=](HttpRequestWorker *worker) { handleRetrieveSELogValues(worker); }); } -PlotLogDataWidget::~PlotLogDataWidget() {} - /* * Private Functions */ @@ -36,9 +46,8 @@ void PlotLogDataWidget::handleRetrieveSELogValues(HttpRequestWorker *worker) if (mainWindow_->handleRequestError(worker, "retrieving log values from run") != Backend::NoError) return; - // Iterate over log values extracted from the target run data and create a vector of all those available - logValues_.reserve(1024); - logValues_.clear(); + // Iterate over log values extracted from the target run data and create a unique set of those available + std::set uniqueValues; foreach (const auto &log, worker->jsonResponse().array()) { auto logArray = log.toArray(); @@ -49,13 +58,16 @@ void PlotLogDataWidget::handleRetrieveSELogValues(HttpRequestWorker *worker) logArray.removeFirst(); auto logArrayVar = logArray.toVariantList(); - std::sort(logArrayVar.begin(), logArrayVar.end(), - [](QVariant &v1, QVariant &v2) { return v1.toString() < v2.toString(); }); - foreach (const auto &block, logArrayVar) - logValues_.emplace_back(block.toString().split("/").last(), block.toString()); + uniqueValues.insert(block.toString()); } + // Copy the set to our vector + logValues_.clear(); + logValues_.resize(uniqueValues.size()); + std::transform(uniqueValues.begin(), uniqueValues.end(), logValues_.begin(), + [](const auto &blockPath) { return LogValue(blockPath.split("/").last(), blockPath); }); + logValueModel_.setData(logValues_); } @@ -65,7 +77,7 @@ void PlotLogDataWidget::handleRetrieveSELogValueData(HttpRequestWorker *worker) // Check network reply if (mainWindow_->handleRequestError(worker, "trying to retrieve log value data") != Backend::NoError) { - ui_.PropertyList->setEnabled(true); + ui_.AvailableLogValueList->setEnabled(true); return; } @@ -95,7 +107,7 @@ void PlotLogDataWidget::handleRetrieveSELogValueData(HttpRequestWorker *worker) [logValueName](auto &value) { return value.name() == logValueName; }); if (valueIt == logValues_.end()) { - ui_.PropertyList->setEnabled(true); + ui_.AvailableLogValueList->setEnabled(true); return; } auto &logValue = *valueIt; @@ -137,7 +149,7 @@ void PlotLogDataWidget::handleRetrieveSELogValueData(HttpRequestWorker *worker) // Add the data to the plot showData(logValue); - ui_.PropertyList->setEnabled(true); + ui_.AvailableLogValueList->setEnabled(true); } // Show data from the supplied LogValue on the plot @@ -180,7 +192,7 @@ void PlotLogDataWidget::hideData(const LogValue &logValue) */ // Log value selection changed -void PlotLogDataWidget::logValuesChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) +void PlotLogDataWidget::logValueChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) { auto optLogValue = logValueModel_.getData(topLeft); auto &logValue = optLogValue->get(); @@ -197,7 +209,7 @@ void PlotLogDataWidget::logValuesChanged(const QModelIndex &topLeft, const QMode } // Disable the property list for now - ui_.PropertyList->setDisabled(true); + ui_.AvailableLogValueList->setDisabled(true); // Request the log value data backend_.getNexusLogValueData(journalSource_, runNumbers_, logValue.neXuSLocation(), @@ -210,6 +222,18 @@ void PlotLogDataWidget::logValuesChanged(const QModelIndex &topLeft, const QMode } } +// Log value group selection changed +void PlotLogDataWidget::logValueGroupChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QList &roles) +{ + auto optGroup = logValueGroupModel_.getData(topLeft); + auto &group = optGroup->get(); + + // We have tagged every data entity on the plot as "RunNumber/Property" so we just need to request that those + // with a matching tag are shown / hidden + ui_.Plot->setDataEnabled(QRegularExpression(QString("^%1/.*").arg(group.name())), group.isSelected()); +} + /* * Public */ diff --git a/frontend/plotLogDataWidget.h b/frontend/plotLogDataWidget.h index 87b8d928..409b6762 100644 --- a/frontend/plotLogDataWidget.h +++ b/frontend/plotLogDataWidget.h @@ -6,6 +6,8 @@ #include "backend.h" #include "logValue.h" #include "logValueFilterProxy.h" +#include "logValueGroup.h" +#include "logValueGroupModel.h" #include "logValueModel.h" #include "mainWindow.h" #include "ui_plotLogDataWidget.h" @@ -23,14 +25,17 @@ class PlotLogDataWidget : public QWidget public: PlotLogDataWidget(MainWindow *parent, Backend &backend, const JournalSource *journalSource, const std::vector &runNumbers); - ~PlotLogDataWidget(); + ~PlotLogDataWidget() = default; private: // User interface object Ui::PlotLogDataWidget ui_; - // Model and proxy for data + // Model and proxies for log values LogValueModel logValueModel_; - LogValueFilterProxy logValueFilterProxy_; + LogValueFilterProxy availableLogValueFilterProxy_; + LogValueFilterProxy shownLogValueFilterProxy_; + // Model and proxy for log value groups + LogValueGroupModel logValueGroupModel_; // Main Window parent MainWindow *mainWindow_{nullptr}; // Main backend @@ -41,6 +46,8 @@ class PlotLogDataWidget : public QWidget std::vector logValues_; // Run numbers to display on the plot std::vector runNumbers_; + // Log value groups (per run-number) + std::vector logValueGroups_; private: // Handle retrieved log values @@ -54,7 +61,9 @@ class PlotLogDataWidget : public QWidget private slots: // Log value selection changed - void logValuesChanged(const QModelIndex &, const QModelIndex &, const QList &); + void logValueChanged(const QModelIndex &, const QModelIndex &, const QList &); + // Log value group selection changed + void logValueGroupChanged(const QModelIndex &, const QModelIndex &, const QList &); public: // Create summary text for the plot diff --git a/frontend/plotLogDataWidget.ui b/frontend/plotLogDataWidget.ui index 695175c9..f1cef836 100644 --- a/frontend/plotLogDataWidget.ui +++ b/frontend/plotLogDataWidget.ui @@ -10,10 +10,25 @@ 0 0 1086 - 586 + 1285
- + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + @@ -46,24 +61,179 @@ + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + - + - + 0 1 + + Run Numbers + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + 0 + 1 + + + + + - + - + 0 - 3 + 1 + + Shown Log Values + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + 0 + 3 + + + + + + + + + + + + 0 + 2 + + + + Available Log Values + + + + 4 + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + + + 0 + 0 + + + + Filter run data + + + + + + + + 0 + 0 + + + + + 20 + 16777215 + + + + + + + + :/icons/cross.svg:/icons/cross.svg + + + + + + + + + + 0 + 3 + + + + + @@ -80,6 +250,8 @@ 1 - + + + From c9c2f57929b32886651968d93745687110a99ec2 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 24 Jan 2025 10:33:55 +0000 Subject: [PATCH 16/24] Working correctly, bad UX. --- frontend/logValueFilterProxy.cpp | 11 +++++++---- frontend/logValueFilterProxy.h | 15 +++++++++++---- frontend/plotLogDataWidget.cpp | 11 +++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/frontend/logValueFilterProxy.cpp b/frontend/logValueFilterProxy.cpp index 8ee9bd53..9894944f 100644 --- a/frontend/logValueFilterProxy.cpp +++ b/frontend/logValueFilterProxy.cpp @@ -17,7 +17,10 @@ bool LogValueFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sou { const auto &logValue = logValueModel_.getData(sourceRow)->get(); - if (showSelectedOnly_ && !logValue.isSelected()) + // Deal with selected state first + if (selectedValueBehaviour_ == SelectedStateBehaviour::HideSelected && logValue.isSelected()) + return false; + else if (selectedValueBehaviour_ == SelectedStateBehaviour::ShowOnlySelected && !logValue.isSelected()) return false; return (filterString_.isEmpty() || logValueModel_.getData(sourceRow)->get().name().contains(filterString_)); @@ -30,10 +33,10 @@ void LogValueFilterProxy::setFilterString(const QString &search) invalidate(); } -// Set whether to show only selected log values -void LogValueFilterProxy::setShowSelectedOnly(bool selectedOnly) +// Set selected value behaviour +void LogValueFilterProxy::setSelectedStateBehaviour(SelectedStateBehaviour behaviour) { - showSelectedOnly_ = selectedOnly; + selectedValueBehaviour_ = behaviour; invalidate(); } diff --git a/frontend/logValueFilterProxy.h b/frontend/logValueFilterProxy.h index 0a785f83..d794f154 100644 --- a/frontend/logValueFilterProxy.h +++ b/frontend/logValueFilterProxy.h @@ -18,14 +18,21 @@ class LogValueFilterProxy : public QSortFilterProxyModel public: LogValueFilterProxy(LogValueModel &journalSourceModel); + // Selected Sate Behaviour + enum class SelectedStateBehaviour + { + Ignore, + HideSelected, + ShowOnlySelected + }; private: // Target model LogValueModel &logValueModel_; // Search string QString filterString_; - // Whether to show only selected log values - bool showSelectedOnly_{false}; + // Behaviour for selected log values in the model + SelectedStateBehaviour selectedValueBehaviour_{SelectedStateBehaviour::Ignore}; protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; @@ -33,7 +40,7 @@ class LogValueFilterProxy : public QSortFilterProxyModel public: // Set filter string, or empty string to disable void setFilterString(const QString &search); - // Set whether to show only selected log values - void setShowSelectedOnly(bool selectedOnly); + // Set selected value behaviour + void setSelectedStateBehaviour(SelectedStateBehaviour behaviour); }; } // namespace JV2 diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 631ea0cb..a4dd84ba 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -23,7 +23,18 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const connect(&logValueGroupModel_, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QList &)), this, SLOT(logValueGroupChanged(const QModelIndex &, const QModelIndex &, const QList &))); + // Set up the shown log value list and model + shownLogValueFilterProxy_.setSelectedStateBehaviour(LogValueFilterProxy::SelectedStateBehaviour::ShowOnlySelected); + shownLogValueFilterProxy_.sort(0); + ui_.ShownLogValueList->setModel(&shownLogValueFilterProxy_); + ui_.ShownLogValueList->setSelectionBehavior(QAbstractItemView::SelectRows); + + // Acquire the available log data + backend_.getNeXuSLogValues(journalSource_, runNumbers_, + [=](HttpRequestWorker *worker) { handleRetrieveSELogValues(worker); }); + // Set up the available log value list and model + availableLogValueFilterProxy_.setSelectedStateBehaviour(LogValueFilterProxy::SelectedStateBehaviour::HideSelected); availableLogValueFilterProxy_.sort(0); ui_.AvailableLogValueList->setModel(&availableLogValueFilterProxy_); ui_.AvailableLogValueList->setSelectionBehavior(QAbstractItemView::SelectRows); From 89ffb0e12daf92f2af8ce2bbaae2f44cc9b2ff75 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 24 Jan 2025 14:06:00 +0000 Subject: [PATCH 17/24] ADd icons. --- frontend/icons/add.svg | 55 +++++++++++++++++++++++++++++ frontend/icons/down.svg | 69 +++++++++++++++++++++++++++++++++++++ frontend/icons/down_off.svg | 65 ++++++++++++++++++++++++++++++++++ frontend/icons/remove.svg | 57 ++++++++++++++++++++++++++++++ frontend/icons/up.svg | 69 +++++++++++++++++++++++++++++++++++++ frontend/icons/up_off.svg | 69 +++++++++++++++++++++++++++++++++++++ frontend/resources.qrc | 6 ++++ 7 files changed, 390 insertions(+) create mode 100644 frontend/icons/add.svg create mode 100644 frontend/icons/down.svg create mode 100644 frontend/icons/down_off.svg create mode 100644 frontend/icons/remove.svg create mode 100644 frontend/icons/up.svg create mode 100644 frontend/icons/up_off.svg diff --git a/frontend/icons/add.svg b/frontend/icons/add.svg new file mode 100644 index 00000000..7697d382 --- /dev/null +++ b/frontend/icons/add.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/frontend/icons/down.svg b/frontend/icons/down.svg new file mode 100644 index 00000000..1dc8bdc6 --- /dev/null +++ b/frontend/icons/down.svg @@ -0,0 +1,69 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/frontend/icons/down_off.svg b/frontend/icons/down_off.svg new file mode 100644 index 00000000..de3077e2 --- /dev/null +++ b/frontend/icons/down_off.svg @@ -0,0 +1,65 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/frontend/icons/remove.svg b/frontend/icons/remove.svg new file mode 100644 index 00000000..864a96cf --- /dev/null +++ b/frontend/icons/remove.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/frontend/icons/up.svg b/frontend/icons/up.svg new file mode 100644 index 00000000..46cefddd --- /dev/null +++ b/frontend/icons/up.svg @@ -0,0 +1,69 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/frontend/icons/up_off.svg b/frontend/icons/up_off.svg new file mode 100644 index 00000000..e1096986 --- /dev/null +++ b/frontend/icons/up_off.svg @@ -0,0 +1,69 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/frontend/resources.qrc b/frontend/resources.qrc index e3214225..19b8a0fb 100644 --- a/frontend/resources.qrc +++ b/frontend/resources.qrc @@ -1,9 +1,15 @@ + icons/down.svg + icons/up.svg icons/group.svg icons/cross.svg + icons/up_off.svg + icons/down_off.svg icons/filter.svg icons/open.svg + icons/add.svg + icons/remove.svg icons/jv2.svg From e07471b4d554124d9ca3e9d09b7a484f73b23267 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 24 Jan 2025 14:07:22 +0000 Subject: [PATCH 18/24] Hiding and showing of selected log values. --- frontend/logValueModel.cpp | 23 +++-- frontend/logValueModel.h | 2 + frontend/plotLogDataWidget.cpp | 41 +++++++-- frontend/plotLogDataWidget.h | 9 +- frontend/plotLogDataWidget.ui | 152 +++++++++++++++++++++++---------- 5 files changed, 166 insertions(+), 61 deletions(-) diff --git a/frontend/logValueModel.cpp b/frontend/logValueModel.cpp index db4782bd..34b7273b 100644 --- a/frontend/logValueModel.cpp +++ b/frontend/logValueModel.cpp @@ -32,6 +32,22 @@ OptionalReferenceWrapper LogValueModel::getData(int row) const // Get LogValue at index specified OptionalReferenceWrapper LogValueModel::getData(const QModelIndex &index) const { return getData(index.row()); } +// Set selected status of all supplied indices +void LogValueModel::setSelected(const QModelIndexList &indices, bool selectedState) +{ + for (const auto &index : indices) + { + auto optData = getData(index); + if (!optData) + continue; + auto &data = optData->get(); + + data.setSelected(selectedState); + } + + emit(dataChanged(indices.front(), indices.back())); +} + /* * QAbstractListModel Overrides */ @@ -40,10 +56,7 @@ int LogValueModel::rowCount(const QModelIndex &parent) const { return data_ ? da int LogValueModel::columnCount(const QModelIndex &parent) const { return 1; } -Qt::ItemFlags LogValueModel::flags(const QModelIndex &index) const -{ - return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; -} +Qt::ItemFlags LogValueModel::flags(const QModelIndex &index) const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QVariant LogValueModel::data(const QModelIndex &index, int role) const { @@ -66,8 +79,6 @@ QVariant LogValueModel::data(const QModelIndex &index, int role) const return data.name(); case (Qt::ToolTipRole): return data.neXuSLocation(); - case (Qt::CheckStateRole): - return data.isSelected() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked; default: return {}; } diff --git a/frontend/logValueModel.h b/frontend/logValueModel.h index 4a576870..23757a97 100644 --- a/frontend/logValueModel.h +++ b/frontend/logValueModel.h @@ -28,6 +28,8 @@ class LogValueModel : public QAbstractListModel OptionalReferenceWrapper getData(int row) const; // Get LogValue at index specified OptionalReferenceWrapper getData(const QModelIndex &index) const; + // Set selected status of all supplied indices + void setSelected(const QModelIndexList &indices, bool selectedState); /* * QAbstractTableModel Overrides diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index a4dd84ba..d09f430d 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -14,6 +14,9 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const { ui_.setupUi(this); + connect(&logValueModel_, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QList &)), this, + SLOT(logValueChanged(const QModelIndex &, const QModelIndex &, const QList &))); + // Create LogValueGroups for each run we've been given and set up the relevant list model for (auto runNumber : runNumbers_) logValueGroups_.emplace_back(QString::number(runNumber), true); @@ -27,7 +30,8 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const shownLogValueFilterProxy_.setSelectedStateBehaviour(LogValueFilterProxy::SelectedStateBehaviour::ShowOnlySelected); shownLogValueFilterProxy_.sort(0); ui_.ShownLogValueList->setModel(&shownLogValueFilterProxy_); - ui_.ShownLogValueList->setSelectionBehavior(QAbstractItemView::SelectRows); + connect(ui_.ShownLogValueList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(shownLogValuesSelectionChanged(const QItemSelection &, const QItemSelection &))); // Acquire the available log data backend_.getNeXuSLogValues(journalSource_, runNumbers_, @@ -37,10 +41,9 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const availableLogValueFilterProxy_.setSelectedStateBehaviour(LogValueFilterProxy::SelectedStateBehaviour::HideSelected); availableLogValueFilterProxy_.sort(0); ui_.AvailableLogValueList->setModel(&availableLogValueFilterProxy_); - ui_.AvailableLogValueList->setSelectionBehavior(QAbstractItemView::SelectRows); - - connect(&logValueModel_, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &, const QList &)), this, - SLOT(logValueChanged(const QModelIndex &, const QModelIndex &, const QList &))); + connect(ui_.AvailableLogValueList->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, + SLOT(availableLogValuesSelectionChanged(const QItemSelection &, const QItemSelection &))); // Acquire the available log data backend_.getNeXuSLogValues(journalSource_, runNumbers_, @@ -202,7 +205,7 @@ void PlotLogDataWidget::hideData(const LogValue &logValue) * Private Slots */ -// Log value selection changed +// Log value model data changed void PlotLogDataWidget::logValueChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) { auto optLogValue = logValueModel_.getData(topLeft); @@ -233,7 +236,7 @@ void PlotLogDataWidget::logValueChanged(const QModelIndex &topLeft, const QModel } } -// Log value group selection changed +// Log value group model data changed void PlotLogDataWidget::logValueGroupChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList &roles) { @@ -245,6 +248,30 @@ void PlotLogDataWidget::logValueGroupChanged(const QModelIndex &topLeft, const Q ui_.Plot->setDataEnabled(QRegularExpression(QString("^%1/.*").arg(group.name())), group.isSelected()); } +// Log values selection changed +void PlotLogDataWidget::availableLogValuesSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + ui_.ShowLogValueButton->setDisabled(selected.isEmpty()); +} + +void PlotLogDataWidget::shownLogValuesSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + ui_.HideLogValueButton->setDisabled(selected.isEmpty()); +} + +void PlotLogDataWidget::on_ShowLogValueButton_clicked(bool checked) +{ + logValueModel_.setSelected( + availableLogValueFilterProxy_.mapSelectionToSource(ui_.AvailableLogValueList->selectionModel()->selection()).indexes(), + true); +} + +void PlotLogDataWidget::on_HideLogValueButton_clicked(bool checked) +{ + logValueModel_.setSelected( + shownLogValueFilterProxy_.mapSelectionToSource(ui_.ShownLogValueList->selectionModel()->selection()).indexes(), false); +} + /* * Public */ diff --git a/frontend/plotLogDataWidget.h b/frontend/plotLogDataWidget.h index 409b6762..0d470df6 100644 --- a/frontend/plotLogDataWidget.h +++ b/frontend/plotLogDataWidget.h @@ -60,10 +60,15 @@ class PlotLogDataWidget : public QWidget void hideData(const LogValue &logValue); private slots: - // Log value selection changed + // Log value model data changed void logValueChanged(const QModelIndex &, const QModelIndex &, const QList &); - // Log value group selection changed + // Log value group model data changed void logValueGroupChanged(const QModelIndex &, const QModelIndex &, const QList &); + // Log values selection changed + void availableLogValuesSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void shownLogValuesSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void on_ShowLogValueButton_clicked(bool checked); + void on_HideLogValueButton_clicked(bool checked); public: // Create summary text for the plot diff --git a/frontend/plotLogDataWidget.ui b/frontend/plotLogDataWidget.ui index f1cef836..b793910c 100644 --- a/frontend/plotLogDataWidget.ui +++ b/frontend/plotLogDataWidget.ui @@ -9,8 +9,8 @@ 0 0 - 1086 - 1285 + 1041 + 592 @@ -121,11 +121,11 @@ 0 - 1 + 3 - Shown Log Values + Log Values @@ -148,41 +148,111 @@ 0 - 3 + 1 + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + 14 + 14 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Show + + + + :/icons/icons/up.svg + :/icons/icons/up_off.svg:/icons/icons/up.svg + + + + + + + false + + + Hide + + + + :/icons/icons/down.svg + :/icons/icons/down_off.svg:/icons/icons/down.svg + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 4 + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + 14 + 14 + + - -
-
- - - - - 0 - 2 - - - - Available Log Values - - - - 4 - - - 4 - - - 4 - - - 4 - - - 4 - @@ -194,7 +264,7 @@ - Filter run data + Filter log values @@ -223,16 +293,6 @@ - - - - - 0 - 3 - - - - From f438798a0c86b72477d350d08abba6fb7f645d7e Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 24 Jan 2025 14:07:51 +0000 Subject: [PATCH 19/24] Remove debug output. --- frontend/plotLogDataWidget.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index d09f430d..32c8c141 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -210,7 +210,6 @@ void PlotLogDataWidget::logValueChanged(const QModelIndex &topLeft, const QModel { auto optLogValue = logValueModel_.getData(topLeft); auto &logValue = optLogValue->get(); - qDebug() << "Toggled data was " + logValue.name(); // If the logValue has been selected, then either redisplay or retrieve the data if (logValue.isSelected()) From 2d0ac20bfbe00dcd38248a1bfe330e04f86386b8 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 24 Jan 2025 14:18:43 +0000 Subject: [PATCH 20/24] Implement filtering, case insensitivity throughout. --- frontend/logValueFilterProxy.cpp | 3 ++- frontend/plotLogDataWidget.cpp | 9 +++++++++ frontend/plotLogDataWidget.h | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/frontend/logValueFilterProxy.cpp b/frontend/logValueFilterProxy.cpp index 9894944f..39645d31 100644 --- a/frontend/logValueFilterProxy.cpp +++ b/frontend/logValueFilterProxy.cpp @@ -23,7 +23,8 @@ bool LogValueFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sou else if (selectedValueBehaviour_ == SelectedStateBehaviour::ShowOnlySelected && !logValue.isSelected()) return false; - return (filterString_.isEmpty() || logValueModel_.getData(sourceRow)->get().name().contains(filterString_)); + return (filterString_.isEmpty() || + logValueModel_.getData(sourceRow)->get().name().contains(filterString_, Qt::CaseSensitivity::CaseInsensitive)); } // Set filter string, or empty string to disable diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 32c8c141..51a28e1c 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -28,6 +28,7 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const // Set up the shown log value list and model shownLogValueFilterProxy_.setSelectedStateBehaviour(LogValueFilterProxy::SelectedStateBehaviour::ShowOnlySelected); + shownLogValueFilterProxy_.setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); shownLogValueFilterProxy_.sort(0); ui_.ShownLogValueList->setModel(&shownLogValueFilterProxy_); connect(ui_.ShownLogValueList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), @@ -39,6 +40,7 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const // Set up the available log value list and model availableLogValueFilterProxy_.setSelectedStateBehaviour(LogValueFilterProxy::SelectedStateBehaviour::HideSelected); + availableLogValueFilterProxy_.setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); availableLogValueFilterProxy_.sort(0); ui_.AvailableLogValueList->setModel(&availableLogValueFilterProxy_); connect(ui_.AvailableLogValueList->selectionModel(), @@ -271,6 +273,13 @@ void PlotLogDataWidget::on_HideLogValueButton_clicked(bool checked) shownLogValueFilterProxy_.mapSelectionToSource(ui_.ShownLogValueList->selectionModel()->selection()).indexes(), false); } +void PlotLogDataWidget::on_LogValueFilterEdit_textChanged(const QString &text) +{ + availableLogValueFilterProxy_.setFilterString(text); +} + +void PlotLogDataWidget::on_LogValueFilterClearButton_clicked(bool checked) { ui_.LogValueFilterEdit->clear(); } + /* * Public */ diff --git a/frontend/plotLogDataWidget.h b/frontend/plotLogDataWidget.h index 0d470df6..0db42465 100644 --- a/frontend/plotLogDataWidget.h +++ b/frontend/plotLogDataWidget.h @@ -69,6 +69,8 @@ class PlotLogDataWidget : public QWidget void shownLogValuesSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void on_ShowLogValueButton_clicked(bool checked); void on_HideLogValueButton_clicked(bool checked); + void on_LogValueFilterEdit_textChanged(const QString &text); + void on_LogValueFilterClearButton_clicked(bool checked); public: // Create summary text for the plot From bda999cc4d18c67e73548acb05c1f8cbbf44b148 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 24 Jan 2025 15:55:47 +0000 Subject: [PATCH 21/24] Add decoration to run number list showing group colour. --- frontend/logValueGroup.cpp | 8 +++++++- frontend/logValueGroup.h | 7 ++++++- frontend/logValueGroupModel.cpp | 7 +++++++ frontend/plotLogDataWidget.cpp | 18 ++++++++++++------ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/frontend/logValueGroup.cpp b/frontend/logValueGroup.cpp index c05e7fdc..ce65def8 100644 --- a/frontend/logValueGroup.cpp +++ b/frontend/logValueGroup.cpp @@ -5,7 +5,10 @@ namespace JV2 { -LogValueGroup::LogValueGroup(const QString &name, bool selected) : name_(name), selected_(selected) {} +LogValueGroup::LogValueGroup(const QString &name, bool selected, Mildred::DisplayGroup *displayGroup) + : name_(name), selected_(selected), displayGroup_(displayGroup) +{ +} /* * Basic Data @@ -14,6 +17,9 @@ LogValueGroup::LogValueGroup(const QString &name, bool selected) : name_(name), // Return the name of the property const QString &LogValueGroup::name() const { return name_; } +// Return the associated DisplayGroup (if set) +Mildred::DisplayGroup *LogValueGroup::displayGroup() const { return displayGroup_; } + // Return whether the property is selected bool LogValueGroup::isSelected() const { return selected_; } diff --git a/frontend/logValueGroup.h b/frontend/logValueGroup.h index 4110989c..a4e1c9e4 100644 --- a/frontend/logValueGroup.h +++ b/frontend/logValueGroup.h @@ -4,6 +4,7 @@ #pragma once #include "logValueData.h" +#include "plot/src/displaygroup.h" #include namespace JV2 @@ -12,7 +13,7 @@ namespace JV2 class LogValueGroup { public: - LogValueGroup(const QString &name, bool selected = false); + LogValueGroup(const QString &name, bool selected = false, Mildred::DisplayGroup *displayGroup = nullptr); ~LogValueGroup() = default; /* @@ -21,12 +22,16 @@ class LogValueGroup private: // Display name of the group QString name_; + // Associated DisplayGroup in the plot + Mildred::DisplayGroup *displayGroup_{nullptr}; // Whether this group is selected bool selected_{false}; public: // Return the name of the property const QString &name() const; + // Return the associated DisplayGroup (if set) + Mildred::DisplayGroup *displayGroup() const; // Return whether the property is selected bool isSelected() const; // Set whether the property is selected diff --git a/frontend/logValueGroupModel.cpp b/frontend/logValueGroupModel.cpp index 799f5c63..72fdcbd5 100644 --- a/frontend/logValueGroupModel.cpp +++ b/frontend/logValueGroupModel.cpp @@ -2,6 +2,8 @@ // Copyright (c) 2024 Team LogValueGroupViewer and contributors #include "logValueGroupModel.h" +#include +#include namespace JV2 { @@ -67,6 +69,11 @@ QVariant LogValueGroupModel::data(const QModelIndex &index, int role) const case (Qt::DisplayRole): case (Qt::EditRole): return data.name(); + case (Qt::DecorationRole): + if (data.displayGroup()) + return data.displayGroup()->colourPolicyIcon({16, 16}); + else + return {}; case (Qt::CheckStateRole): return data.isSelected() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked; default: diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 51a28e1c..37077a3d 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -18,8 +18,16 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const SLOT(logValueChanged(const QModelIndex &, const QModelIndex &, const QList &))); // Create LogValueGroups for each run we've been given and set up the relevant list model + auto count = 0; for (auto runNumber : runNumbers_) - logValueGroups_.emplace_back(QString::number(runNumber), true); + { + // Create a DisplayGroup in the plot so we colourise each associated log value dataset the same + auto *group = ui_.Plot->getDisplayGroup(QString::number(runNumber)); + group->setColourPolicy(Mildred::DisplayGroup::ColourPolicy::Stock); + group->setStockColour(Mildred::DisplayGroup::stockColourForIndex(count++)); + + logValueGroups_.emplace_back(QString::number(runNumber), true, group); + } ui_.RunNumberList->setModel(&logValueGroupModel_); logValueGroupModel_.setData(logValueGroups_); @@ -177,14 +185,12 @@ void PlotLogDataWidget::showData(const LogValue &logValue) // Add each contained per-run dataset to the plot for (auto &&[dataName, data] : logValue.data()) { - // Create a display group with some default policies - auto group = ui_.Plot->addDisplayGroup(); - group->setSingleColour({255, 0, 200, 255}); - // Create a renderable and add it to the group + // Create a renderable and add it to the group for the run number (dataName) auto *renderable = ui_.Plot->addData1D((dataName + "/" + logValue.name())); renderable->setData(data.times(), data.values()); - group->addTarget(renderable); + + ui_.Plot->getDisplayGroup(dataName)->addTarget(renderable); } // If the plot was empty when we started, auto-scale it now From 2169b5afcd279b31a4ed48c4d30af2d7f4ff46cc Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 12 Dec 2025 16:27:06 +0000 Subject: [PATCH 22/24] More plotting stuff apparently. --- frontend/plotLogDataWidget.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 37077a3d..92ac041c 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -25,6 +25,7 @@ PlotLogDataWidget::PlotLogDataWidget(MainWindow *parent, Backend &backend, const auto *group = ui_.Plot->getDisplayGroup(QString::number(runNumber)); group->setColourPolicy(Mildred::DisplayGroup::ColourPolicy::Stock); group->setStockColour(Mildred::DisplayGroup::stockColourForIndex(count++)); + group->setTranslationPolicyX(Mildred::DisplayGroup::TranslationPolicy::Constant); logValueGroups_.emplace_back(QString::number(runNumber), true, group); } @@ -142,15 +143,24 @@ void PlotLogDataWidget::handleRetrieveSELogValueData(HttpRequestWorker *worker) { // Get the data name (run number) const auto dataName = run[QString("runNumber")].toString(); - qDebug() << dataName; + auto groupIt = std::find_if(logValueGroups_.begin(), logValueGroups_.end(), [dataName](const auto &group) { return group.name() == dataName; }); + if (groupIt == logValueGroups_.end()) + { + qDebug() << QString("Error: LogValueGroup missing for run '%1'.").arg(dataName); + continue; + } + auto &group = *groupIt; // Extract the time range data const auto timeRange = run[QString("timeRange")].toArray(); - // Get start and end times + // Store start and end times in the LogValueGroup object for the run + auto startTime = QDateTime::fromString(timeRange.first()[0].toString(), "yyyy-MM-dd'T'HH:mm:ss"); auto endTime = QDateTime::fromString(timeRange.first()[1].toString(), "yyyy-MM-dd'T'HH:mm:ss"); - auto startSecs = startTime.toSecsSinceEpoch(); + group.setTimeRange(startTime, endTime); + + group->setTranslationX(); // Get time / value vectors // TODO Need to check / detect enumerated data here From ad7a1afa9a75d5378f9f7bf5a547965ad17e14df Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 12 Dec 2025 16:39:31 +0000 Subject: [PATCH 23/24] Include qDebug(). --- frontend/args.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/args.cpp b/frontend/args.cpp index b419677b..797bde16 100644 --- a/frontend/args.cpp +++ b/frontend/args.cpp @@ -2,6 +2,7 @@ // Copyright (c) 2025 Team JournalViewer and contributors #include "args.h" +#include namespace JV2 { From fb9bcfefab9f73f1800a2d297d1ef1835bc990a8 Mon Sep 17 00:00:00 2001 From: Tristan Youngs Date: Fri, 12 Dec 2025 16:40:08 +0000 Subject: [PATCH 24/24] Update plot, comment out broken code. --- frontend/plot | 2 +- frontend/plotLogDataWidget.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/plot b/frontend/plot index 11478b2a..dbbe1a93 160000 --- a/frontend/plot +++ b/frontend/plot @@ -1 +1 @@ -Subproject commit 11478b2aad1faf86c2920382f8618dd016a2053f +Subproject commit dbbe1a9358844b8a15e45c1d92aaa6f2d5f707ff diff --git a/frontend/plotLogDataWidget.cpp b/frontend/plotLogDataWidget.cpp index 92ac041c..9f3eb80b 100644 --- a/frontend/plotLogDataWidget.cpp +++ b/frontend/plotLogDataWidget.cpp @@ -158,9 +158,9 @@ void PlotLogDataWidget::handleRetrieveSELogValueData(HttpRequestWorker *worker) auto startTime = QDateTime::fromString(timeRange.first()[0].toString(), "yyyy-MM-dd'T'HH:mm:ss"); auto endTime = QDateTime::fromString(timeRange.first()[1].toString(), "yyyy-MM-dd'T'HH:mm:ss"); - group.setTimeRange(startTime, endTime); + // group.setTimeRange(startTime, endTime); - group->setTranslationX(); + // group->setTranslationX(); // Get time / value vectors // TODO Need to check / detect enumerated data here