From ce8d83022b66174fd36ef489ad2514bdd69d0f62 Mon Sep 17 00:00:00 2001 From: Chris Harris Date: Wed, 22 Jun 2022 09:24:06 -0400 Subject: [PATCH] Add support for exporting dataset to Tiled Signed-off-by: Chris Harris --- tomviz/CMakeLists.txt | 4 + tomviz/DataBroker.cxx | 34 ++++++++ tomviz/DataBroker.h | 13 ++++ tomviz/DataBrokerSaveDialog.cxx | 51 ++++++++++++ tomviz/DataBrokerSaveDialog.h | 48 ++++++++++++ tomviz/DataBrokerSaveDialog.ui | 74 ++++++++++++++++++ tomviz/DataBrokerSaveReaction.cxx | 103 +++++++++++++++++++++++++ tomviz/DataBrokerSaveReaction.h | 41 ++++++++++ tomviz/MainWindow.cxx | 11 ++- tomviz/MainWindow.ui | 9 +++ tomviz/python/tomviz/io/_databroker.py | 10 +++ 11 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 tomviz/DataBrokerSaveDialog.cxx create mode 100644 tomviz/DataBrokerSaveDialog.h create mode 100644 tomviz/DataBrokerSaveDialog.ui create mode 100644 tomviz/DataBrokerSaveReaction.cxx create mode 100644 tomviz/DataBrokerSaveReaction.h diff --git a/tomviz/CMakeLists.txt b/tomviz/CMakeLists.txt index 748d7a5f9..6568f9d59 100644 --- a/tomviz/CMakeLists.txt +++ b/tomviz/CMakeLists.txt @@ -62,6 +62,10 @@ set(SOURCES DataBrokerLoadDialog.h DataBrokerLoadReaction.cxx DataBrokerLoadReaction.h + DataBrokerSaveDialog.cxx + DataBrokerSaveDialog.h + DataBrokerSaveReaction.cxx + DataBrokerSaveReaction.h DataExchangeFormat.cxx DataExchangeFormat.h DataPropertiesModel.cxx diff --git a/tomviz/DataBroker.cxx b/tomviz/DataBroker.cxx index 822235510..526daeeef 100644 --- a/tomviz/DataBroker.cxx +++ b/tomviz/DataBroker.cxx @@ -241,4 +241,38 @@ LoadDataCall* DataBroker::loadVariable(const QString& catalog, return call; } +SaveDataCall* DataBroker::saveData(const QString& catalog, const QString& name, + vtkImageData* data) +{ + auto call = new SaveDataCall(this); + + auto future = QtConcurrent::run([this, catalog, name, data, call]() { + Python python; + + auto saveFunc = m_dataBrokerModule.findFunction("save_data"); + if (!saveFunc.isValid()) { + emit call->error("Failed to import tomviz.io._databroker.save_data"); + } + + Python::Object pydata = Python::VTK::GetObjectFromPointer(data); + Python::Tuple args(3); + args.set(0, catalog.toStdString()); + args.set(1, name.toStdString()); + args.set(2, pydata); + + auto res = saveFunc.call(args); + + if (!res.isValid()) { + emit call->error("Error calling save_data"); + return; + } + + auto id = res.toString(); + + emit call->complete(id); + }); + + return call; +} + } // namespace tomviz diff --git a/tomviz/DataBroker.h b/tomviz/DataBroker.h index 4fc140a54..19ddc8b70 100644 --- a/tomviz/DataBroker.h +++ b/tomviz/DataBroker.h @@ -53,6 +53,17 @@ class LoadDataCall : public DataBrokerCall void complete(vtkSmartPointer imageData); }; +class SaveDataCall : public DataBrokerCall +{ + Q_OBJECT + +public: + explicit SaveDataCall(QObject* parent = 0) : DataBrokerCall(parent) {} + +signals: + void complete(const QString& id); +}; + class DataBroker : public QObject { Q_OBJECT @@ -71,6 +82,8 @@ class DataBroker : public QObject const QString& table); LoadDataCall* loadVariable(const QString& catalog, const QString& runUid, const QString& table, const QString& variable); + SaveDataCall* saveData(const QString& catalog, const QString& name, + vtkImageData* data); }; } // namespace tomviz diff --git a/tomviz/DataBrokerSaveDialog.cxx b/tomviz/DataBrokerSaveDialog.cxx new file mode 100644 index 000000000..b9f717ae2 --- /dev/null +++ b/tomviz/DataBrokerSaveDialog.cxx @@ -0,0 +1,51 @@ +/* This source file is part of the Tomviz project, https://tomviz.org/. + It is released under the 3-Clause BSD License, see "LICENSE". */ + +#include "DataBrokerSaveDialog.h" +#include "DataBroker.h" +#include "Utilities.h" + +#include "ui_DataBrokerSaveDialog.h" + +#include +#include +#include +#include +#include + +#include + +namespace tomviz { + +DataBrokerSaveDialog::DataBrokerSaveDialog(DataBroker* dataBroker, + QWidget* parent) + : QDialog(parent), m_ui(new Ui::DataBrokerSaveDialog), + m_dataBroker(dataBroker) +{ + m_ui->setupUi(this); + + setEnabledOkButton(false); + + connect(m_ui->nameLineEdit, &QLineEdit::textChanged, this, + [this](const QString& name) { + if (name.size() > 0) { + m_name = name; + setEnabledOkButton(true); + } + }); +} + +DataBrokerSaveDialog::~DataBrokerSaveDialog() = default; + +void DataBrokerSaveDialog::setEnabledOkButton(bool enabled) +{ + auto okButton = m_ui->buttonBox->button(QDialogButtonBox::Ok); + okButton->setEnabled(enabled); +} + +QString DataBrokerSaveDialog::name() +{ + return m_name; +} + +} // namespace tomviz diff --git a/tomviz/DataBrokerSaveDialog.h b/tomviz/DataBrokerSaveDialog.h new file mode 100644 index 000000000..cc926df20 --- /dev/null +++ b/tomviz/DataBrokerSaveDialog.h @@ -0,0 +1,48 @@ +/* This source file is part of the Tomviz project, https://tomviz.org/. + It is released under the 3-Clause BSD License, see "LICENSE". */ + +#ifndef tomvizDataBrokerSaveDialog_h +#define tomvizDataBrokerSaveDialog_h + +#include "ActiveObjects.h" + +#include + +#include +#include +#include +#include + +namespace Ui { +class DataBrokerSaveDialog; +} + +class QTreeWidgetItem; + +namespace tomviz { + +class DataBroker; +class ListResourceCall; + +class DataBrokerSaveDialog : public QDialog +{ + Q_OBJECT + +public: + explicit DataBrokerSaveDialog(DataBroker* dataBroker, + QWidget* parent = nullptr); + ~DataBrokerSaveDialog() override; + QString name(); + +private: + Q_DISABLE_COPY(DataBrokerSaveDialog) + QScopedPointer m_ui; + DataBroker* m_dataBroker; + + void setEnabledOkButton(bool enable); + + QString m_name; +}; +} // namespace tomviz + +#endif diff --git a/tomviz/DataBrokerSaveDialog.ui b/tomviz/DataBrokerSaveDialog.ui new file mode 100644 index 000000000..e37b20a47 --- /dev/null +++ b/tomviz/DataBrokerSaveDialog.ui @@ -0,0 +1,74 @@ + + + DataBrokerSaveDialog + + + + 0 + 0 + 400 + 117 + + + + Export to DataBroker + + + + + 30 + 70 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 9 + 9 + 381 + 51 + + + + + + + Name to associate with data: + + + + + + + + + + + + + + + + + buttonBox + accepted() + DataBrokerSaveDialog + accept() + + + buttonBox + rejected() + DataBrokerSaveDialog + reject() + + + diff --git a/tomviz/DataBrokerSaveReaction.cxx b/tomviz/DataBrokerSaveReaction.cxx new file mode 100644 index 000000000..734301a24 --- /dev/null +++ b/tomviz/DataBrokerSaveReaction.cxx @@ -0,0 +1,103 @@ +/* This source file is part of the Tomviz project, https://tomviz.org/. + It is released under the 3-Clause BSD License, see "LICENSE". */ + +#include "DataBrokerSaveReaction.h" +#include "DataBrokerSaveDialog.h" + +#include "ActiveObjects.h" +#include "DataSource.h" +#include "GenericHDF5Format.h" +#include "LoadDataReaction.h" +#include "Utilities.h" + +#include + +#include +#include + +namespace tomviz { + +DataBrokerSaveReaction::DataBrokerSaveReaction(QAction* parentObject, + MainWindow* mainWindow) + : pqReaction(parentObject), m_mainWindow(mainWindow) +{ + QObject::connect( + &ActiveObjects::instance(), + QOverload::of(&ActiveObjects::dataSourceChanged), this, + [this](DataSource* dataSource) { + parentAction()->setEnabled(dataSource != nullptr && + m_dataBrokerInstalled); + }); +} + +DataBrokerSaveReaction::~DataBrokerSaveReaction() = default; + +void DataBrokerSaveReaction::onTriggered() +{ + saveData(); +} + +void DataBrokerSaveReaction::setDataBrokerInstalled(bool installed) +{ + m_dataBrokerInstalled = installed; +} + +void DataBrokerSaveReaction::saveData() +{ + auto dataBroker = new DataBroker(tomviz::mainWidget()); + DataBrokerSaveDialog dialog(dataBroker, tomviz::mainWidget()); + + if (dialog.exec() == QDialog::Accepted) { + auto name = dialog.name(); + + auto ds = ActiveObjects::instance().activeDataSource(); + if (ds == nullptr) { + qWarning() << "No active data source!"; + return; + } + + auto data = ds->imageData(); + + vtkNew permutedData; + if (DataSource::hasTiltAngles(data)) { + // No deep copies of data needed. Just re-label the axes. + permutedData->ShallowCopy(data); + relabelXAndZAxes(permutedData); + } else { + // Need to re-order to C ordering before writing + GenericHDF5Format::reorderData(data, permutedData, + ReorderMode::FortranToC); + } + + tomviz::mainWidget()->setCursor(Qt::WaitCursor); + auto call = dataBroker->saveData("fxi", name, data); + connect( + call, &SaveDataCall::complete, dataBroker, + [dataBroker, this](const QString& id) { + dataBroker->deleteLater(); + tomviz::mainWidget()->unsetCursor(); + QMessageBox messageBox( + QMessageBox::Information, "tomviz", + QString( + "The active dataset was successfully exported to DataBroker: %1") + .arg(id), + QMessageBox::Ok, m_mainWindow); + messageBox.exec(); + }); + + connect(call, &DataBrokerCall::error, dataBroker, + [dataBroker, this](const QString& errorMessage) { + tomviz::mainWidget()->unsetCursor(); + dataBroker->deleteLater(); + QMessageBox messageBox( + QMessageBox::Warning, "tomviz", + QString("Error export data to DataBroker: %1. Please check " + "message log for details.") + .arg(errorMessage), + QMessageBox::Ok, m_mainWindow); + messageBox.exec(); + }); + } +} + +} // namespace tomviz diff --git a/tomviz/DataBrokerSaveReaction.h b/tomviz/DataBrokerSaveReaction.h new file mode 100644 index 000000000..218b836e4 --- /dev/null +++ b/tomviz/DataBrokerSaveReaction.h @@ -0,0 +1,41 @@ +/* This source file is part of the Tomviz project, https://tomviz.org/. + It is released under the 3-Clause BSD License, see "LICENSE". */ + +#ifndef tomvizDataBrokerSaveReaction_h +#define tomvizDataBrokerSaveReaction_h + +#include + +#include "DataBroker.h" +#include "MainWindow.h" + +namespace tomviz { +class DataSource; + +/// DataBrokerSaveReaction handles the "Export to DataBroker" action in +/// tomviz. +/// +class DataBrokerSaveReaction : public pqReaction +{ + Q_OBJECT + +public: + DataBrokerSaveReaction(QAction* parentAction, MainWindow* mainWindow); + ~DataBrokerSaveReaction() override; + + void setDataBrokerInstalled(bool installed); + void saveData(); + +protected: + /// Called when the action is triggered. + void onTriggered() override; + + bool m_dataBrokerInstalled = false; + +private: + Q_DISABLE_COPY(DataBrokerSaveReaction) + MainWindow* m_mainWindow; +}; +} // namespace tomviz + +#endif diff --git a/tomviz/MainWindow.cxx b/tomviz/MainWindow.cxx index ccc18fbcf..63c60c576 100644 --- a/tomviz/MainWindow.cxx +++ b/tomviz/MainWindow.cxx @@ -29,6 +29,7 @@ #include "Connection.h" #include "DataBroker.h" #include "DataBrokerLoadReaction.h" +#include "DataBrokerSaveReaction.h" #include "DataPropertiesPanel.h" #include "DataTransformMenu.h" #include "FileFormatManager.h" @@ -254,6 +255,9 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) new DataBrokerLoadReaction(m_ui->actionImportFromDataBroker); + auto dataBrokerSaveReaction = + new DataBrokerSaveReaction(m_ui->actionExportToDataBroker, this); + // Build Data Transforms menu new DataTransformMenu(this, m_ui->menuData, m_ui->menuSegmentation); @@ -566,7 +570,7 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) statusBar()->showMessage("Initializing python..."); auto pythonWatcher = new QFutureWatcher>; connect(pythonWatcher, &QFutureWatcherBase::finished, this, - [this, pyXRFRunner, pythonWatcher]() { + [this, pyXRFRunner, pythonWatcher, dataBrokerSaveReaction]() { m_ui->actionAcquisition->setEnabled(true); m_ui->actionPassiveAcquisition->setEnabled(true); registerCustomOperators(pythonWatcher->result()); @@ -574,6 +578,11 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) auto dataBroker = new DataBroker(this); m_ui->actionImportFromDataBroker->setEnabled( dataBroker->installed()); + m_ui->actionExportToDataBroker->setEnabled( + dataBroker->installed() && + ActiveObjects::instance().activeDataSource() != nullptr); + dataBrokerSaveReaction->setDataBrokerInstalled( + dataBroker->installed()); dataBroker->deleteLater(); bool installed = pyXRFRunner->isInstalled(); diff --git a/tomviz/MainWindow.ui b/tomviz/MainWindow.ui index 16b50a563..7c23fc73c 100644 --- a/tomviz/MainWindow.ui +++ b/tomviz/MainWindow.ui @@ -53,6 +53,7 @@ + @@ -426,6 +427,14 @@ &Import from DataBroker + + + false + + + &Export to DataBroker + + Export Screenshot... diff --git a/tomviz/python/tomviz/io/_databroker.py b/tomviz/python/tomviz/io/_databroker.py index f95610c35..24eb4c08e 100644 --- a/tomviz/python/tomviz/io/_databroker.py +++ b/tomviz/python/tomviz/io/_databroker.py @@ -15,6 +15,9 @@ _installed = True except ImportError: pass +except Exception: + import traceback + traceback.print_exc() def installed(): @@ -152,3 +155,10 @@ def load_variable(catalog_name, run_uid, table, variable): tomviz.utils.set_tilt_angles(image_data, angles) return image_data + + +def save_data(catalog_name, name, data): + scalars = tomviz.utils.get_scalars(data) + r = c[catalog_name]["sandbox"].write_array(scalars, {"name": name}) + + return r